nopCommerce架构分析系列(二)数据Cache

原文(http://www.cnblogs.com/gusixing/archive/2012/04/12/2443799.html)非常感谢作者顾思行的分享!

序言

在很多访问量较大的系统中,尤其在某一项数据访问频次较高时,我们会考虑使用缓存,减少系统和数据库的交互,以达到良好的用户体验。缓存主要有页面缓存和数据缓存。数据缓存的实现有很多方式,有基于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 部分缓存框架相关的类

2、基于HttpContextBase页面请求级数据缓存

HttpContextBase 类为抽象类,该类包含的成员与 HttpContext 类相同。 使用 HttpContextBase 类可以创建一些派生类,这些派生类与

HttpContext 类相似,但是可以进行自定义并在 ASP.NET 管道外部使用。 在执行单元测试时,通常使用派生类实现具有自定义行为的成员以实现正在测试的方案,这更容易进行单元测试。HttpContextWrapper 类是从 HttpContextBase 类派生的。 HttpContextWrapper 类用作 HttpContext 类的包装。 在运行时,通常使用 HttpContextWrapper 类的实例调用 HttpContext 对象上的成员。

HttpContext的Items集合是IDictionary键/值对的对象集合,在HttpRequest的生存期中共享。存储成本很高的调用的结果,防止该调用在页面上出现多次。一个HttpRequest中的各个单元需要处理相同或类似的数据。如果数据的生存期只是一个请求,就可以考虑使用HttpContext. Items作为短期的高速缓存。

nopCommerce项目中的缓存

1、缓存的实现

nopCommerce项目缓存类层级图

ICacheManager接口,该接口定义了数据缓存常用的方法。

 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进行扩展。

 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  缓存框架实现数据缓存

 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类,实现页面请求级的数据缓存。

  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类,空的数据缓存类。

 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的所有缓存,以避免读取到脏数据。

 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的依赖注入会在以后为大家介绍:)

 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

时间: 2024-10-12 20:50:29

nopCommerce架构分析系列(二)数据Cache的相关文章

NopCommerce架构分析(转载)

原文 一,NopCommerce架构分析之开篇 NopCommerce是.net开源项目中比较成熟的一款业务应用框架,也是电子商务系统中的典范.所以很想多学习一下里面的设计和实现方式. 二,NopCommerce架构分析之参考资料 参考:DependencyResolver http://www.cnblogs.com/RobbinHan/archive/2011/11/30/2269537.html 依赖注入框架Autofac的简单使用 http://www.cnblogs.com/lipin

Cordova Android源码分析系列二(CordovaWebView相关类分析)

本篇文章是Cordova Android源码分析系列文章的第二篇,主要分析CordovaWebView和CordovaWebViewClient类,通过分析代码可以知道Web网页加载的过程,错误出来,多线程处理等. CordovaWebView类分析 CordovaWebView类继承了Android WebView类,这是一个很自然的实现,共1000多行代码.包含了PluginManager pluginManager,BroadcastReceiver receiver,CordovaInt

分享JAVA从初级程序员到架构师视频,文档,架构设计,大型网站架构分析,大数据分析资料

JAVA从初级程序员到架构师视频,文档,架构设计,大型网站架构分析,大数据分析资料, 搭建高并发.高可用电商架构设计资料需要的联系我.很多目录都没列出来(QQ空间相册里有很多目录的截图)加QQ:1927360914

NopCommerce架构分析之三---数据库初试化及数据操作

系统启动时执行任务:IStartupTask,启动时执行的任务主要是数据库的初始化和加载. IStartupTask调用IEfDataProvider进行数据库的初始化. IEfDataProvider,SqlCeDataProvider:获取数据连接工厂,不同类型数据库,连接工厂不同. 接口IStartupTask的实体类EfStartUpTask的实现如下: [csharp] view plain copy public class EfStartUpTask : IStartupTask

QQ2010协议分析系列(二) - 登录之第一篇(0x0091)

QQ2010协议分析第一篇 测试QQ:597789809 昵称:浪子无情 尝试QQ服务器IP:112.95.240.125 client IP:222.35.174.5(我的外网IP) 密码:这个算了吧 Send: 看到下面的数字很迷茫吧,不用着急,我慢慢解释 下面的文本是HEX字符串,是常用网络16进制文本方式. QQ常用消息包结构: 包头:02 //表示包的开头 1F 57 //QQ版本代码,这里表示QQ2010sp3版本 00 91 //包命令 58 16      //这个不是固定的,表

NopCommerce架构分析之五------Model绑定Action参数

asp.net MVC中Action参数不只是一些基本类型,也支持实体参数.那么从客户端传来的数据如何映射或转换成实体对象呢?就是通过实体绑定类ModelBinder.此系列类在请求转化为后台Controller的Action方法前,捕获传递过来的数据,并对其进行解析和转换,最终为实体类对象. 在系统启动前,Global.asax.cs中的方法Application_Start方法调用下面代码定义参数转换规则. [csharp] view plain copy //model binders M

NopCommerce架构分析之一----依赖类生成容器

NopCommerce为了实现松耦合的框架设计目的,使用了IOC框架:Autofac.据有人测试,Autofac是性能好的IOC工具. 1.在IOC中,组件首先需要在IOC中注册,有通过配置文件注册的,像Spring.net,也有通过特性注册的,像StructureMap,也有通过代理来注册的,像Autofac.但是IOC讲究一个原则,就是接口和实现分离.所有IOC就是生命某个具体类实现了某个接口.然后在使用时,系统从IOC中获取接口的实现类,并创建对象. 2.下面来看NopCommerce如何

NopCommerce架构分析之八------多语言

系统支持的语言是有类:Language表示: 多语言资源对应的类为:LocalizedProperty: 当先选择某种语言存储在类中:GenericAttribute: 多语言可以导出为XML文件,当然也支持导出. IWorkContext及其实体类WebWorkContext为当前运行上下文:用户的登录信息以及一些上下文环境设置都保存在此类中. 具体包括:当前用户信息:CurrentCustomer:当前用户Cookie:货币:语言:税的类型:供应商等: 展现多语言资源的方式有几种: 一.在自

NopCommerce架构分析之六------自定义RazorViewEngine

系统中对Razor的支持包括两部分,其中之一就是自定义RazorViewEngine 一.自定义RazorViewEngine 在Global.asax.cs的Application_Start方法中,注册了自定义视图引擎: [csharp] view plain copy //remove all view engines ViewEngines.Engines.Clear(); //except the themeable razor view engine we use ViewEngin