nopCommerce架構分析系列(二)數據Cache


序言

在很多訪問量較大的系統中,尤其在某一項數據訪問頻次較高時,我們會考慮使用緩存,減少系統和數據庫的交互,以達到良好的用戶體驗。緩存主要有頁面緩存和數據緩存。數據緩存的實現有很多方式,有基於memcached的,還有基於.net 4.0數據緩存框架,還有一些其他的實現方式。院子里有 PetterLiumemcached快遞上手之C#,有興趣的可以查看,本文主要討論的是基於.net 4.0 數據緩存框架.

數據緩存的實現原理

nopCommerce項目中有兩類的數據緩存,一個是全局數據緩存MemoryCacheManager,是用.net 4.0數據緩存框架實現的。另一個是頁面請求級的數據緩存PerRequestCacheManager是基於HttpContextBase實現的。

1、數據緩存框架是.net 4.0框架中新增的功能,詳細了解.net 4.0  的緩存功能請看阿不寫的全面認識一下.NET 4.0的緩存功能

圖1 部分緩存框架相關的類

clip_image002

2、基於HttpContextBase頁面請求級數據緩存

HttpContextBase 類為抽象類,該類包含的成員與 HttpContext 類相同。 使用 HttpContextBase 類可以創建一些派生類,這些派生類與

HttpContext 類相似,但是可以進行自定義並在 ASP.NET 管道外部使用。 在執行單元測試時,通常使用派生類實現具有自定義行為的成員以實現正在測試的方案,這更容易進行單元測試。HttpContextWrapper 類是從 HttpContextBase 類派生的。 HttpContextWrapper 類用作 HttpContext 類的包裝。 在運行時,通常使用 HttpContextWrapper 類的實例調用 HttpContext 對象上的成員。

HttpContext的Items集合是IDictionary鍵/值對的對象集合,在HttpRequest的生存期中共享。存儲成本很高的調用的結果,防止該調用在頁面上出現多次。一個HttpRequest中的各個單元需要處理相同或類似的數據。如果數據的生存期只是一個請求,就可以考慮使用HttpContext. Items作為短期的高速緩存。

nopCommerce項目中的緩存

1、緩存的實現

nopCommerce項目緩存類層級圖

clip_image004

ICacheManager接口,該接口定義了數據緩存常用的方法。 

View Code
 1   public  interface ICacheManager
 2     {
 3          ///   <summary>
 4           ///  Gets or sets the value associated with the specified key.
 5           ///   </summary>
 6           ///   <typeparam name="T"> Type </typeparam>
 7           ///   <param name="key"> The key of the value to get. </param>
 8           ///   <returns> The value associated with the specified key. </returns>
 9          T Get<T>( string key);
10 
11          ///   <summary>
12           ///  Adds the specified key and object to the cache.
13           ///   </summary>
14           ///   <param name="key"> key </param>
15           ///   <param name="data"> Data </param>
16           ///   <param name="cacheTime"> Cache time </param>
17           void Set( string key,  object data,  int cacheTime);
18 
19          ///   <summary>
20           ///  Gets a value indicating whether the value associated with the specified key is cached
21           ///   </summary>
22           ///   <param name="key"> key </param>
23           ///   <returns> Result </returns>
24           bool IsSet( string key);
25 
26          ///   <summary>
27           ///  Removes the value with the specified key from the cache
28           ///   </summary>
29           ///   <param name="key"> /key </param>
30           void Remove( string key);
31 
32          ///   <summary>
33           ///  Removes items by pattern
34           ///   </summary>
35           ///   <param name="pattern"> pattern </param>
36           void RemoveByPattern( string pattern);
37 
38          ///   <summary>
39           ///  Clear all cache data
40           ///   </summary>
41           void Clear();
42     }

 

CacheExtensions擴展方法對ICacheManager進行擴展。

 

View Code
 1  ///   <summary>
 2       ///  Extensions
 3       ///   </summary>
 4       public  static  class CacheExtensions
 5     {
 6          public  static T Get<T>( this ICacheManager cacheManager,  string key, Func<T> acquire)
 7         {
 8              return Get(cacheManager, key,  60, acquire);
 9         }
10 
11          public  static T Get<T>( this ICacheManager cacheManager,  string key,  int cacheTime, Func<T> acquire) 
12         {
13              if (cacheManager.IsSet(key))
14             {
15                  return cacheManager.Get<T>(key);
16             }
17              else
18             {
19                  var result = acquire();
20                  // if (result != null)
21                      cacheManager.Set(key, result, cacheTime);
22                  return result;
23             }
24         }
25     }

 

MemoryCacheCache類,使用.net  緩存框架實現數據緩存

 

View Code
 1  ///   <summary>
 2       ///  Represents a MemoryCacheCache
 3       ///   </summary>
 4       public  partial  class MemoryCacheManager : ICacheManager
 5     {
 6          protected ObjectCache Cache
 7         {
 8              get
 9             {
10                  return MemoryCache.Default;
11             }
12         }
13         
14          ///   <summary>
15           ///  Gets or sets the value associated with the specified key.
16           ///   </summary>
17           ///   <typeparam name="T"> Type </typeparam>
18           ///   <param name="key"> The key of the value to get. </param>
19           ///   <returns> The value associated with the specified key. </returns>
20           public T Get<T>( string key)
21         {
22              return (T)Cache[key];
23         }
24 
25          ///   <summary>
26           ///  Adds the specified key and object to the cache.
27           ///   </summary>
28           ///   <param name="key"> key </param>
29           ///   <param name="data"> Data </param>
30           ///   <param name="cacheTime"> Cache time </param>
31           public  void Set( string key,  object data,  int cacheTime)
32         {
33              if (data ==  null)
34                  return;
35 
36              var policy =  new CacheItemPolicy();
37             policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime);
38             Cache.Add( new CacheItem(key, data), policy);
39         }
40 
41          ///   <summary>
42           ///  Gets a value indicating whether the value associated with the specified key is cached
43           ///   </summary>
44           ///   <param name="key"> key </param>
45           ///   <returns> Result </returns>
46           public  bool IsSet( string key)
47         {
48              return (Cache.Contains(key));
49         }
50 
51          ///   <summary>
52           ///  Removes the value with the specified key from the cache
53           ///   </summary>
54           ///   <param name="key"> /key </param>
55           public  void Remove( string key)
56         {
57             Cache.Remove(key);
58         }
59 
60          ///   <summary>
61           ///  Removes items by pattern
62           ///   </summary>
63           ///   <param name="pattern"> pattern </param>
64           public  void RemoveByPattern( string pattern)
65         {
66              var regex =  new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
67              var keysToRemove =  new List<String>();
68 
69              foreach ( var item  in Cache)
70                  if (regex.IsMatch(item.Key))
71                     keysToRemove.Add(item.Key);
72 
73              foreach ( string key  in keysToRemove)
74             {
75                 Remove(key);
76             }
77         }
78 
79          ///   <summary>
80           ///  Clear all cache data
81           ///   </summary>
82           public  void Clear()
83         {
84              foreach ( var item  in Cache)
85                 Remove(item.Key);
86         }
87     }

 

PerRequestCacheManager類,實現頁面請求級的數據緩存。

 

View Code
  1   ///   <summary>
  2       ///  Represents a NopStaticCache
  3       ///   </summary>
  4       public  partial  class PerRequestCacheManager : ICacheManager
  5     {
  6          private  readonly HttpContextBase _context;
  7 
  8          ///   <summary>
  9           ///  Ctor
 10           ///   </summary>
 11           ///   <param name="context"> Context </param>
 12           public PerRequestCacheManager(HttpContextBase context)
 13         {
 14              this._context = context;
 15         }
 16         
 17          ///   <summary>
 18           ///  Creates a new instance of the NopRequestCache class
 19           ///   </summary>
 20           protected IDictionary GetItems()
 21         {
 22              if (_context !=  null)
 23                  return _context.Items;
 24 
 25              return  null;
 26         }
 27 
 28          ///   <summary>
 29           ///  Gets or sets the value associated with the specified key.
 30           ///   </summary>
 31           ///   <typeparam name="T"> Type </typeparam>
 32           ///   <param name="key"> The key of the value to get. </param>
 33           ///   <returns> The value associated with the specified key. </returns>
 34           public T Get<T>( string key)
 35         {
 36              var items = GetItems();
 37              if (items ==  null)
 38                  return  default(T);
 39 
 40              return (T)items[key];
 41         }
 42 
 43          ///   <summary>
 44           ///  Adds the specified key and object to the cache.
 45           ///   </summary>
 46           ///   <param name="key"> key </param>
 47           ///   <param name="data"> Data </param>
 48           ///   <param name="cacheTime"> Cache time </param>
 49           public  void Set( string key,  object data,  int cacheTime)
 50         {
 51              var items = GetItems();
 52              if (items ==  null)
 53                  return;
 54 
 55              if (data !=  null)
 56             {
 57                  if (items.Contains(key))
 58                     items[key] = data;
 59                  else
 60                     items.Add(key, data);
 61             }
 62         }
 63 
 64          ///   <summary>
 65           ///  Gets a value indicating whether the value associated with the specified key is cached
 66           ///   </summary>
 67           ///   <param name="key"> key </param>
 68           ///   <returns> Result </returns>
 69           public  bool IsSet( string key)
 70         {
 71              var items = GetItems();
 72              if (items ==  null)
 73                  return  false;
 74             
 75              return (items[key] !=  null);
 76         }
 77 
 78          ///   <summary>
 79           ///  Removes the value with the specified key from the cache
 80           ///   </summary>
 81           ///   <param name="key"> /key </param>
 82           public  void Remove( string key)
 83         {
 84              var items = GetItems();
 85              if (items ==  null)
 86                  return;
 87 
 88             items.Remove(key);
 89         }
 90 
 91          ///   <summary>
 92           ///  Removes items by pattern
 93           ///   </summary>
 94           ///   <param name="pattern"> pattern </param>
 95           public  void RemoveByPattern( string pattern)
 96         {
 97              var items = GetItems();
 98              if (items ==  null)
 99                  return;
100 
101              var enumerator = items.GetEnumerator();
102              var regex =  new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
103              var keysToRemove =  new List<String>();
104              while (enumerator.MoveNext())
105             {
106                  if (regex.IsMatch(enumerator.Key.ToString()))
107                 {
108                     keysToRemove.Add(enumerator.Key.ToString());
109                 }
110             }
111 
112              foreach ( string key  in keysToRemove)
113             {
114                 items.Remove(key);
115             }
116         }
117 
118          ///   <summary>
119           ///  Clear all cache data
120           ///   </summary>
121           public  void Clear()
122         {
123              var items = GetItems();
124              if (items ==  null)
125                  return;
126 
127              var enumerator = items.GetEnumerator();
128              var keysToRemove =  new List<String>();
129              while (enumerator.MoveNext())
130             {
131                 keysToRemove.Add(enumerator.Key.ToString());
132             }
133 
134              foreach ( string key  in keysToRemove)
135             {
136                 items.Remove(key);
137             }
138         }
139     }

 

NopNullCache類,空的數據緩存類。

 

View Code
 1  ///   <summary>
 2       ///  Represents a NopNullCache
 3       ///   </summary>
 4       public  partial  class NopNullCache : ICacheManager
 5     {
 6          ///   <summary>
 7           ///  Gets or sets the value associated with the specified key.
 8           ///   </summary>
 9           ///   <typeparam name="T"> Type </typeparam>
10           ///   <param name="key"> The key of the value to get. </param>
11           ///   <returns> The value associated with the specified key. </returns>
12           public T Get<T>( string key)
13         {
14              return  default(T);
15         }
16 
17          ///   <summary>
18           ///  Adds the specified key and object to the cache.
19           ///   </summary>
20           ///   <param name="key"> key </param>
21           ///   <param name="data"> Data </param>
22           ///   <param name="cacheTime"> Cache time </param>
23           public  void Set( string key,  object data,  int cacheTime)
24         {
25         }
26 
27          ///   <summary>
28           ///  Gets a value indicating whether the value associated with the specified key is cached
29           ///   </summary>
30           ///   <param name="key"> key </param>
31           ///   <returns> Result </returns>
32           public  bool IsSet( string key)
33         {
34              return  false;
35         }
36 
37          ///   <summary>
38           ///  Removes the value with the specified key from the cache
39           ///   </summary>
40           ///   <param name="key"> /key </param>
41           public  void Remove( string key)
42         {
43         }
44 
45          ///   <summary>
46           ///  Removes items by pattern
47           ///   </summary>
48           ///   <param name="pattern"> pattern </param>
49           public  void RemoveByPattern( string pattern)
50         {
51         }
52 
53          ///   <summary>
54           ///  Clear all cache data
55           ///   </summary>
56           public  void Clear()
57         {
58         }
59     }

 

2、緩存的應用

下面是BlogService類中的CRUD,從中我們可以了解到,數據緩存是如何處理的,在數據檢索時,直接從緩存取數據,其他方法均根據相關正則表達式移除BlogPost的所有緩存,以避免讀取到臟數據。

 

View Code
 1  ///   <summary>
 2           ///  Gets a blog post
 3           ///   </summary>
 4           ///   <param name="blogPostId"> Blog post identifier </param>
 5           ///   <returns> Blog post </returns>
 6           public  virtual BlogPost GetBlogPostById( int blogPostId)
 7         {
 8              if (blogPostId ==  0)
 9                  return  null;
10 
11              string key =  string.Format(BLOGPOST_BY_ID_KEY, blogPostId);
12              return _cacheManager.Get(key, () =>
13             {
14                  var pv = _blogPostRepository.GetById(blogPostId);
15                  return pv;
16             });
17         }
18 
19          ///   <summary>
20           ///  Deletes a blog post
21           ///   </summary>
22           ///   <param name="blogPost"> Blog post </param>
23           public  virtual  void DeleteBlogPost(BlogPost blogPost)
24         {
25              if (blogPost ==  null)
26                  throw  new ArgumentNullException( " blogPost ");
27 
28             _blogPostRepository.Delete(blogPost);
29 
30             _cacheManager.RemoveByPattern(BLOGPOST_PATTERN_KEY);
31 
32              // event notification
33              _eventPublisher.EntityDeleted(blogPost);
34         }
35 
36 
37          ///   <summary>
38           ///  Inserts an blog post
39           ///   </summary>
40           ///   <param name="blogPost"> Blog post </param>
41           public  virtual  void InsertBlogPost(BlogPost blogPost)
42         {
43              if (blogPost ==  null)
44                  throw  new ArgumentNullException( " blogPost ");
45 
46             _blogPostRepository.Insert(blogPost);
47 
48             _cacheManager.RemoveByPattern(BLOGPOST_PATTERN_KEY);
49 
50              // event notification
51              _eventPublisher.EntityInserted(blogPost);
52         }
53 
54          ///   <summary>
55           ///  Updates the blog post
56           ///   </summary>
57           ///   <param name="blogPost"> Blog post </param>
58           public  virtual  void UpdateBlogPost(BlogPost blogPost)
59         {
60              if (blogPost ==  null)
61                  throw  new ArgumentNullException( " blogPost ");
62 
63             _blogPostRepository.Update(blogPost);
64 
65             _cacheManager.RemoveByPattern(BLOGPOST_PATTERN_KEY);
66 
67              // event notification
68              _eventPublisher.EntityUpdated(blogPost);
69         }

 

下面是nopCommerce中該部分的依賴注入部分:ps:nopCommerce的依賴注入會在以后為大家介紹:)

 

View Code
 1  // HTTP context and other related stuff
 2              builder.Register(c => 
 3                  // register FakeHttpContext when HttpContext is not available
 4                  HttpContext.Current !=  null ?
 5                 ( new HttpContextWrapper(HttpContext.Current)  as HttpContextBase) :
 6                 ( new FakeHttpContext( " ~/ "as HttpContextBase))
 7                 .As<HttpContextBase>()
 8                 .InstancePerHttpRequest();
 9             builder.Register(c => c.Resolve<HttpContextBase>().Request)
10                 .As<HttpRequestBase>()
11                 .InstancePerHttpRequest();
12             builder.Register(c => c.Resolve<HttpContextBase>().Response)
13                 .As<HttpResponseBase>()
14                 .InstancePerHttpRequest();
15             builder.Register(c => c.Resolve<HttpContextBase>().Server)
16                 .As<HttpServerUtilityBase>()
17                 .InstancePerHttpRequest();
18             builder.Register(c => c.Resolve<HttpContextBase>().Session)
19                 .As<HttpSessionStateBase>()
20                 .InstancePerHttpRequest();
21         // cache manager
22              builder.RegisterType<MemoryCacheManager>().As<ICacheManager>().Named<ICacheManager>( " nop_cache_static ").SingleInstance();
23             builder.RegisterType<PerRequestCacheManager>().As<ICacheManager>().Named<ICacheManager>( " nop_cache_per_request ").InstancePerHttpRequest();

 

有何改進指出?

在緩存具體實現的時候,除了檢索方法,其他的CRUD方法,均刪除了所有同類的數據緩存,我們是不是可以這樣想,上面的BlogPost肯定是有主鍵的,我們可以根據主鍵對緩存里面數據進行相關的操作,而不是在增刪改的時候,移除所有的BlogPost緩存。

總結

在我們的系統中,根據需要去判斷是否需要去設置緩存,采用何種方式去實現緩存?nopCommerce項目中給我提供了很好的例子,在實際應用可以借鑒其實現方式,增強我們系統的用戶體驗。

相關資料:

1、為短時間狀態存儲應用HttpContext.Current.Items

2、全面認識一下.NET 4.0的緩存功能

3、HttpContext是干什么的

4、為什么是HttpContextBase而不是IHttpContext


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM