c# - Redis with RedisBoost

.Net(C#)中使用Redis时,探讨了ServiceStack.Redis和StackExchange.Redis的限制。选择了RedisBoost,但遇到XML序列化占用空间大及缺乏Set操作的问题。通过Json序列化解决空间问题,并编写扩展方法实现Set操作。推荐使用RedisBoost并提供Redis客户端链接。

.Net(C#) 下调用Redis的API有若干个,如下图所示:


大家可以看到两个标记为星星的项目:

1. ServiceStack.Redis如果每小时超过6000个请求,需要购买License,大约100$~200$。

2. StackExchange.Redis貌似不可以直接存取Set。

Whatever, 总之,哥最后用的是redisboost, 一个未标星的项目,其作者为Andrey Bulygin(https://github.com/andrew-bn)

RedisBoost项目在此:https://github.com/andrew-bn/RedisBoost


但是,RedisBoost这玩意儿有两个问题:

1. 在序列化自定义类的时候使用的是XmlSerialization, 生成的value太占地方。

2. 木有直接操作Set的API.

以下代码了解决这两个问题。

对于1,使用Json序列化;对于2,写了若干个扩展方法(同步异步)让它支持Set操作。话不多说,请看代码:

using RedisBoost;
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RedisBoost.Core.Serialization;
using System.Globalization;
using System.Runtime.Serialization;
using System.Collections.Generic;

namespace RedisDemo
{
    /// <summary>
    /// 本文件内容及实现基于:http://andrew-bn.github.io/RedisBoost/
    /// </summary>
    public static class RedisUtil
    {
        static RedisUtil()
        {
            RedisClient.DefaultSerializer = new JsonSerializer();
        }

        #region Async Actions

        /// <summary>
        /// 得到一个Redis客户端实例(异步)
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns>Redis客户端实例</returns>
        public static async Task<IRedisClient> GetRedisClientAsync(string ip, int port)
        {
            var client = await RedisClient.ConnectAsync(ip, port);
            return client;
        }

        /// <summary>
        /// 将指定项目(数组)存储到Set(异步)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="redisClient">Redis客户端实例</param>
        /// <param name="key">Set键值</param>
        /// <param name="items">需要存储在此Set中的Item数组</param>
        /// <returns>存储成功的项目个数</returns>
        public static async Task<long> SaveSetAsync<T>(this IRedisClient redisClient, string key, T[] items)
        {
            long x = await redisClient.SAddAsync<T>(key, items);
            return x;
        }

        /// <summary>
        /// 得到指定Set中的项目(异步)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="redisClient">Redis客户端实例</param>
        /// <param name="setName">Set键值</param>
        /// <returns>此Set中的所有项目,以数组的形式返回</returns>
        public static async Task<T[]> GetSetAsync<T>(this IRedisClient redisClient, string key)
        {
            var task = redisClient.ExecuteAsync("smembers", key);

            var result = await task.ContinueWith<T[]>(res =>
            {
                List<T> items = new List<T>();
                if (res.Result.AsMultiBulk().Count() > 0)
                {
                    foreach (var item in res.Result.AsMultiBulk())
                    {
                        items.Add(item.As<T>());
                    }
                }
                return items.ToArray<T>();
            });

            return result;
        }

        #endregion

        #region Sync Actions
        
        /// <summary>
        /// 得到一个Redis客户端实例
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns>Redis客户端实例</returns>
        public static IRedisClient GetRedisClient(string ip, int port)
        {
            var task = RedisClient.ConnectAsync(ip, port);
            task.Wait();
            return task.Result;
        }

        /// <summary>
        /// 将指定项目(数组)存储到Set
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="redisClient">Redis客户端实例</param>
        /// <param name="key">Set键值</param>
        /// <param name="items">需要存储在此Set中的Item数组</param>
        /// <returns>存储成功的项目个数</returns>
        public static long SaveSet<T>(this IRedisClient redisClient, string key, T[] items)
        {
            var task = redisClient.SAddAsync<T>(key, items);
            task.Wait();
            return task.Result;
        }

        /// <summary>
        /// 得到指定Set中的项目
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="redisClient">Redis客户端实例</param>
        /// <param name="setName">Set键值</param>
        /// <returns>此Set中的所有项目,以数组的形式返回</returns>
        public static T[] GetSet<T>(this IRedisClient redisClient, string key)
        {
            List<T> items = new List<T>();

            var t = redisClient.ExecuteAsync("smembers", key);
            t.Wait();

            if (t.Result.AsMultiBulk().Count() > 0)
            {
                foreach (var item in t.Result.AsMultiBulk())
                {
                    items.Add(item.As<T>());
                }
            }
            return items.ToArray<T>();
        }

        #endregion
    }

    public class JsonSerializer : BasicRedisSerializer
    {
        internal static readonly byte[] Null = new byte[] { 0 };
        internal const string DatetimeFormat = "yyyy-MM-dd'T'HH:mm:ss.fffffff";

        #region Serialization

        public override byte[] Serialize(object value)
        {
            if (value == null) return Null;

            var type = value.GetType();

            if (type == typeof(string))
                return SerializeString(value.ToString());
            if (type == typeof(byte[]))
                return value as byte[];
            if (type.IsEnum)
                return SerializeString(value.ToString());
            if (type == typeof(DateTime))
                return SerializeString((value as IFormattable).ToString(DatetimeFormat, CultureInfo.InvariantCulture));
            if (type == typeof(Guid))
                return SerializeString(value.ToString());
            if (type == typeof(int) || type == typeof(long) || type == typeof(byte) || type == typeof(short) ||
                type == typeof(uint) || type == typeof(ulong) || type == typeof(sbyte) || type == typeof(ushort) ||
                type == typeof(bool) || type == typeof(decimal) || type == typeof(double) || type == typeof(char))
                return SerializeString((value as IConvertible).ToString(CultureInfo.InvariantCulture));

            var result = SerializeComplexValue(value);

            if (result.SequenceEqual(Null))
                throw new SerializationException("Serializer returned unexpected result. byte[]{0} value is reserved for NULL");

            return result;
        }

        protected virtual byte[] SerializeComplexValue(object value)
        {
            string json = JsonConvert.SerializeObject(value);
            return Encoding.UTF8.GetBytes(json);
        }

        #endregion

        #region Deserialization

        public override object Deserialize(Type type, byte[] value)
        {
            if (value == null || value.SequenceEqual(Null)) return null;

            if (type == typeof(string))
                return DeserializeToString(value);
            if (type == typeof(byte[]))
                return value;
            if (type.IsEnum)
                return DeserializeToEnum(DeserializeToString(value), type);
            if (type == typeof(DateTime))
                return DateTime.ParseExact(DeserializeToString(value), DatetimeFormat, CultureInfo.InvariantCulture);
            if (type == typeof(Guid))
                return Guid.Parse(DeserializeToString(value));
            if (type == typeof(int))
                return int.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(long))
                return long.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(byte))
                return byte.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(short))
                return short.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(uint))
                return uint.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(ulong))
                return ulong.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(sbyte))
                return sbyte.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(ushort))
                return ushort.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(bool))
                return bool.Parse(DeserializeToString(value));
            if (type == typeof(decimal))
                return decimal.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(double))
                return double.Parse(DeserializeToString(value), CultureInfo.InvariantCulture);
            if (type == typeof(char))
                return DeserializeToString(value)[0];

            return DeserializeComplexValue(type, value);
        }

        protected override object DeserializeComplexValue(Type type, byte[] value)
        {
            string json = Encoding.UTF8.GetString(value);
            return JsonConvert.DeserializeObject(json, type);
        }

        #endregion

        private static object DeserializeToEnum(string value, Type enumType)
        {
            try
            {
                return Enum.Parse(enumType, value, true);
            }
            catch (Exception ex)
            {
                throw new SerializationException("Invalid enum value. Enum type: " + enumType.Name, ex);
            }
        }

        private static byte[] SerializeString(string value)
        {
            return Encoding.UTF8.GetBytes(value);
        }

        private static string DeserializeToString(byte[] value)
        {
            return Encoding.UTF8.GetString(value);
        }
    }
}


测试调用:
using Newtonsoft.Json;
using RedisBoost;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RedisDemo
{
    class Program
    {
        static IRedisClient Client = RedisUtil.GetRedisClient("127.0.0.1", 6379);

        static List<CustomClass> CustomItems = new List<CustomClass>() 
            { 
                new CustomClass() { ID = 1, Name = "a" },
                new CustomClass() { ID = 2, Name = "b" },
                new CustomClass() { ID = 3, Name = "c" }
            };

        static void Main(string[] args)
        {
            Console.WriteLine("Test SaveSet()");
            SaveSet();
            Console.WriteLine("Press <Enter> to continue...");
            Console.ReadLine();

            Console.WriteLine("Test SaveSetAsync()");
            SaveSetAsync();
            Console.WriteLine("Press <Enter> to continue...");
            Console.ReadLine();

            Console.WriteLine("Test GetSet()");
            GetSet();
            Console.WriteLine("Press <Enter> to continue...");
            Console.ReadLine();

            Console.WriteLine("Test GetSetAsync()");
            GetSetAsync();

            Console.ReadLine();
        }

        static void SaveSet()
        {
            var result = Client.SaveSet<CustomClass>("SetSync_1", CustomItems.ToArray());
            Console.WriteLine("Affect items:" + result);
        }

        static void SaveSetAsync()
        {
            var task = Client.SaveSetAsync("SetAsync_1", CustomItems.ToArray());
            task.ContinueWith(res =>
            {
                Console.WriteLine("Affect items:" + res.Result);
            });
            Console.WriteLine("Saving...");
        }

        static void GetSet()
        {
            var items1 = Client.GetSet<CustomClass>("SetSync_1");
            Console.WriteLine("Affect items:" + items1.Count());
        }

        static void GetSetAsync()
        {
            var items2 = Client.GetSetAsync<CustomClass>("SetAsync_1");
            items2.ContinueWith(res =>
            {
                Console.WriteLine("Affect items:" + res.Result.Count());
            });
            Console.WriteLine("Getting...");
        }

        public class CustomClass
        {
            public int ID { get; set; }

            public string Name { get; set; }
        }
    }
}

Package.config:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Newtonsoft.Json" version="8.0.2" targetFramework="net45" />
  <package id="RedisBoost" version="1.6.4" targetFramework="net45" />
</packages>

Enjoy:).


Redis 客户端:http://redisdesktop.com/download


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值