Nop中的Cache浅析
Nop中定义了ICacheManger接口,它有几个实现,其中MemoryCacheManager是内存缓存的一个实现。
MemoryCacheManager:
- using System;
- using System.Collections.Generic;
- using System.Runtime.Caching;
- using System.Text.RegularExpressions;
- namespace Nop.Core.Caching
- {
- /// <summary>
- /// Represents a manager for caching between HTTP requests (long term caching)
- /// </summary>
- public partial class MemoryCacheManager : ICacheManager
- {
- /// <summary>
- /// Cache object
- /// </summary>
- protected ObjectCache Cache
- {
- get
- {
- return MemoryCache.Default;
- }
- }
- /// <summary>
- /// Gets or sets the value associated with the specified key.
- /// </summary>
- /// <typeparam name="T">Type</typeparam>
- /// <param name="key">The key of the value to get.</param>
- /// <returns>The value associated with the specified key.</returns>
- public virtual T Get<T>(string key)
- {
- return (T)Cache[key];
- }
- /// <summary>
- /// Adds the specified key and object to the cache.
- /// </summary>
- /// <param name="key">key</param>
- /// <param name="data">Data</param>
- /// <param name="cacheTime">Cache time</param>
- public virtual void Set(string key, object data, int cacheTime)
- {
- if (data == null)
- return;
- var policy = new CacheItemPolicy();
- policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime);
- Cache.Add(new CacheItem(key, data), policy);
- }
- /// <summary>
- /// Gets a value indicating whether the value associated with the specified key is cached
- /// </summary>
- /// <param name="key">key</param>
- /// <returns>Result</returns>
- public virtual bool IsSet(string key)
- {
- return (Cache.Contains(key));
- }
- /// <summary>
- /// Removes the value with the specified key from the cache
- /// </summary>
- /// <param name="key">/key</param>
- public virtual void Remove(string key)
- {
- Cache.Remove(key);
- }
- /// <summary>
- /// Removes items by pattern
- /// </summary>
- /// <param name="pattern">pattern</param>
- public virtual void RemoveByPattern(string pattern)
- {
- var regex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
- var keysToRemove = new List<String>();
- foreach (var item in Cache)
- if (regex.IsMatch(item.Key))
- keysToRemove.Add(item.Key);
- foreach (string key in keysToRemove)
- {
- Remove(key);
- }
- }
- /// <summary>
- /// Clear all cache data
- /// </summary>
- public virtual void Clear()
- {
- foreach (var item in Cache)
- Remove(item.Key);
- }
- /// <summary>
- /// Dispose
- /// </summary>
- public virtual void Dispose()
- {
- }
- }
- }
缓存的添加,在需要的地方构建cache key然后调用ICacheManger接口存储起来:
- var cachedModel = _cacheManager.Get(cacheKey, () =>
- {
- var model = new List<BlogPostYearModel>();
- var blogPosts = _blogService.GetAllBlogPosts(_storeContext.CurrentStore.Id,
- _workContext.WorkingLanguage.Id);
- if (blogPosts.Count > )
- {
- var months = new SortedDictionary<DateTime, int>();
- var first = blogPosts[blogPosts.Count - ].CreatedOnUtc;
- while (DateTime.SpecifyKind(first, DateTimeKind.Utc) <= DateTime.UtcNow.AddMonths())
- {
- var list = blogPosts.GetPostsByDate(new DateTime(first.Year, first.Month, ), new DateTime(first.Year, first.Month, ).AddMonths().AddSeconds(-));
- if (list.Count > )
- {
- var date = new DateTime(first.Year, first.Month, );
- months.Add(date, list.Count);
- }
- first = first.AddMonths();
- }
- int current = ;
- foreach (var kvp in months)
- {
- var date = kvp.Key;
- var blogPostCount = kvp.Value;
- if (current == )
- current = date.Year;
- if (date.Year > current || model.Count == )
- {
- var yearModel = new BlogPostYearModel
- {
- Year = date.Year
- };
- model.Add(yearModel);
- }
- model.Last().Months.Add(new BlogPostMonthModel
- {
- Month = date.Month,
- BlogPostCount = blogPostCount
- });
- current = date.Year;
- }
- }
- return model;
- });
这个ICacheManger的Get方法其实是个扩展方法,当获取不到缓存的时候调用Func<T>获取值,然后缓存起来:
- using System;
- namespace Nop.Core.Caching
- {
- /// <summary>
- /// Extensions
- /// </summary>
- public static class CacheExtensions
- {
- /// <summary>
- /// Get a cached item. If it's not in the cache yet, then load and cache it
- /// </summary>
- /// <typeparam name="T">Type</typeparam>
- /// <param name="cacheManager">Cache manager</param>
- /// <param name="key">Cache key</param>
- /// <param name="acquire">Function to load item if it's not in the cache yet</param>
- /// <returns>Cached item</returns>
- public static T Get<T>(this ICacheManager cacheManager, string key, Func<T> acquire)
- {
- return Get(cacheManager, key, , acquire);
- }
- /// <summary>
- /// Get a cached item. If it's not in the cache yet, then load and cache it
- /// </summary>
- /// <typeparam name="T">Type</typeparam>
- /// <param name="cacheManager">Cache manager</param>
- /// <param name="key">Cache key</param>
- /// <param name="cacheTime">Cache time in minutes (0 - do not cache)</param>
- /// <param name="acquire">Function to load item if it's not in the cache yet</param>
- /// <returns>Cached item</returns>
- public static T Get<T>(this ICacheManager cacheManager, string key, int cacheTime, Func<T> acquire)
- {
- if (cacheManager.IsSet(key))
- {
- return cacheManager.Get<T>(key);
- }
- var result = acquire();
- if (cacheTime > )
- cacheManager.Set(key, result, cacheTime);
- return result;
- }
- }
- }
Cache的移除。Nop缓存的移除比较有意思,它使用Pub/Sub模式来实现。
当你缓存一个Blog的列表,如果后面对某个Blog进行Update的时候,你就有两个选择:1.更新这个Blog的cache 2.移除所有关于Blog的cache。Nop选择的是后者,因为第一种方案实现起来的代价有点大,你可能需要给单独每个Blog指定一个Key来缓存起来,或者遍历所有关于Blog的cache。
当发生Blog的Update的时候,会发送一个通知事件:
- public virtual void UpdateBlogPost(BlogPost blogPost)
- {
- if (blogPost == null)
- throw new ArgumentNullException("blogPost");
- _blogPostRepository.Update(blogPost);
- //event notification
- _eventPublisher.EntityUpdated(blogPost);
- }
看一下EventPublish的实现 :
- public interface IEventPublisher
- {
- /// <summary>
- /// Publish event
- /// </summary>
- /// <typeparam name="T">Type</typeparam>
- /// <param name="eventMessage">Event message</param>
- void Publish<T>(T eventMessage);
- }
- using System;
- using System.Linq;
- using Nop.Core.Infrastructure;
- using Nop.Core.Plugins;
- using Nop.Services.Logging;
- namespace Nop.Services.Events
- {
- /// <summary>
- /// Evnt publisher
- /// </summary>
- public class EventPublisher : IEventPublisher
- {
- private readonly ISubscriptionService _subscriptionService;
- /// <summary>
- /// Ctor
- /// </summary>
- /// <param name="subscriptionService"></param>
- public EventPublisher(ISubscriptionService subscriptionService)
- {
- _subscriptionService = subscriptionService;
- }
- /// <summary>
- /// Publish to cunsumer
- /// </summary>
- /// <typeparam name="T">Type</typeparam>
- /// <param name="x">Event consumer</param>
- /// <param name="eventMessage">Event message</param>
- protected virtual void PublishToConsumer<T>(IConsumer<T> x, T eventMessage)
- {
- //Ignore not installed plugins
- var plugin = FindPlugin(x.GetType());
- if (plugin != null && !plugin.Installed)
- return;
- try
- {
- x.HandleEvent(eventMessage);
- }
- catch (Exception exc)
- {
- //log error
- var logger = EngineContext.Current.Resolve<ILogger>();
- //we put in to nested try-catch to prevent possible cyclic (if some error occurs)
- try
- {
- logger.Error(exc.Message, exc);
- }
- catch (Exception)
- {
- //do nothing
- }
- }
- }
- /// <summary>
- /// Find a plugin descriptor by some type which is located into its assembly
- /// </summary>
- /// <param name="providerType">Provider type</param>
- /// <returns>Plugin descriptor</returns>
- protected virtual PluginDescriptor FindPlugin(Type providerType)
- {
- if (providerType == null)
- throw new ArgumentNullException("providerType");
- if (PluginManager.ReferencedPlugins == null)
- return null;
- foreach (var plugin in PluginManager.ReferencedPlugins)
- {
- if (plugin.ReferencedAssembly == null)
- continue;
- if (plugin.ReferencedAssembly.FullName == providerType.Assembly.FullName)
- return plugin;
- }
- return null;
- }
- /// <summary>
- /// Publish event
- /// </summary>
- /// <typeparam name="T">Type</typeparam>
- /// <param name="eventMessage">Event message</param>
- public virtual void Publish<T>(T eventMessage)
- {
- var subscriptions = _subscriptionService.GetSubscriptions<T>();
- subscriptions.ToList().ForEach(x => PublishToConsumer(x, eventMessage));
- }
- }
- }
很简单,只是获取所有的订阅,然后依次调用其中的PublishToConsumer方法。
那么订阅是在哪里呢?
首先这是Blog消息消费者的定义:
- using Nop.Core.Caching;
- using Nop.Core.Domain.Blogs;
- using Nop.Core.Domain.Catalog;
- using Nop.Core.Domain.Configuration;
- using Nop.Core.Domain.Directory;
- using Nop.Core.Domain.Localization;
- using Nop.Core.Domain.Media;
- using Nop.Core.Domain.News;
- using Nop.Core.Domain.Orders;
- using Nop.Core.Domain.Polls;
- using Nop.Core.Domain.Topics;
- using Nop.Core.Domain.Vendors;
- using Nop.Core.Events;
- using Nop.Core.Infrastructure;
- using Nop.Services.Events;
- namespace Nop.Web.Infrastructure.Cache
- {
- /// <summary>
- /// Model cache event consumer (used for caching of presentation layer models)
- /// </summary>
- public partial class ModelCacheEventConsumer:
- //blog posts
- IConsumer<EntityInserted<BlogPost>>,
- IConsumer<EntityUpdated<BlogPost>>,
- IConsumer<EntityDeleted<BlogPost>>
- {
- /// <summary>
- /// Key for blog tag list model
- /// </summary>
- /// <remarks>
- /// {0} : language ID
- /// {1} : current store ID
- /// </remarks>
- public const string BLOG_TAGS_MODEL_KEY = "Nop.pres.blog.tags-{0}-{1}";
- /// <summary>
- /// Key for blog archive (years, months) block model
- /// </summary>
- /// <remarks>
- /// {0} : language ID
- /// {1} : current store ID
- /// </remarks>
- public const string BLOG_MONTHS_MODEL_KEY = "Nop.pres.blog.months-{0}-{1}";
- public const string BLOG_PATTERN_KEY = "Nop.pres.blog";
- private readonly ICacheManager _cacheManager;
- public ModelCacheEventConsumer()
- {
- //TODO inject static cache manager using constructor
- this._cacheManager = EngineContext.Current.ContainerManager.Resolve<ICacheManager>("nop_cache_static");
- }
- //Blog posts
- public void HandleEvent(EntityInserted<BlogPost> eventMessage)
- {
- _cacheManager.RemoveByPattern(BLOG_PATTERN_KEY);
- }
- public void HandleEvent(EntityUpdated<BlogPost> eventMessage)
- {
- _cacheManager.RemoveByPattern(BLOG_PATTERN_KEY);
- }
- public void HandleEvent(EntityDeleted<BlogPost> eventMessage)
- {
- _cacheManager.RemoveByPattern(BLOG_PATTERN_KEY);
- }
- }
- }
所有的Blog的key都采用统一的前缀,Nop.pres.blog。这样只要使用这个前缀就能清楚所有关于blog的缓存了。
这个类继承了3个接口所以有3个HandleEvent的实现,都是清楚blog相关的缓存。
这些消费者其实并未主动的去注册订阅,而是通过反射在启动的时候自动加载进IoC容器里的,当需要使用的时候通过接口直接取出来使用。
- //Register event consumers
- var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();
- foreach (var consumer in consumers)
- {
- builder.RegisterType(consumer)
- .As(consumer.FindInterfaces((type, criteria) =>
- {
- var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());
- return isMatch;
- }, typeof(IConsumer<>)))
- .InstancePerLifetimeScope();
- }
- builder.RegisterType<EventPublisher>().As<IEventPublisher>().SingleInstance();
- builder.RegisterType<SubscriptionService>().As<ISubscriptionService>().SingleInstance();
其中Pub/Sub是其中的精髓,非常值得学习。
Nop中的Cache浅析的更多相关文章
- JavaScript中闭包之浅析解读
JavaScript中的闭包真心是一个老生常谈的问题了,最近面试也是一直问到,我自己的表述能力又不能完全支撑起来,真是抓狂.在回来的路上,我突然想到了一个很简单的事情,其实我们在做项目时候,其实就经常 ...
- [转]学习Nop中Routes的使用
本文转自:http://www.cnblogs.com/miku/archive/2012/09/27/2706276.html 1. 映射路由 大型MVC项目为了扩展性,可维护性不能像一般项目在Gl ...
- cache 浅析
http://blog.chinaunix.net/uid-26817832-id-3244916.html 1. Cache Cache一词来源于法语,其原意是"藏匿处,隐秘的地方&q ...
- Linux内存中的Cache真的能被回收么?
在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个RHEL6的系统上,free命令的显示内容大概是这样一个状态: [root@tencent64 ~]# free ...
- 在Spring中使用cache(EhCache的对象缓存和页面缓存)
Spring框架从version3.1开始支持cache,并在version4.1版本中对cache功能进行了增强. spring cache 的关键原理就是 spring AOP,通过 spring ...
- 浅谈数据库系统中的cache
Cache和Buffer是两个不同的概念,简单的说,Cache是加速“读”,而buffer是缓冲“写”,前者解决读的问题,保存从磁盘上读出的数据,后者是解决写的问题,保存即将要写入到磁盘上的数据.在很 ...
- Linux内核中的Cache段
Linux内核中的Cache段 原文地址:http://blogold.chinaunix.net/u2/85263/showart_1743693.html 最近移植LEON3的内核时,了解了一些简 ...
- Linux 内存中的Cache,真的能被回收么?
您真的了解Linux的free命令么? 在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个RHEL6的系统上,free命令的显示内容大概是这样一个状态: 这里的默认显示单位是k ...
- Linux内核中内存cache的实现【转】
Linux内核中内存cache的实现 转自:http://blog.chinaunix.net/uid-127037-id-2919545.html 本文档的Copyleft归yfydz所有,使用 ...
随机推荐
- Java面试查漏补缺
一.基础 1.&和&&的区别. [概述] &&只能用作逻辑与(and)运算符(具有短路功能):但是&可以作为逻辑与运算符(是“无条件与”,即没有短路的功 ...
- poj1200-Crazy Search(hash入门经典)
Hash:一般是一个整数.就是说通过某种算法,可以把一个字符串"压缩" 成一个整数.一,题意: 给出两个数n,nc,并给出一个由nc种字符组成的字符串.求这个字符串中长度为n的不同 ...
- C#_基础:委托速讲
1定义:委托=函数指针 C# public delegate void Test(string str); 等价C++ public void (*Test)(string str): 委托赋值(初始 ...
- 《Linux内核设计与实现》读书笔记 第四章 进程调度
第四章进程调度 进程调度程序可看做在可运行太进程之间分配有限的处理器时间资源的内核子系统.调度程序是多任务操作系统的基础.通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发执行的 ...
- MongoDB 初见指南
技术若只如初见,那么还会踩坑么? 在系统引入 MongoDB 也有几年了,一开始是因为 MySQL 中有单表记录增长太快(每天几千万条吧)容易拖慢 MySQL 的主从复制.而这类数据增长迅速的流水表, ...
- Javascript基础回顾 之(一) 类型
本来是要继续由浅入深表达式系列最后一篇的,但是最近团队突然就忙起来了,从来没有过的忙!不过喜欢表达式的朋友请放心,已经在写了:) 在工作当中发现大家对Javascript的一些基本原理普遍存在这里或者 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章 ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...
- JS.中文乱码,Jsp\Servlet端的解决办法
JS.中文乱码,Jsp\Servlet端的解决办法 2010-03-08 15:18:21| 分类: Extjs | 标签:encodeuricomponent 乱码 urldecoder ...
- 集群下session共享问题的解决方案.
这一篇博客来讲解下babasport这个项目中使用的Login功能, 当然这里说的只是其中的一些简单的部分, 记录在此 方便以后查阅. 一: 去登录页面首先我们登录需要注意的事项是, 当用户点击登录按 ...
- Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构
Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构 1. 索引的分类1 1.1. 按照存储结构划分btree,hash,bitmap,fulltext1 1.2. 索引的类型 按查找 ...