Redis系列(二)-Hredis客戶端設計及開源

接上篇c#實現redis客戶端(一),從新整理些了下。html

閱讀目錄:git

  1. 項目說明
  2. Hredis設計圖  
  3. 單元測試場景 
  4. 總結

項目說明

  背景:由於有地方要用,而又沒找到對sentinel良好支持的Net客戶端,因此就簡單重寫了個。github

  目標:儘量的簡單,輕量級,不進行過分的封裝,使用方便。redis

  代碼說明:
c#

        1. 與Redis服務端的Socket通訊、協議格式封裝。在RedisBaseClient裏 
多線程

        2. 只對Set、Get封裝,暴露出Send接口。在RedisCommand裏面添加本身想要的支持。併發

var info = rcClient.Send(RedisCommand.INFO);

       3.  RedisBaseClient是通訊層。 若是擴展其餘用途繼承便可,好比RedisPubSub:RedisBaseClient,RedisSentinel:RedisBaseClientsocket

       4.  供上層良好調用的話,能夠作成partial類擴展redisclient。好比RedisClient.Stringide

       5.  訂閱的監聽使用while的,可在觸發事件裏面作阻塞。post

       6.  PoolRedisClient池的實現使用ConcurrentStack,僅達到了複用socket鏈接的目的。

       7.  支持socket重連,作法是關閉舊鏈接,從新創建新socket。

       8.  多個命令使用管道實現。見set實現

  後續思路:

      一:PoolRedisClient池裏面鏈接的釋放問題?

           1. 使用using  

           2. 不用使用using,會自動檢測並回收 。 

           不作成自動檢測的話,就會出現鏈接沒法釋放的問題,總會有人忘記釋放的,因此要優化成1+2結合的方式。

     二:client池和socket池分離,socket單獨作一個池? 還在考慮中。   

Hredis設計圖

命令執行流程圖、解決方案圖、類圖。

  

單元測試場景  

一. Info命令通訊、密碼配置。

 [TestMethod, TestCategory("Server")]
        public void Redis_PassWord()
        {
            using (var rcClient = new RedisClient(new RedisConfiguration()
            {
               Host = ip,
               Port = 6381,
               PassWord = "123465"
            }))
            {
                var info = rcClient.Send(RedisCommand.INFO);
                Debug.Write(info.ToString());
            }
        }

 二. 普通訂閱,及模式匹配訂閱。這裏訂閱的是Sentinel事件信息。

        [TestMethod, TestCategory("PushSub")]
        public void Subscribe_Sentinel_Test() { using (RedisPubSub rsc = new RedisPubSub("127.0.0.1", 20001)) { rsc.SubscriptionReceived += rsc_SubscriptionReceived; //rsc.Subscribe("+sdown");  } }
[TestMethod, TestCategory("PushSub")]
        public void PSubscribe_Sentinel_Test()
        {
            using (RedisPubSub rsc = new RedisPubSub("127.0.0.1", 20001))
            {
                rsc.SubscriptionReceived += rsc_SubscriptionReceived;
               // rsc.PSubscribe("*");
            }
        }


        private void rsc_SubscriptionReceived(object sender, object args)
        {
            if (args is object[])
            {
                var list = args as object[];
                foreach (var o in list)
                {
                    Debug.Write("\r\n" + o.ToString());
                }
            }
            else
            {
                Debug.Write("\r\n" + args.ToString());
            }

            var sr = sender as RedisPubSub;
            sr.UnSubscribe("*");
        }
View Code

三.  client鏈接池、Parallel併發模擬。

  [TestMethod, TestCategory("poolRedisclient")]
        public void GetClient_Test()
        {
            PoolRedisClient prc = new PoolRedisClient(new PoolConfiguration()
            {
                Host = ip,
                Port = port,
                MaxClients = 100
            });
            using (var client = prc.GetClient())
            {
                client.Set("GetClient_Test", "GetClient_Test");

                var info2 = client.Get("GetClient_Test");

                Assert.AreEqual(info2.ToString(), "GetClient_Test");
            }
            prc.Dispose();
        }
View Code
        [TestMethod, TestCategory("poolRedisclient")]
        public void Parallel_PoolClient_Test()
        {
            PoolRedisClient prc = new PoolRedisClient(new PoolConfiguration()
            {
                Host = ip,
                Port = port,
                MaxClients = 100
            });
            Parallel.For(0, 1000, new ParallelOptions() {MaxDegreeOfParallelism = 100}, (index, item) =>
            {
                using (var client = prc.GetClient())
                {
                    Thread.Sleep(100);
                    client.Set("Parallel_PoolClient_Test" + index, "Parallel_PoolClient_Test");

                    var info2 = client.Get("Parallel_PoolClient_Test" + index);

                    Assert.AreEqual(info2.ToString(), "Parallel_PoolClient_Test");
                }
            });
            prc.Dispose();
        }
View Code

三.  超時重連、多線程併發超時重連。

        [TestMethod, TestCategory("poolRedisclient")]
        public void PoolClient_TimeOut_Test()
        {
            PoolRedisClient prc = new PoolRedisClient(new PoolConfiguration()
            {
                Host = ip,
                Port = port,
                MaxClients = 100
            });
            object info2;
            using (var client = prc.GetClient())
            {
                var result = client.Set("PoolClient_TimeOut_Test", "PoolClient_TimeOut_Test");
                Thread.Sleep(15000);
                info2 = client.Get("PoolClient_TimeOut_Test");

            }
            Assert.AreEqual(info2.ToString(), "PoolClient_TimeOut_Test");

            prc.Dispose();
        }
View Code
  [TestMethod, TestCategory("poolRedisclient")]
        public void Thread_PoolClient_Test()
        {
            PoolRedisClient prc = new PoolRedisClient(new PoolConfiguration()
            {
                Host = ip,
                Port = port
            });
            Parallel.For(0, 1000, new ParallelOptions() {MaxDegreeOfParallelism = 100}, (index, item) =>
            {
                var t = new Thread(() =>
                {
                    Thread.Sleep(1000);
                    object info2;
                    using (var client = prc.GetClient())
                    {
                        client.Set("Parallel_PoolClient_Test" + index, "Parallel_PoolClient_Test");

                       Thread.Sleep(15000);

                        info2 = client.Get("Parallel_PoolClient_Test" + index);
                    }
                    Assert.AreEqual(info2.ToString(), "Parallel_PoolClient_Test");
                });
                t.Start();
            });
            Thread.Sleep(20000);
            prc.Dispose();
        }

四. String類型添加、過時時間添加。

[TestMethod, TestCategory("String")]
        public void Set_Get_key() { using (var rcClient = new RedisClient(ip, port)) { rcClient.Set("Set_Get_key", "Set_Get_key"); var info2 = rcClient.Get("Set_Get_key"); Assert.AreEqual(info2.ToString(), "Set_Get_key"); } }
  [TestMethod, TestCategory("String")]
        public void Set_key_Expire()
        {
            using (var rcClient = new RedisClient(ip, port))
            {
                rcClient.Set("Set_key_Expire", "Set_key_Expire", 10);

                var info1 = rcClient.Get("Set_key_Expire");

                Assert.AreEqual(info1.ToString(), "Set_key_Expire");

                Thread.Sleep(11000);
                var info2 = rcClient.Get("Set_key_Expire");

                Assert.AreEqual(info2, null);

            }
        }
View Code

總結

開源地址:https://github.com/mushroomsir/HRedis  有須要的同窗,能夠參考下。

Hredis後續會跟實際需求來寫,若是有更好的實現思路,歡迎一塊兒交流。

相關文章
相關標籤/搜索