快取工廠之Redis快取

神牛003發表於2016-08-18

這幾天沒有按照計劃分享技術博文,主要是去醫院了,這裡一想到在醫院經歷的種種,我真的有話要說;醫院裡的醫務人員曾經被吹捧為美麗+和藹+可親的天使,在經受5天左右相互接觸後不得不讓感慨;遇見的有些人員在掛號隊伍猶如長龍的時候坐在收費視窗玩手機,理由是自己是換班的差幾分鐘才上班呢;遇見態度極其惡劣的主任醫師,做諮詢幾個問題聲音馬上提高並言語中攜帶諷刺話語;還有其他幾個遇見哈哈這裡就不多說了,可能是某些醫務人員覺得多您個不少,我有的是客源,所以個別是這種態度吧,還是市醫院真不知道怎麼混進去的。

 

以上是個人的看法,下面來正式分享今天的文章吧:

。搭建Redis服務端,並用客戶端連線

。封裝快取父類,定義Get,Set等常用方法

。定義RedisCache快取類,執行Redis的Get,Set方法

。構造出快取工廠呼叫方法

 

下面一步一個腳印的來分享:

。搭建Redis服務端,並用客戶端連線

首先,咋們去這個地址下載安裝檔案https://github.com/dmajkic/redis/downloads,我這裡的版本是:redis-2.4.5-win32-win64裡面有32位和64位的執行檔案,我這裡伺服器是64位的下面給出截圖和用到部分程式的說明:

現在,咋們直接可以用滑鼠雙擊redis-server.exe這個應用程式,這樣就開啟了redis服務窗體(您也可以下載一個windows服務承載器,把redis服務執行在windows的服務中,就不用擔心每次關閉redis服務黑色窗體後無法訪問redis了),執行起來是這樣:

有紅色框的資訊就表示成功了,這裡redis服務監聽的埠預設是6379,要修改埠或者更多的配置資訊請找到redis.conf配置檔案,具體配置資訊介紹可以來這裡http://www.shouce.ren/api/view/a/6231

再來,開啟客戶端連線服務端,咋們退到64bit資料夾的目錄中,滑鼠移到64bit資料夾上並且安裝Shift鍵,同時點選滑鼠的右鍵,選中"在此處開啟命令視窗"這樣快速進入到了該資料夾的cmd命令視窗中(當然不同的作業系統不同,這裡演示的是windows的操作;還有其他進入的方式這裡不做介紹,因為個人感覺這是最快的);然後,在命令視窗中錄入redis-cli.exe -h localhost -p 6379回車來訪問服務端,效果圖:

再來看下服務端窗體截圖:

沒錯這樣客戶端就連線上服務端了,可以簡單在客戶端執行下set,get命令:

如果是客戶端要訪問遠端的redis服務端,只需要把localhost換成可訪問的ip就行了如果還需要密碼等更多配置請去上面的那個地址連結;

 

。封裝快取父類,定義Get,Set等常用方法

先來,上父類的程式碼:

 1 public class BaseCache : IDisposable
 2     {
 3         protected string def_ip = string.Empty;
 4         protected int def_port = 0;
 5         protected string def_password = string.Empty;
 6 
 7         public BaseCache()
 8         {
 9 
10         }
11 
12         public virtual void InitCache(string ip = "", int port = 0, string password = "")
13         {
14 
15         }
16 
17         public virtual bool SetCache<T>(string key, T t, int timeOutMinute = 10) where T : class,new()
18         {
19 
20             return false;
21         }
22 
23         public virtual T GetCache<T>(string key) where T : class,new()
24         {
25 
26             return default(T);
27         }
28 
29         public virtual bool Remove(string key)
30         {
31 
32             return false;
33         }
34 
35         public virtual bool FlushAll()
36         {
37 
38             return false;
39         }
40 
41         public virtual bool Any(string key)
42         {
43 
44             return false;
45         }
46 
47         public virtual void Dispose(bool isfalse)
48         {
49 
50             if (isfalse)
51             {
52 
53 
54             }
55         }
56 
57         //手動釋放
58         public void Dispose()
59         {
60 
61             this.Dispose(true);
62             //不自動釋放
63             GC.SuppressFinalize(this);
64         }
65     }
View Code

這裡定義的方法沒有太多的註釋,更多的意思我想看方法名稱就明白了,這個父類主要實現了IDisposable,實現的Dispose()中主要用來釋放資源並且自定義了一個 public virtual void Dispose(bool isfalse)方法,這裡面有一句是GC.SuppressFinalize(this);按照官網介紹的意思是阻塞自動釋放資源,其他的沒有什麼了,繼續看下面的

 

。定義RedisCache快取類,執行Redis的Get,Set方法

首先,咋們分別定義類RedisCache,MemcachedCache(這裡暫未實現對memcache快取的操作),並且繼承BaseCache,重寫Set,Get方法如下程式碼:

  1 /// <summary>
  2     /// Redis快取
  3     /// </summary>
  4     public class RedisCache : BaseCache
  5     {
  6         public RedisClient redis = null;
  7 
  8         public RedisCache()
  9         {
 10 
 11             //這裡去讀取預設配置檔案資料
 12             def_ip = "172.0.0.1";
 13             def_port = 6379;
 14             def_password = "";
 15         }
 16 
 17         #region Redis快取
 18 
 19         public override void InitCache(string ip = "", int port = 0, string password = "")
 20         {
 21 
 22             if (redis == null)
 23             {
 24                 ip = string.IsNullOrEmpty(ip) ? def_ip : ip;
 25                 port = port == 0 ? def_port : port;
 26                 password = string.IsNullOrEmpty(password) ? def_password : password;
 27 
 28                 redis = new RedisClient(ip, port, password);
 29             }
 30         }
 31 
 32         public override bool SetCache<T>(string key, T t, int timeOutMinute = 10)
 33         {
 34 
 35             var isfalse = false;
 36 
 37             try
 38             {
 39                 if (string.IsNullOrEmpty(key)) { return isfalse; }
 40 
 41                 InitCache();
 42                 isfalse = redis.Set<T>(key, t, TimeSpan.FromMinutes(timeOutMinute));
 43             }
 44             catch (Exception ex)
 45             {
 46             }
 47             finally { this.Dispose(); }
 48             return isfalse;
 49         }
 50 
 51         public override T GetCache<T>(string key)
 52         {
 53             var t = default(T);
 54             try
 55             {
 56                 if (string.IsNullOrEmpty(key)) { return t; }
 57 
 58                 InitCache();
 59                 t = redis.Get<T>(key);
 60             }
 61             catch (Exception ex)
 62             {
 63             }
 64             finally { this.Dispose(); }
 65             return t;
 66         }
 67 
 68         public override bool Remove(string key)
 69         {
 70             var isfalse = false;
 71             try
 72             {
 73                 if (string.IsNullOrEmpty(key)) { return isfalse; }
 74 
 75                 InitCache();
 76                 isfalse = redis.Remove(key);
 77             }
 78             catch (Exception ex)
 79             {
 80             }
 81             finally { this.Dispose(); }
 82             return isfalse;
 83         }
 84 
 85         public override void Dispose(bool isfalse)
 86         {
 87 
 88             if (isfalse && redis != null)
 89             {
 90 
 91                 redis.Dispose();
 92                 redis = null;
 93             }
 94         }
 95 
 96         #endregion
 97     }
 98 
 99 
100     /// <summary>
101     /// Memcached快取
102     /// </summary>
103     public class MemcachedCache : BaseCache
104     {
105 
106 
107     }
View Code

這裡,用到的RedisClient類是來自nuget包引用的,這裡nuget包是:

然後,來看下重寫的InitCache方法,這裡面有一些ip,port(埠),password(密碼)引數,這裡直接寫入在cs檔案中沒有從配置檔案讀取,大家可以擴充套件下;這些引數通過RedisClient建構函式傳遞給底層Socket訪問需要的資訊,下面簡單展示下RedisClient幾個的建構函式:

1         public RedisClient();
2         public RedisClient(RedisEndpoint config);
3         public RedisClient(string host);
4         public RedisClient(Uri uri);
5         public RedisClient(string host, int port);
6         public RedisClient(string host, int port, string password = null, long db = 0);

至於Get,Set方法最終都是使用RedisClient物件訪問的,個人覺得需要注意的是Set方法裡面的過期時間引數,目前還沒有試驗這種情況的效果:

?通過這幾種方法設定過期時間後,快到過期時間的時候如果此時有使用這個快取key那麼過期時間是否會往後自動增加過期時間有效期,這裡暫時沒有試驗(這裡是由於前面專案中的.net core框架中的memecache快取都有這種設定,想來redis應該也有吧)

這裡,需要重寫下public override void Dispose(bool isfalse)方法,因為呼叫完RedisClient後需要釋放,我們通過Dispose統一來手動釋放,而不是直接在呼叫的時候使用using()

 

。構造出快取工廠呼叫方法

接下來,咋們需要定義一個快取工廠,因為上面剛才定義了一個RedisCache和MemcachedCache明顯這裡會有多個不同快取的方法呼叫,所用咋們來定義個工廠模式來呼叫對應的快取;這裡的工廠模式沒有使用直接顯示建立new RedisCache(),new MemcachedCache()物件的方法,而是使用了反射的原理,建立對應的快取物件;

先來,定義個列舉,列舉裡面的宣告的名字要和咋們快取類的名稱相同,程式碼如下:

1 public enum CacheType
2     {
3         RedisCache,
4 
5         MemcachedCache
6     }

再來,定義個工廠來CacheRepository(快取工廠),並且定義方法Current如下程式碼:

1 public static BaseCache Current(CacheType cacheType = CacheType.RedisCache)
2         {
3             var nspace = typeof(BaseCache);
4             var fullName = nspace.FullName;
5             var nowspace = fullName.Substring(0, fullName.LastIndexOf('.') + 1);
6 
7             return Assembly.GetExecutingAssembly().CreateInstance(nowspace + cacheType.ToString(), true) as BaseCache;
8         }

*:通過傳遞列舉引數,來確定反射CreateInstance()方法需要用到的typeName引數,從而來定義需要訪問的那個快取物件,這裡要注意的是加上了一個名稱空間nowspace,因為快取類可能和工廠類不是同一個名稱空間,但是通常會和快取基類是同名稱空間所以在方法最開始的時候擷取獲取了快取類需要的名稱空間(這裡看自身專案來定吧);

*:Assembly.GetExecutingAssembly()這個是用來獲取當前應用程式集的路徑,這裡就避免了咋們使用Assembly.Load()方法還需要傳遞程式集的路徑地址了

好了滿上上面要求後,咋們可以在測試頁面呼叫程式碼如:CacheRepository.Current(CacheType.RedisCache).SetCache<MoFlightSearchResponse>(keyData, value);就如此簡單,咋們使用redis-cli.exe客戶端來看下快取起來的資料:

怎麼樣,您們的是什麼效果呢,下面給出整體程式碼(最後更新時間:2016-09-29):

  1 #region CacheRepository 快取工廠(預設儲存Session中)
  2 
  3     /// <summary>
  4     /// 快取列舉
  5     /// </summary>
  6     public enum CacheType
  7     {
  8         BaseCache,
  9 
 10         RedisCache,
 11 
 12         MemcachedCache
 13     }
 14 
 15     /// <summary>
 16     /// 快取工廠(預設儲存Session中)
 17     /// </summary>
 18     public class CacheRepository
 19     {
 20 
 21         /// <summary>
 22         /// 快取工廠(預設儲存Session中, CacheKey = "SeesionKey")
 23         /// </summary>
 24         /// <param name="cacheType">快取型別</param>
 25         /// <returns></returns>
 26         public static BaseCache Current(CacheType cacheType = CacheType.RedisCache)
 27         {
 28             var nspace = typeof(BaseCache);
 29             var fullName = nspace.FullName;
 30             var nowspace = fullName.Substring(0, fullName.LastIndexOf('.') + 1);
 31 
 32             return Assembly.GetExecutingAssembly().CreateInstance(nowspace + cacheType.ToString(), true) as BaseCache;
 33         }
 34     }
 35 
 36     /// <summary>
 37     /// 快取基類(預設儲存Session中)
 38     /// </summary>
 39     public class BaseCache : IDisposable
 40     {
 41         protected string def_ip = string.Empty;
 42         protected int def_port = 0;
 43         protected string def_password = string.Empty;
 44         protected string CacheKey = "SeesionKey";
 45 
 46         public BaseCache()
 47         {
 48 
 49         }
 50 
 51         /// <summary>
 52         /// 獲取自定義SessionId值
 53         /// </summary>
 54         /// <param name="key">key:使用唯一的登陸賬號</param>
 55         /// <returns>hash值的SessionId</returns>
 56         public virtual string GetSessionId(string key)
 57         {
 58             return Md5Extend.GetSidMd5Hash(key);
 59         }
 60 
 61         public virtual void InitCache(bool isReadAndWriter = true, string ip = "", int port = 0, string password = "")
 62         {
 63 
 64         }
 65 
 66         public virtual bool SetCache<T>(string key, T t, int timeOutMinute = 10, bool isSerilize = false) where T : class,new()
 67         {
 68             var isfalse = false;
 69 
 70             try
 71             {
 72                 key = key ?? CacheKey;
 73                 if (t == null) { return isfalse; }
 74 
 75                 var session_json = JsonConvert.SerializeObject(t);
 76                 HttpContext.Current.Session.Timeout = timeOutMinute;
 77                 HttpContext.Current.Session.Add(key, session_json);
 78                 isfalse = true;
 79             }
 80             catch (Exception ex)
 81             {
 82 
 83                 throw new Exception(ex.Message);
 84             }
 85             return isfalse;
 86         }
 87 
 88         public virtual T GetCache<T>(string key = null, bool isSerilize = false) where T : class,new()
 89         {
 90             var t = default(T);
 91 
 92             try
 93             {
 94 
 95                 key = key ?? CacheKey;
 96                 var session = HttpContext.Current.Session[key];
 97                 if (session == null) { return t; }
 98 
 99                 t = JsonConvert.DeserializeObject<T>(session.ToString());
100             }
101             catch (Exception ex)
102             {
103 
104                 throw new Exception(ex.Message);
105             }
106             return t;
107         }
108 
109         public virtual bool Remove(string key = null)
110         {
111             var isfalse = false;
112 
113             try
114             {
115                 key = key ?? CacheKey;
116                 HttpContext.Current.Session.Remove(key);
117                 isfalse = true;
118             }
119             catch (Exception ex)
120             {
121 
122                 throw new Exception(ex.Message);
123             }
124             return isfalse;
125         }
126 
127         /// <summary>
128         /// 增加快取時間
129         /// </summary>
130         /// <returns></returns>
131         public virtual bool AddExpire(string key, int nTimeMinute = 10)
132         {
133             return true;
134         }
135 
136         public virtual bool FlushAll()
137         {
138 
139             return false;
140         }
141 
142         public virtual bool Any(string key)
143         {
144 
145             return false;
146         }
147 
148         public virtual bool SetHashCache<T>(string hashId, string key, T t, int nTimeMinute = 10) where T : class,new()
149         {
150 
151             return false;
152         }
153 
154         public virtual List<string> GetHashKeys(string hashId)
155         {
156 
157             return null;
158         }
159 
160         public virtual List<string> GetHashValues(string hashId)
161         {
162 
163             return null;
164         }
165 
166         public virtual T GetHashValue<T>(string hashId, string key) where T : class,new()
167         {
168             var t = default(T);
169             return t;
170         }
171 
172         public virtual bool RemoveHashByKey(string hashId, string key)
173         {
174 
175             return false;
176         }
177 
178 
179         public virtual void Dispose(bool isfalse)
180         {
181 
182             if (isfalse)
183             {
184 
185 
186             }
187         }
188 
189         //手動釋放
190         public void Dispose()
191         {
192 
193             this.Dispose(true);
194             //不自動釋放
195             GC.SuppressFinalize(this);
196         }
197     }
198 
199     /// <summary>
200     /// Redis快取
201     /// </summary>
202     public class RedisCache : BaseCache
203     {
204         public IRedisClient redis = null;
205 
206         public RedisCache()
207         {
208 
209             //這裡去讀取預設配置檔案資料
210             def_ip = "127.0.0.1";
211             def_port = 6379;
212             def_password = "";
213         }
214 
215         #region Redis快取
216 
217         public static object _lockCache = new object();
218         public override void InitCache(bool isReadAndWriter = true, string ip = "", int port = 0, string password = "")
219         {
220 
221             if (redis == null)
222             {
223                 ip = string.IsNullOrEmpty(ip) ? def_ip : ip;
224                 port = port == 0 ? def_port : port;
225                 password = string.IsNullOrEmpty(password) ? def_password : password;
226 
227                 //單個redis服務
228                 //redis = new RedisClient(ip, port, password);
229 
230                 //叢集服務 如果密碼,格式如:pwd@ip:port
231                 var readAndWritePorts = new List<string> { "shenniubuxing3@127.0.0.1:6379" };
232                 var onlyReadPorts = new List<string> {
233                     "shenniubuxing3@127.0.0.1:6378",
234                     "shenniubuxing3@127.0.0.1:6377"
235                 };
236 
237                 var redisPool = new PooledRedisClientManager(
238                     readAndWritePorts,
239                     onlyReadPorts,
240                     new RedisClientManagerConfig
241                     {
242                         AutoStart = true,
243                         //最大讀取連結
244                         MaxReadPoolSize = 20,
245                         //最大寫入連結
246                         MaxWritePoolSize = 10
247                     })
248                 {
249                     //每個連結超時時間
250                     ConnectTimeout = 20,
251                     //連線池超時時間
252                     PoolTimeout = 60
253                 };
254 
255                 lock (_lockCache)
256                 {
257                     redis = isReadAndWriter ? redisPool.GetClient() : redisPool.GetReadOnlyClient();
258                 }
259             }
260         }
261 
262         public override bool AddExpire(string key, int nTimeMinute = 10)
263         {
264             var isfalse = false;
265             try
266             {
267                 if (string.IsNullOrEmpty(key)) { return isfalse; }
268 
269                 InitCache();
270                 //isfalse = redis.ExpireEntryIn(key, TimeSpan.FromMinutes(nTimeMinute));
271                 isfalse = redis.ExpireEntryAt(key, DateTime.Now.AddMinutes(nTimeMinute));
272             }
273             catch (Exception ex)
274             {
275             }
276             finally { this.Dispose(); }
277             return isfalse;
278         }
279 
280         public override bool SetCache<T>(string key, T t, int timeOutMinute = 10, bool isSerilize = false)
281         {
282 
283             var isfalse = false;
284 
285             try
286             {
287                 if (string.IsNullOrEmpty(key)) { return isfalse; }
288 
289                 InitCache();
290                 if (isSerilize)
291                 {
292                     var data = JsonConvert.SerializeObject(t);
293                     var bb = System.Text.Encoding.UTF8.GetBytes(data);
294                     isfalse = redis.Set<byte[]>(key, bb, TimeSpan.FromMinutes(timeOutMinute));
295                 }
296                 else { isfalse = redis.Set<T>(key, t, TimeSpan.FromMinutes(timeOutMinute)); }
297             }
298             catch (Exception ex)
299             {
300             }
301             finally { this.Dispose(); }
302             return isfalse;
303         }
304 
305 
306         public override T GetCache<T>(string key, bool isSerilize = false)
307         {
308             var t = default(T);
309             try
310             {
311                 if (string.IsNullOrEmpty(key)) { return t; }
312 
313                 InitCache(false);
314                 if (isSerilize)
315                 {
316 
317                     var bb = redis.Get<byte[]>(key);
318                     if (bb.Length <= 0) { return t; }
319                     var data = System.Text.Encoding.UTF8.GetString(bb);
320                     t = JsonConvert.DeserializeObject<T>(data);
321                 }
322                 else { t = redis.Get<T>(key); }
323             }
324             catch (Exception ex)
325             {
326             }
327             finally { this.Dispose(); }
328             return t;
329         }
330 
331         public override bool Remove(string key)
332         {
333             var isfalse = false;
334             try
335             {
336                 if (string.IsNullOrEmpty(key)) { return isfalse; }
337 
338                 InitCache();
339                 isfalse = redis.Remove(key);
340             }
341             catch (Exception ex)
342             {
343             }
344             finally { this.Dispose(); }
345             return isfalse;
346         }
347 
348         public override bool SetHashCache<T>(string hashId, string key, T t, int nTimeMinute = 10)
349         {
350 
351             var isfalse = false;
352 
353             try
354             {
355                 if (string.IsNullOrEmpty(hashId) || string.IsNullOrEmpty(key) || t == null) { return isfalse; }
356 
357                 InitCache();
358 
359                 var result = JsonConvert.SerializeObject(t);
360                 if (string.IsNullOrEmpty(result)) { return isfalse; }
361                 isfalse = redis.SetEntryInHash(hashId, key, result);
362                 if (isfalse) { AddExpire(key, nTimeMinute); }
363             }
364             catch (Exception ex)
365             {
366             }
367             finally { this.Dispose(); }
368             return isfalse;
369         }
370 
371         public override List<string> GetHashKeys(string hashId)
372         {
373             var hashKeys = new List<string>();
374             try
375             {
376                 if (string.IsNullOrEmpty(hashId)) { return hashKeys; }
377 
378                 InitCache();
379                 hashKeys = redis.GetHashKeys(hashId);
380 
381             }
382             catch (Exception ex)
383             {
384             }
385             finally { this.Dispose(); }
386             return hashKeys;
387         }
388 
389         public override List<string> GetHashValues(string hashId)
390         {
391             var hashValues = new List<string>();
392             try
393             {
394                 if (string.IsNullOrEmpty(hashId)) { return hashValues; }
395 
396                 InitCache();
397                 hashValues = redis.GetHashValues(hashId);
398             }
399             catch (Exception ex)
400             {
401             }
402             finally { this.Dispose(); }
403             return hashValues;
404         }
405 
406         public override T GetHashValue<T>(string hashId, string key)
407         {
408             var t = default(T);
409             try
410             {
411                 if (string.IsNullOrEmpty(hashId) || string.IsNullOrEmpty(key)) { return t; }
412 
413                 InitCache();
414                 var result = redis.GetValueFromHash(hashId, key);
415                 if (string.IsNullOrEmpty(result)) { return t; }
416 
417                 t = JsonConvert.DeserializeObject<T>(result);
418             }
419             catch (Exception ex)
420             {
421             }
422             finally { this.Dispose(); }
423             return t;
424         }
425 
426         public override bool RemoveHashByKey(string hashId, string key)
427         {
428             var isfalse = false;
429 
430             try
431             {
432                 if (string.IsNullOrEmpty(hashId) || string.IsNullOrEmpty(key)) { return isfalse; }
433 
434                 InitCache();
435                 isfalse = redis.RemoveEntryFromHash(hashId, key);
436             }
437             catch (Exception ex)
438             {
439             }
440             finally { this.Dispose(); }
441             return isfalse;
442         }
443 
444         public override void Dispose(bool isfalse)
445         {
446 
447             if (isfalse && redis != null)
448             {
449 
450                 redis.Dispose();
451                 redis = null;
452             }
453         }
454 
455         #endregion
456     }
457 
458     /// <summary>
459     /// Memcached快取
460     /// </summary>
461     public class MemcachedCache : BaseCache
462     {
463 
464 
465     }
466 
467     #endregion
View Code

這次分享的Redis快取從搭建到使用希望給您們有幫助,還請多多支援點贊,謝謝。

.2016-08-30號修改最新版本程式碼

相關文章