天天看點

ServiceStack.Redis高效封裝和簡易破解

 1.ServiceStack.Redis封裝

封裝的Redis操作類名為RedisHandle,如下代碼塊(隻展示部分代碼),它的特點:

1)使用連接配接池管理連接配接,見代碼中的PooledClientManager屬性。如果不用連接配接池,而是代碼直接RedisClient client = new RedisClient("localhost", 6379, "password");去擷取一個連接配接執行個體操作,那麼當Redis操作頻繁時,代價很大,不可行。

2)支援讀寫分離的Redis服務端(如果你隻用一個Redis服務端,那麼讀寫服務端連接配接字元串一樣即可)。

3)操作Redis時,自動切換讀寫Redis連接配接執行個體,見代碼中的GetRedisClient函數,所有寫操作取“寫連接配接執行個體”PooledClientManager.GetClient(),所有讀操作取“讀連接配接執行個體”PooledClientManager.GetReadOnlyClient()。

注意:如果你讀寫是兩個做了主從複制的Redis服務端,那麼要考慮主從複制是否有延遲,是否有一些讀操作要求實時資料,如果是,那麼需要在GetXX讀資料時用寫連接配接執行個體。這時候,可以改寫此GetXX函數,可在函數參數末尾增加 bool? isReadOnly = null 帶預設值的參數,即支援外部調用指定用哪種連接配接執行個體操作。這種情況一般是系統把Redis當作一個NoSql資料庫;而更多時候我們系統是把Redis當作一個緩存,不需要做主從複制,讀寫連接配接執行個體指向的是同一個Redis服務端,當系統比較大時可能會用到緩存叢集(比如一緻性哈希緩存等)。

4)後繼如果Redis需要做一緻性哈希等叢集,那麼可以執行個體化多個RedisHandle執行個體,然後撰寫算法來取相應的RedisHandle執行個體。 

1 namespace NetDh.RedisUtility
  2 {
  3     /*
  4      * 一個RedisHandle執行個體對應一個Redis服務端或者一組主從複制Redis服務端。
  5      * 如果Redis需要做一緻性哈希等叢集,則要自己撰寫算法來取相應的RedisHandle執行個體。
  6      */
  7 
  8     /// <summary>
  9     /// Redis操作類
 10     /// </summary>
 11     public class RedisHandle
 12     {
 13         /// <summary>
 14         /// Redis連接配接池管理執行個體
 15         /// </summary>
 16         public PooledRedisClientManager PooledClientManager { get; set; }
 17 
 18         /* 如果你的需求需要經常切換Redis資料庫,則可把Db當屬性,這樣每一個RedisHandle執行個體可以對應操作某Redis的某個資料庫。此時,可在構造函數中增加int db參數。*/
 19         ///// <summary>
 20         ///// 一個Redis服務端預設有16個資料庫,預設都是用第0個資料庫。如果需要切換資料庫,則傳入db值(0~15)
 21         ///// </summary>
 22         //public int Db { get; set; }
 23 
 24         /// <summary>
 25         /// 構造函數
 26         /// </summary>
 27         public RedisHandle()
 28         {
 29             #region 此代碼為建立“連接配接池示例”,配置資訊直接用靜态類RedisClientConfig1承載,你也可以選擇用配置檔案承載
 30             var config = new RedisClientManagerConfig
 31             {
 32                 AutoStart = true,
 33                 MaxWritePoolSize = RedisClientConfig1.MaxWritePoolSize,
 34                 MaxReadPoolSize = RedisClientConfig1.MaxReadPoolSize,
 35                 DefaultDb = RedisClientConfig1.DefaultDb,
 36             };
 37             //如果你隻用到一個Redis服務端,那麼配置讀寫時就指定一樣的連接配接字元串即可。
 38             PooledClientManager = new PooledRedisClientManager(RedisClientConfig1.ReadWriteServers
 39                 , RedisClientConfig1.ReadOnlyServers, config)
 40             {
 41                 ConnectTimeout = RedisClientConfig1.ConnectTimeout,
 42                 SocketSendTimeout = RedisClientConfig1.SendTimeout,
 43                 SocketReceiveTimeout = RedisClientConfig1.ReceiveTimeout,
 44                 IdleTimeOutSecs = RedisClientConfig1.IdleTimeOutSecs,
 45                 PoolTimeout = RedisClientConfig1.PoolTimeout
 46             };
 47             #endregion
 48         }
 49         /// <summary>
 50         /// 構造函數
 51         /// </summary>
 52         /// <param name="poolManager">連接配接池,外部傳入自己建立的PooledRedisClientManager連接配接池對象,
 53         /// 可以把其它RedisHandle執行個體的PooledClientManager傳入,共用連接配接池</param>
 54         public RedisHandle(PooledRedisClientManager poolManager)
 55         {
 56             PooledClientManager = poolManager;
 57 
 58         }
 59         /// <summary>
 60         /// 擷取Redis用戶端連接配接對象,有連接配接池管理。
 61         /// </summary>
 62         /// <param name="isReadOnly">是否取隻讀連接配接。Get操作一般是讀,Set操作一般是寫</param>
 63         /// <returns></returns>
 64         public RedisClient GetRedisClient(bool isReadOnly = false)
 65         {
 66             RedisClient result;
 67             if (!isReadOnly)
 68             {
 69                 //RedisClientManager.GetCacheClient()會傳回一個新執行個體,而且隻提供一小部分方法,它的作用是幫你判斷是否用寫執行個體還是讀執行個體
 70                 result = PooledClientManager.GetClient() as RedisClient;
 71             }
 72             else
 73             {
 74                 //如果你讀寫是兩個做了主從複制的Redis服務端,那麼要考慮主從複制是否有延遲。有一些讀操作是否是即時的,需要在寫執行個體中擷取。
 75                 result = PooledClientManager.GetReadOnlyClient() as RedisClient;
 76             }
 77             //如果你的需求需要經常切換Redis資料庫,則下一句可以用。否則一般都隻用預設0資料庫,叢集是沒有資料庫的概念。
 78             //result.ChangeDb(Db);
 79             return result;
 80         }
 81 
 82         #region 存儲單值 key-value,其中value是string,使用時如果value是int,可以把比如int轉成string存儲
 83         public void SetValue(string key, string value, int expirySeconds = -1)
 84         {
 85             using (RedisClient redisClient = GetRedisClient())
 86             {
 87                 //redisClient.SetEntry(key, value, expireIn);
 88                 if (expirySeconds == -1)
 89                 {
 90                     redisClient.SetValue(key, value);
 91                 }
 92                 else
 93                 {
 94                     redisClient.SetValue(key, value, new TimeSpan(0, 0, 0, expirySeconds));
 95                 }
 96             }
 97         }
 98 
 99         public string GetValue(string key)
100         {
101             using (RedisClient redisClient = GetRedisClient(true))
102             {
103                 var val = redisClient.GetValue(key);
104 
105                 return val;
106             }
107         }
108 
109         public bool Remove(string key)
110         {
        ...      

5)在GetRedisClient函數中有句注釋的代碼//result.ChangeDb(Db);。其中,ChangeDb是切換Redis資料庫(Redis預設有16個資料庫,見redis-server.exe目錄下的redis.conf配置檔案中的“databases 16”)。我們一般預設都是用第0個資料庫,如果需要切換資料庫,則傳入Db值(0~15)。我這邊一般不會用到切換資料庫的需求,如果你的需求需要經常切換Redis資料庫,此句可用。否則一般都隻用預設0資料庫,叢集是沒有資料庫的概念。

為了說明一個Redis服務端有多個資料庫以及資料庫之間的切換,做個小示例,如下圖,我在Redis的第0個資料庫存放了鍵值對資料"test2:1",當我切到第1個資料庫ChangeDb(1)時,GetValue("test2")傳回的是null,當切回第0個資料庫時,就取到1的值。

ServiceStack.Redis高效封裝和簡易破解

 現在用指令登入Redis再示範一遍這個過程,如下圖:

ServiceStack.Redis高效封裝和簡易破解

  6)RedisHandle操作類包含的操作,大緻如下圖,Redis支援的資料類型比Memcache多,而且很實用,如果你的系統存取緩存會涉及比較複雜的邏輯,推薦使用Redis,Memcache能的Redis都能。

ServiceStack.Redis高效封裝和簡易破解

完整的源碼請參考:https://gitee.com/donghan/NetDh-Framework/tree/master/Data/NetDh.RedisUtility

此工具類已經并到我的NetDh架構項目中,NetDh架構碼雲位址:https://gitee.com/donghan/NetDh-Framework 

2.ServiceStack.Redis破解

我這邊封裝的是ServiceStack.Redis最新版本5.7.0,它在4.0版本之後就商業化,有做限制:每小時隻能有6000次的Redis通路。網上有對ServiceStack.Redis和StackExchange.Reids進行比較,結果是前者性能比較好,不管真假,我是ServiceStack.Redis 3.x就開始用它了,一如既往繼續用呗,有限制就破解呗。

步驟:

1)限制6000次是在ServiceStack.Text.dll中,而且在兩個地方,用ILSpy打開ServiceStack.Text.dll,在搜尋欄輸入“RedisRequestPerHour”,可以看到RedisRequestPerHour=6000的限制,如下圖(第1步你可不做,看看就好):

ServiceStack.Redis高效封裝和簡易破解

  再搜尋“AssertValidUsage”,發現另一個地方的6000次限制,如下圖:

ServiceStack.Redis高效封裝和簡易破解

2)下載下傳一個十六進制編輯器,我網上找的是wxMEdit工具(下載下傳頁面:http://wxmedit.github.io/downloads.html)。

3)先備份ServiceStack.Text.dll,用十六進制編輯器打開ServiceStack.Text.dll。

分析:6000轉換成位元組形式是 70 17 00 00(雖然6000的16進制是00001770),int的最大值2147483647轉換成位元組形式是 FF FF FF 7F,是以隻要把70 17 00 00替換成FF FF FF 7F即可。

如下圖,替換之前點了“查找下一個”發現全局就兩個地方,那就确定是要修改的值,然後點選“替換”兩次,ctrl+s儲存檔案,dll修改完成。

ServiceStack.Redis高效封裝和簡易破解

 4)再用ILSpy看這兩個值,已經修改了,如下圖(第4步你也可不做,看看就好):

ServiceStack.Redis高效封裝和簡易破解

5)把修改的dll覆寫原來dll,最好在IDE中把原來的引用移除,重新添加引用一次,以防有緩存執行的還是舊的dll。編寫如下代碼測試:

ServiceStack.Redis高效封裝和簡易破解

覆寫dll之前會報6000限制,覆寫之後輸出ok正常:

ServiceStack.Redis高效封裝和簡易破解

  完美,點贊!

分享、互相交流學習

繼續閱讀