.NET平台下Redis使用(五)【StackExchange.Redis和Protobuf-net】

本文介绍了一种使用Redis缓存结合Protobuf.NET序列化的高效数据存储方案。通过具体示例演示了不同类型数据的存储与读取过程,并比较了有属性与无属性实体类在序列化过程中的差异。

没有应该获得的成功,只有不肯放弃的梦想


Models层实体类:

[ProtoContract]
    public class People
    {
        [ProtoMember(1)]
        public int ID { get; set; }

        [ProtoMember(2)]
        public string FirstName { get; set; }

        [ProtoMember(3)]
        public string LastName { get; set; }

        [ProtoMember(4)]
        public DateTime BirthDate { get; set; }

        [ProtoMember(5)]
        public bool Active { get; set; }

        [ProtoMember(6)]
        public AddressModel Address { get; set; }
    }

    [ProtoContract]
    public class AddressModel
    {
        [ProtoMember(1)]
        public int AptNumber { get; set; }

        [ProtoMember(2)]
        public string StreetAdress { get; set; }

        [ProtoMember(3)]
        public string ZipCode { get; set; }

        [ProtoMember(4)]
        public string City { get; set; }

        [ProtoMember(5)]
        public string State { get; set; }

        [ProtoMember(6)]
        public string Country { get; set; }
    }


    [ProtoContract]
    public class PeopleNoAttr
    {
        [ProtoMember(1)]
        public int ID { get; set; }

        [ProtoMember(2)]
        public string FirstName { get; set; }

        [ProtoMember(3)]
        public string LastName { get; set; }

        [ProtoMember(4)]
        public DateTime BirthDate { get; set; }

        [ProtoMember(5)]
        public bool Active { get; set; }

        [ProtoMember(6)]
        public AddressNoAttr Address { get; set; }
    }

    [ProtoContract]
    public class AddressNoAttr
    {
        [ProtoMember(1)]
        public int AptNumber { get; set; }

        [ProtoMember(2)]
        public string StreetAdress { get; set; }

        [ProtoMember(3)]
        public string ZipCode { get; set; }

        [ProtoMember(4)]
        public string City { get; set; }

        [ProtoMember(5)]
        public string State { get; set; }

        [ProtoMember(6)]
        public string Country { get; set; }
    }

Program.cs主程序:

 internal class Program
    {
        private static IDatabase db = RedisConnection.Instance.GetDatabase();

        private static void Main(string[] args)
        {
            SetAndGet();
            TestInteger();
            TestDouble();
            TestBool();
            TestSerializedDate();
            TestProtobufNet();
            TestSerializedArray();
            TestProtobufAndRedis();
            TestProtobufAndRedis_List();
            TestProtobufAndRedis_IEnumerable();
            TestDeleteKey();
            TestSync();
            TestAsync();
            Console.ReadKey();
        }


        public static void SetAndGet()
        {
            const string value = "abcdefg";
            db.StringSet("mykey", value);
            var val = db.StringGet("mykey");
            Console.WriteLine(val);
        }


        public static void TestInteger()
        {
            const int num = 5;
            db.StringSet("StackExchangeRedis_TestInteger", num);
            var val = db.StringGet("StackExchangeRedis_TestInteger");
            Console.WriteLine(val);
        }


        public static void TestDouble()
        {
            const double num = 5.34567;
            db.StringSet("StackExchangeRedis_TestDouble", num);
            var val = db.StringGet("StackExchangeRedis_TestDouble");
            Console.WriteLine(val);
        }


        public static void TestBool()
        {
            const bool b = true;
            db.StringSet("StackExchangeRedis_TestBoolT", b);
            var val = db.StringGet("StackExchangeRedis_TestBoolT");
            Console.WriteLine(val);
        }


        public static void TestSerializedDate()
        {
            DateTime now = DateTime.Now;
            SetCache<DateTime>("StackExchangeRedis_TestSerializedDate", now);
            var val = GetCache<DateTime>("StackExchangeRedis_TestSerializedDate");
            Console.WriteLine(now);
            Console.WriteLine(val);
        }


        public static void TestProtobufNet()
        {
            var ppl = new People()
            {
                ID = 1,
                FirstName = "John",
                LastName = "Doe",
                Address = new AddressModel()
                {
                    AptNumber = 56,
                    StreetAdress = "123 Main Street",
                    City = "Toronto",
                    State = "Ontario",
                    Country = "Canada"
                }
            };
            using (var file = File.Create("person.bin"))
            {
                Serializer.Serialize<People>(file, ppl);
            }

            People newPerson;
            using (var file = File.OpenRead("person.bin"))
            {
                newPerson = Serializer.Deserialize<People>(file);
            }
            Console.WriteLine(newPerson.Address.StreetAdress);
            Console.WriteLine(newPerson.Address.Country+"=="+ppl.Address.Country);
        }


        public static void TestSerializedArray()
        {
            int[] arr = new int[4] {5, 7, 11, 17};
            SetCache<int[]>("StackExchangeRedis_TestSerializedArray", arr);
            Console.WriteLine("Array length = " + arr.Length);
            arr = GetCache<int[]>("StackExchangeRedis_TestSerializedArray");
            Console.WriteLine("Deserialized array length = " + arr.Length);
            Console.WriteLine(arr[2]);
        }


        public static void TestProtobufAndRedis()
        {
            var ppl = new PeopleNoAttr()
            {
                ID = 2,
                FirstName = "Jane",
                LastName = "Smith",
                Address = new AddressNoAttr()
                {
                    AptNumber = 56,
                    StreetAdress = "123 Main Street",
                    City = "Toronto",
                    State = "Ontario",
                    Country = "Canada"
                }
            };
            SetCacheNoAttr<PeopleNoAttr>("StackExchangeRedis_TestProtobufAndRedis_NoAttr", ppl);
            var val2 = GetCache<PeopleNoAttr>("StackExchangeRedis_TestProtobufAndRedis_NoAttr");
            Console.WriteLine(val2.Address.AptNumber);
        }


        public static void TestProtobufAndRedis_List()
        {
            var cachekey = "StackExchangeRedis_TestProtobufAndRedisList";
            List<People> ppl = GenerateList();
            SetCache<List<People>>(cachekey, ppl);
            var val2 = GetCache<List<People>>(cachekey);
            Console.WriteLine(val2[1].Address.StreetAdress);
        }


        public static void TestProtobufAndRedis_IEnumerable()
        {
            var cachekey = "StackExchangeRedis_TestProtobufAndRedisIEnumerable";
            List<People> ppl = GenerateList();
            IEnumerable<People> Ippl = (IEnumerable<People>) ppl;
            SetCache<IEnumerable<People>>(cachekey, ppl);
            var val2 = GetCache<IEnumerable<People>>(cachekey);
            var el = val2.ElementAt(1);
            Console.WriteLine(el.Address.StreetAdress);
        }


        public static void TestDeleteKey()
        {
            DeleteFromCache("StackExchangeRedis_TestProtobufAndRedis");
        }

        // TO DO:
        // =====
        // no attributes
        // twemproxy
        // compare to old redis: C:\workspace\CareerCruising_Core\CC.Data_Tests\RemoteCacheProvider_Test.cs


        //******************************

        public static void TestSync()
        {
            var aSync = db.StringGet("StackExchangeRedis_TestDouble");
            var bSync = db.StringGet("StackExchangeRedis_TestInteger");
        }

        public static void TestAsync()
        {
            var aPending = db.StringGetAsync("StackExchangeRedis_TestDouble");
            var bPending = db.StringGetAsync("StackExchangeRedis_TestInteger");
            var a = db.Wait(aPending);
            var b = db.Wait(bPending);
        }

        //******************************

        /*
        public static void TestExpirationDate()
        {
            var cachekey = "StackExchangeRedis_TestExpirationDate";
            db.StringSet(cachekey, "testing expiration date");
            db.KeyExpire(cachekey, TimeSpan.FromMinutes(30));
            var ttl = db.KeyTimeToLive(cachekey);
            Console.Write(ttl);
        }

        
        public static void TestDeleteKeysByPartOfName()
        {
            DeleteKeysByPartOfName("StackExchangeRedis_");
        }*/

        /*
        public static void TestDeleteAllKeys()
        {
            ClearCache();
        }*/

        #region non-test methods

        
        private static byte[] DataToBytes<T>(T data)
        {
            MemoryStream stream = new MemoryStream();
            //ProtoBuf序列化开始
            Serializer.Serialize(stream, data);
            byte[] bytes = stream.ToArray();
            stream.Close();
            return bytes;
        }

        private static List<People> GenerateList()
        {
            List<People> ppl = new List<People>();
            var person1 = new People()
            {
                ID = 1,
                FirstName = "Jane",
                LastName = "Smith",
                Address =
                    new AddressModel()
                    {
                        AptNumber = 51,
                        StreetAdress = "123 Main Street",
                        City = "Toronto",
                        State = "Ontario",
                        Country = "Canada"
                    }
            };
            var person2 = new People()
            {
                ID = 2,
                FirstName = "John",
                LastName = "Doe",
                Address =
                    new AddressModel()
                    {
                        AptNumber = 52,
                        StreetAdress = "678 Main Street",
                        City = "Toronto1",
                        State = "Ontario1",
                        Country = "Canada1"
                    }
            };
            ppl.Add(person1);
            ppl.Add(person2);
            return ppl;
        }

        // Serialization/deserialization and caching:
        public static bool SetCache<T>(string key, T value)
        {
            if (!string.IsNullOrWhiteSpace(key))
            {
                return db.StringSet(key, DataToBytes<T>(value));
            }
            return false;
        }

        public static bool SetCacheNoAttr<T>(string key, T value)
        {
            if (!string.IsNullOrWhiteSpace(key))
            {

                Console.WriteLine(DataToBytes<T>(value));
                return db.StringSet(key, DataToBytes<T>(value));
            }
            return false;
        }


        public static T GetCache<T>(string key)
        {
            byte[] val = db.StringGet(key);
            MemoryStream stream = new MemoryStream(val, false);
            return Serializer.Deserialize<T>(stream);
        }

        public static bool DeleteFromCache(string key)
        {
            return db.KeyDelete(key);
        }

        public bool DeleteKeysByPartOfName(string pattern)
        {
            bool result = true;
            var keysPattern = string.Format("*{0}*", pattern);
            /*foreach (var key in server.Keys(pattern: keysPattern))
            {
                if (!db.KeyDelete(key))
                    result = false;
            }*/
            return result;
        }

        /*public static void ClearCache()
        {
            server.FlushDatabase();
        }*/

        #endregion
    }

RedisConnection.cs代码:

  public sealed class RedisConnection
    {
        //自动配置与手动配置

        //在很多常见的情况下,StackExchange.Redis 将会自动的配置多个设置选项,包括服务器类型和版本,连接超时和主/从关系配置。可是有时候在Redis服务器这个命令是被禁止的。在这种情况下,提供更多的信息是非常有用的:
        private static ConfigurationOptions configOptions = new ConfigurationOptions
        {
            EndPoints =
            {
                { "127.0.0.1", 6379 }
            },
            CommandMap = CommandMap.Create(new HashSet<string>
            { 
                // 排除几个命令
                //"INFO", "CONFIG", "CLUSTER", "PING", "ECHO", "CLIENT"
            }, available: false),
            AllowAdmin = true,
            Proxy = Proxy.Twemproxy,
            Password = "123456",
        };

        private static readonly ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(configOptions);

        static RedisConnection()
        {

        }

        /// <summary>
        /// 获得Redis的连接实例
        /// </summary>
        public static ConnectionMultiplexer Instance
        {
            get
            {
                return redis;
            }
        }
    }

运行结果:

这里写图片描述


这里写图片描述

StackExchange.Redis,这是redis.net客户端之一。Redis是一个开源的内存数据存储,可以用来做数据库,缓存或者消息代理服务。目前有不少人在使用    ServiceStack.Redis这个.net客户端,但是这个的最新版本目前已经变成了商业软件。对于    ServiceStack.Redis这种行为,我们没有什么好说的,留给我们的选择是使用低版本的开源版本或者转向其他的客户端。要说到StackExchange.Redis,就不得不说它BookSleeve的关系。BookSleeve已经是比较完善的redis sdk,但是为什么 BookSleeve 的作者要重新写一个redis 的客户端sdk呢? 有兴趣的同学可以看这里    why i wrote another redis client归纳起来其实就一句话:觉得不爽就推倒重来。StackExchange.Redis 安装直接命令或者手动NuGet。 PM> Install-Package StackExchange.Redis 如果需要强签名的版本走下面的命令,当然作者对于强签名的事也是充满了    怨念  PM> Install-Package StackExchange.Redis.StrongName ConnectionMultiplexerConnectionMultiplexer对象是StackExchange.Redis最中枢的对象。这个类的实例需要被整个应用程序域共享重用的,你不要在每个操作中不停的创建该对象的实例,所以使用单例来创建存放这个对象是必须的。public static ConnectionMultiplexer Manager     {         get         {             if (_redis == null)             {                 lock (_locker)                 {                     if (_redis != null) return _redis;                     _redis = GetManager();                     return _redis;                 }             }             return _redis;         }     }     private static ConnectionMultiplexer GetManager(string connectionString = null)     {         if (string.IsNullOrEmpty(connectionString))         {             connectionString = GetDefaultConnectionString();         }         return ConnectionMultiplexer.Connect(connectionString);     }虽然ConnectionMultiplexer是实现了IDisposable接口的,但是我们基于重用的考虑,一般不需要去释放它。当作内存数据库使用IDatabase db = redis.GetDatabase(); 这里的GetDatabase() 返回的db对象是很轻量级别的,不需要被缓存起来,每次用每次拿即可。IDatabase 的所有方法都有同步异步的实现。其中的异步实现都是可以await的。一些基础的操作的封装。public bool Remove(string key)     {         key = MergeKey(key);         var db = RedisManager.Manager.GetDatabase(Database);         return db.KeyDelete(key);     }     public string Get(string key)     {         key = this.MergeKey(key);         var db = RedisManager.Manager.GetDatabase(Database);         return db.StringGet(key);     }     public bool Set(string key, string value, int expireMinutes = 0)    
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值