程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 4.3 可配置的分布式緩存(上),4.3可配置緩存

4.3 可配置的分布式緩存(上),4.3可配置緩存

編輯:關於.NET

4.3 可配置的分布式緩存(上),4.3可配置緩存


為了加快系統運行效率,一般情況下系統會采用緩存技術,將常用信息存放到緩存中,避免頻繁的從數據庫、文件中讀寫,造成系統瓶頸,從而提高響應速度。緩存分為客戶端緩存和服務器端緩存。

目前隨著系統的擴展,服務器端緩存一般采取兩級緩存技術,本地緩存和分布式緩存。部分常用、公共或者小數據量的信息保存在分布式緩存中,運行在不同資源上的系統均從分布式緩存中獲取同樣的數據。相反,常用、私有或者數據量大的信息則保存在本地緩存中,避免了大數據量信息頻繁網絡傳輸、序列化和反序列化造成的系統瓶頸。

 

大家在使用分布式緩存中需要注意的是,應用在將數據存入分布式緩存或者讀取時,數據需要序列化才能存入,讀取時反序列化,這樣對於大的對象占用的網絡/CPU等資源會比較多。

通常情況下,.net core的緩存使用是這樣的:

 1     public void ConfigureServices(IServiceCollection services)
 2     {
 3         services.AddMemoryCache();
 4         // Add framework services.
 5         services.AddMvc();            
 6     }
 7 
 8     public class HomeController : Controller
 9     {
10         private IMemoryCache _memoryCache;
11         public HomeController(IMemoryCache memoryCache)
12         {
13             _memoryCache = memoryCache;
14         }
15 
16         public IActionResult Index()
17         {
18             string cacheKey = "key";
19             string result;
20             if (!_memoryCache.TryGetValue(cacheKey, out result))
21             {
22                 result = DateTime.Now.ToString();
23                 _memoryCache.Set(cacheKey, result);
24             }
25             ViewBag.Cache = result;
26             return View();
27         }
28     }

第一段是在startup.cs中注冊緩存,第二段是具體在某個controller中的使用。

對於分布式緩存來說,以Redis為例則需要寫成這樣:

 1     public void ConfigureServices(IServiceCollection services)
 2     {
 3         services.AddDistributedRedisCache(options =>
 4         {
 5         options.Configuration = "localhost";
 6         options.InstanceName = "SampleInstance";
 7         }); 
 8     }
 9 
10     public class HomeController : Controller
11     {
12         private IDistributedCache _memoryCache;
13         public HomeController(IDistributedCache memoryCache)
14         {
15             _memoryCache = memoryCache;
16         }
17 
18         public IActionResult Index()
19         {
20             string cacheKey = "key";
21             string result;
22             if (!_memoryCache.TryGetValue(cacheKey, out result))
23             {
24                 result = DateTime.Now.ToString();
25                 _memoryCache.Set(cacheKey, result);
26             }
27             ViewBag.Cache = result;
28             return View();
29         }
30     }

然而,這種分布式緩存編程方式在大型可擴展的系統中存在一些問題。一是在不同的環境中可能緩存產品不同,例如阿裡雲是memcached,微軟是appfabirc,我們本地集群的是redis等。不能每次部署修改一遍程序吧?這違反了可替換、可配置的軟件質量原則了。二是IDistributedCache接口方法用起來比較繁瑣,對於過期時間等還得自行編寫DistributedCacheEntryOptions。其實過期時間一般都是滑動時間,應該可以將這個時間直接寫在配置文件中,而不要每個緩存自己寫。

為此,對分布式緩存的封裝首先是建立更好的使用接口,其次是可配置化。接口和抽象類的代碼如下:

  1     public interface ICacheHander
  2     {
  3         /// <summary>
  4         /// 如果不存在緩存項則添加,否則更新
  5         /// </summary>
  6         /// <param name="catalog">緩存分類</param>
  7         /// <param name="key">緩存項標識</param>
  8         /// <param name="value">緩存項</param>
  9         void Put<T>(string catalog, string key, T value);
 10 
 11         /// <summary>
 12         /// 如果不存在緩存項則添加,否則更新
 13         /// </summary>
 14         /// <param name="catalog">緩存分類</param>
 15         /// <param name="key">緩存項標識</param>
 16         /// <param name="value">緩存項</param>
 17         /// <param name="timeSpan">緩存時間,滑動過期</param>
 18         void Put<T>(string catalog, string key, T value, TimeSpan timeSpan);
 19 
 20         /// <summary>
 21         /// 獲取緩存項
 22         /// </summary>
 23         /// <param name="catalog">緩存分類</param>
 24         /// <param name="key">cacheKey</param>
 25         T Get<T>(string catalog, string key);
 26 
 27         /// <summary>
 28         /// 獲取緩存項,如果不存在則使用方法獲取數據並加入緩存
 29         /// </summary>
 30         /// <typeparam name="T"></typeparam>
 31         /// <param name="catalog">緩存分類</param>
 32         /// <param name="key">緩存項標識</param>
 33         /// <param name="func">獲取要緩存的數據的方法</param>
 34         /// <returns>緩存的數據結果</returns>
 35         T GetOrAdd<T>(string catalog, string key, Func<T> func);
 36 
 37         /// <summary>
 38         /// 獲取緩存項,如果不存在則使用方法獲取數據並加入緩存
 39         /// </summary>
 40         /// <typeparam name="T"></typeparam>
 41         /// <param name="catalog">緩存分類</param>
 42         /// <param name="key">緩存項標識</param>
 43         /// <param name="func">獲取要緩存的數據的方法</param>
 44         /// <param name="timeSpan">緩存時間,滑動過期</param>
 45         /// <returns>緩存的數據結果</returns>
 46         T GetOrAdd<T>(string catalog, string key, Func<T> func, TimeSpan timeSpan);
 47 
 48         /// <summary>
 49         /// 移除緩存項
 50         /// </summary>
 51         /// <param name="catalog">緩存分類</param>
 52         /// <param name="key">cacheKey</param>
 53         void Remove(string catalog, string key);
 54 
 55         /// <summary>
 56         /// 刷新緩存項
 57         /// </summary>
 58         /// <param name="catalog">緩存分類</param>
 59         /// <param name="key">cacheKey</param>
 60         void Refresh(string catalog, string key);
 61     }
 62 
 63 
 64     public abstract class BaseCacheHandler : ICacheHander
 65     {
 66         protected abstract IDistributedCache _Cache { get; }
 67         protected CachingConfigInfo _ConfigInfo { get; private set; }
 68 
 69         private TimeSpan _DefaultTimeSpan;
 70 
 71         private ConcurrentDictionary<string, object> _LockObjsDic;
 72 
 73         public BaseCacheHandler(CachingConfigInfo configInfo)
 74         {
 75             this._DefaultTimeSpan = new TimeSpan(0, configInfo.DefaultSlidingTime, 0);
 76             this._LockObjsDic = new ConcurrentDictionary<string, object>();
 77 
 78             this._ConfigInfo = configInfo;
 79         }
 80 
 81         public void Put<T>(string catalog, string key, T value) => Put(catalog, key, value, _DefaultTimeSpan);
 82 
 83         public virtual void Put<T>(string catalog, string key, T value, TimeSpan timeSpan)
 84         {
 85             string cacheKey = GenCacheKey(catalog, key);
 86 
 87             string str = SerializerHelper.ToJson<T>(value);
 88 
 89             _Cache.SetString(cacheKey, str, new DistributedCacheEntryOptions().SetSlidingExpiration(timeSpan));
 90         }
 91 
 92         public T Get<T>(string catalog, string key)
 93         {
 94             MicroStrutLibraryExceptionHelper.TrueThrow(string.IsNullOrWhiteSpace(catalog) || string.IsNullOrWhiteSpace(key), this.GetType().FullName, LogLevel.Error, "緩存分類或者標識不能為空");
 95 
 96             string cacheKey = GenCacheKey(catalog, key);
 97 
 98             string str = _Cache.GetString(cacheKey);
 99 
100             return SerializerHelper.FromJson<T>(str);
101         }
102 
103         public T GetOrAdd<T>(string catalog, string key, Func<T> func) => GetOrAdd(catalog, key, func, _DefaultTimeSpan);
104 
105         public T GetOrAdd<T>(string catalog, string key, Func<T> func, TimeSpan timeSpan)
106         {
107             MicroStrutLibraryExceptionHelper.TrueThrow(string.IsNullOrWhiteSpace(catalog) || string.IsNullOrWhiteSpace(key), this.GetType().FullName, LogLevel.Error, "緩存分類或者標識不能為空");
108 
109             T result = Get<T>(catalog, key);
110 
111             if (result == null)
112             {
113                 string cacheKey = GenCacheKey(catalog, key);
114 
115                 object lockObj = _LockObjsDic.GetOrAdd(cacheKey, n => new object());
116                 lock (lockObj)
117                 {
118                     result = Get<T>(catalog, key);
119 
120                     if (result == null)
121                     {
122                         result = func();
123                         Put(catalog, key, result, timeSpan);
124                     }
125                 }
126             }
127 
128             if (result == null)
129                 return default(T);
130 
131             return result;
132         }
133 
134         /// <summary>
135         /// 刪除緩存
136         /// </summary>
137         /// <param name="catalog"></param>
138         /// <param name="key"></param>
139         public void Remove(string catalog, string key)
140         {
141             string cacheKey = GenCacheKey(catalog, key);
142 
143             _Cache.Remove(cacheKey);
144         }
145 
146         /// <summary>
147         /// 清空緩存
148         /// </summary>
149         public void Refresh(string catalog, string key)
150         {
151             string cacheKey = GenCacheKey(catalog, key);
152 
153             _Cache.Refresh(cacheKey);
154         }
155 
156         /// <summary>
157         /// 生成緩存鍵
158         /// </summary>
159         /// <param name="catalog"></param>
160         /// <param name="key"></param>
161         /// <returns></returns>
162         private string GenCacheKey(string catalog, string key)
163         {
164             return $"{catalog}-{key}";
165         }
166     }

抽象類又對接口進行了進一步的實現。大家可以注意下緩存Get時我們使用了兩次lock,還有GetOrAdd方法,我們用了一個func。

緩存配置信息的代碼,配置基類和配置的介紹請見:多級可換源的配置實現。

 

 1     public sealed class CachingConfigInfo : ConfigInfo
 2     {
 3         /// <summary>
 4         /// 緩存滑動窗口時間(分鐘)
 5         /// </summary>
 6         public int DefaultSlidingTime { get; set; }
 7 
 8         /// <summary>
 9         /// 分布式緩存類型TypeDescription,例如是Redis/Memcached/Default等分布式緩存對應的實現類信息。
10         /// </summary>
11         public string Type { get; set; }
12 
13         /// <summary>
14         /// 緩存的參數,一般是服務器信息
15         /// </summary>
16         public List<CacheServer> Servers { get; set; }
17 
18         public override string SectionName
19         {
20             get
21             {
22                 return "MicroStrutLibrary:Caching";
23             }
24         }
25 
26         public override void RegisterOptions(IServiceCollection services, IConfigurationRoot root)
27         {
28             services.Configure<CachingConfigInfo>(root.GetSection(SectionName));
29         }
30     }
31 
32     /// <summary>
33     /// 分布式緩存 服務器配置項
34     /// </summary>
35     public sealed class CacheServer
36     {
37         /// <summary>
38         /// 服務器地址,可以使服務器網絡名稱也可是IP地址
39         /// </summary>
40         public string HostName { get; set; }
41 
42         /// <summary>
43         /// 服務器分布式緩存服務提供端口
44         /// </summary>
45         public int Port { get; set; }
46     }

 

其中Type屬性就是具體的實現,例如Redis分布式緩存的實現等。具體請見下節。

接下來需要在startup注冊使用分布式緩存處理

 1         public static IServiceCollection AddCacheHandler(this IServiceCollection services)
 2         {
 3             if (services == null)
 4             {
 5                 throw new ArgumentNullException(nameof(services));
 6             }
 7 
 8             IOptions<CachingConfigInfo> optionsAccessor = services.BuildServiceProvider().GetService<IOptions<CachingConfigInfo>>();
 9 
10             ICacheHander handler = ReflectionHelper.CreateInstance(optionsAccessor.Value.Type, optionsAccessor.Value) as ICacheHander;
11 
12             services.TryAddSingleton<ICacheHander>(handler);
13 
14             return services;
15         }

 

在使用分布式緩存時,只需要在方法或者調用類的構造函數上增加ICacheHandler cacheHandler的屬性即可。例如ParameterService 類的構造函數public ParameterService(ICacheHandler cacheHandler) {…}。

 

面向雲的.net core開發框架目錄

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved