Как правильно десериализовать RedisMessage через BookSleeve?
Я создал следующий одноэлементный класс для обработки соединения Redis и предоставления функциональности BookSleeve:
public class RedisConnection
{
private static RedisConnection _instance = null;
private BookSleeve.RedisSubscriberConnection _channel;
private readonly int _db;
private readonly string[] _keys; // represent channel name
public BookSleeve.RedisConnection _connection;
/// <summary>
/// Initialize all class parameters
/// </summary>
private RedisConnection(string serverList, int db, IEnumerable<string> keys)
{
_connection = ConnectionUtils.Connect(serverList);
_db = db;
_keys = keys.ToArray();
_connection.Closed += OnConnectionClosed;
_connection.Error += OnConnectionError;
// Create a subscription channel in redis
_channel = _connection.GetOpenSubscriberChannel();
// Subscribe to the registered connections
_channel.Subscribe(_keys, OnMessage);
// Dirty hack but it seems like subscribe returns before the actual
// subscription is properly setup in some cases
while (_channel.SubscriptionCount == 0)
{
Thread.Sleep(500);
}
}
/// <summary>
/// Do something when a message is received
/// </summary>
/// <param name="key"></param>
/// <param name="data"></param>
private void OnMessage(string key, byte[] data)
{
// since we are just interested in pub/sub, no data persistence is active
// however, if the persistence flag is enabled, here is where we can save the data
// The key is the stream id (channel)
//var message = RedisMessage.Deserialize(data);
var message = Helpers.BytesToString(data);
if (true) ;
//_publishQueue.Enqueue(() => OnReceived(key, (ulong)message.Id, message.Messages));
}
public static RedisConnection GetInstance(string serverList, int db, IEnumerable<string> keys)
{
if (_instance == null)
{
// could include some sort of lock for thread safety
_instance = new RedisConnection(serverList, db, keys);
}
return _instance;
}
private static void OnConnectionClosed(object sender, EventArgs e)
{
// Should we auto reconnect?
if (true)
{
;
}
}
private static void OnConnectionError(object sender, BookSleeve.ErrorEventArgs e)
{
// How do we bubble errors?
if (true)
{
;
}
}
}
В OnMessage()
, var message = RedisMessage.Deserialize(data);
закомментировано из-за следующей ошибки:
RedisMessage недоступен из-за уровня защиты.
RedisMessage - это абстрактный класс в BookSleeve, и я немного застрял в том, почему я не могу использовать это.
Я столкнулся с этой проблемой, потому что, отправляя сообщения на канал (pub/sub), я могу захотеть что-то с ними сделать в OnMessage() - например, если установлен флаг постоянства, я могу начать запись данных. Проблема заключается в том, что на этом этапе данные сериализуются, и я хочу десериализовать их (в строку) и сохранить их в Redis.
Вот мой метод испытаний:
[TestMethod]
public void TestRedisConnection()
{
// setup parameters
string serverList = "dbcache1.local:6379";
int db = 0;
List<string> eventKeys = new List<string>();
eventKeys.Add("Testing.FaucetChannel");
BookSleeve.RedisConnection redisConnection = Faucet.Services.RedisConnection.GetInstance(serverList, db, eventKeys)._connection;
// broadcast to a channel
redisConnection.Publish("Testing.FaucetChannel", "a published value!!!");
}
Так как я не смог использовать Deserialize()
Метод, я создал статический вспомогательный класс:
public static class Helpers
{
/// <summary>
/// Serializes a string to bytes
/// </summary>
/// <param name="val"></param>
/// <returns></returns>
public static byte[] StringToBytes(string str)
{
try
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
catch (Exception ex)
{
/* handle exception omitted */
return null;
}
}
/// <summary>
/// Deserializes bytes to string
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string BytesToString(byte[] bytes)
{
string set;
try
{
char[] chars = new char[bytes.Length / sizeof(char)];
System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
return new string(chars);
}
catch (Exception ex)
{
// removed error handling logic!
return null;
}
}
}
К сожалению, это не правильно десериализовывает строку обратно в исходную форму, и я получаю что-то вроде этого: скорее, чем фактический оригинальный текст.
Предложения?
2 ответа
RedisMessage
представляет ожидающий запрос, который собирается отправить на сервер; Существует несколько конкретных реализаций этого, обычно связанных с характером и количеством отправляемых параметров. Нет смысла "десериализовать" (или даже "сериализовать") RedisMessage
- это не их цель. Единственное, что разумно сделать, это Write(...)
их к Stream
,
Если вы хотите получить информацию о RedisMessage
, затем .ToString()
имеет обзор, но это не является случайным и откровенно предназначено для отладки.
RedisMessage
является internal
учебный класс; деталь реализации. Если вы не работаете над извлечением запроса к основному коду, вам никогда не придется взаимодействовать с RedisMessage
,
На аналогичном уровне, есть RedisResult
который представляет ответ, возвращающийся с сервера. Если вам нужен быстрый способ получения данных, к счастью, это намного проще:
object val = result.Parse(true);
(true
означает "спекулятивный тест, чтобы увидеть, если данные выглядят как string
"). Но опять же, это internal
детали реализации, с которыми вам не придется работать.
Очевидно, что это была проблема с типом кодирования, а пока, немного взглянув на эту ссылку, я просто добавил тип кодировки UTF8, и результат выглядит хорошо:
#region EncodingType enum
/// <summary>
/// Encoding Types.
/// </summary>
public enum EncodingType
{
ASCII,
Unicode,
UTF7,
UTF8
}
#endregion
#region ByteArrayToString
/// <summary>
/// Converts a byte array to a string using Unicode encoding.
/// </summary>
/// <param name="bytes">Array of bytes to be converted.</param>
/// <returns>string</returns>
public static string ByteArrayToString(byte[] bytes)
{
return ByteArrayToString(bytes, EncodingType.Unicode);
}
/// <summary>
/// Converts a byte array to a string using specified encoding.
/// </summary>
/// <param name="bytes">Array of bytes to be converted.</param>
/// <param name="encodingType">EncodingType enum.</param>
/// <returns>string</returns>
public static string ByteArrayToString(byte[] bytes, EncodingType encodingType)
{
System.Text.Encoding encoding=null;
switch (encodingType)
{
case EncodingType.ASCII:
encoding=new System.Text.ASCIIEncoding();
break;
case EncodingType.Unicode:
encoding=new System.Text.UnicodeEncoding();
break;
case EncodingType.UTF7:
encoding=new System.Text.UTF7Encoding();
break;
case EncodingType.UTF8:
encoding=new System.Text.UTF8Encoding();
break;
}
return encoding.GetString(bytes);
}
#endregion
--ОБНОВИТЬ--
Еще проще: var message = Encoding.UTF8.GetString(data);