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的值。

現在用指令登入Redis再示範一遍這個過程,如下圖:
6)RedisHandle操作類包含的操作,大緻如下圖,Redis支援的資料類型比Memcache多,而且很實用,如果你的系統存取緩存會涉及比較複雜的邏輯,推薦使用Redis,Memcache能的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步你可不做,看看就好):
再搜尋“AssertValidUsage”,發現另一個地方的6000次限制,如下圖:
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修改完成。
4)再用ILSpy看這兩個值,已經修改了,如下圖(第4步你也可不做,看看就好):
5)把修改的dll覆寫原來dll,最好在IDE中把原來的引用移除,重新添加引用一次,以防有緩存執行的還是舊的dll。編寫如下代碼測試:
覆寫dll之前會報6000限制,覆寫之後輸出ok正常:
完美,點贊!
分享、互相交流學習