NopCommerce 源码研究(五)缓存技术
2017-03-28 18:24
525 查看
缓存的调用和添加:
上面调用了ICacheManager的Get方法去取数据,ICacheManager的Get方法在CacheExtensions中,缓存不存在时会自动添加:
ICacheManager的实际实现是MemoryCacheManager,顺便看一下它的代码:
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);
}
}
}
可以看到:
1、使用的是System.Runtime.Caching.MemoryCache。关于.net的缓存,可以看这里:http://www.cnblogs.com/hjf1223/archive/2010/07/16/net_4_caching.html
2、移除缓存时根据正则匹配了key,并移除了全部缓存。
那么,内容变化时,如何刷新缓存呢?从services中开始跟一下代码:
可以看到,删除某一条的时候,调用了_eventPublisher.EntityDeleted(blogPost)方法,我们看一下EntityDeleted方法:
实际上调用了EventPublisher的Publish方法。
publish方法中调用了SubscriptionService.GetSubscriptions<T>()找到了 订阅者,然后调用PublishToConsumer方法,后者调用了消费者自己的HandleEvent方法:
事件发布者和订阅者是在下面被注册的:
消费者(这里只摘抄了blog相关的):
可以看到,调用了MemoryCacheManager的RemoveByPattern方法。至此,MemoryCacheManager的跟踪结束。
做一下总结:
Nop.Core.Caching.MemoryCacheManager是通过.net 4.0数据缓存框架实现的,负责管理相关数据缓存。RemoveByPattern方法会根据正则表达式找出相关key移除其所有缓存,在数据变动时会被调用;
Nop.Core.Caching.CacheExtensions实现了一些扩展方法,比如Get方法,如果不存在时将会通过传入的参数Func<T> acquire获取相应的数据并存入缓存。此方法在获取时可以调用,比如:Nop.Web.Controllers.BlogController.BlogMonths方法中就进行了调用;
Nop.Services.Events.EventPublisher是缓存事件通知处理类,Publish方法会调用消费者(IConsumer<T>)的HandleEvent方法,来执行对应的数据变化相关操作;
Nop.Services.Events.EventPublisherExtensions实现了增删改的扩展方法,Services层在数据有变化时,将通过调用起对应的增删改方法发送通知,调用消费者的HandleEvent方法做相应的处理;
Nop.Services.Events.SubscriptionService是用来获取订阅者的。
还有一个缓存:PerRequestCacheManager,是基于HttpContextBase实现的。
[ChildActionOnly] public ActionResult BlogMonths() { if (!_blogSettings.Enabled) return Content(""); var cacheKey = string.Format(ModelCacheEventConsumer.BLOG_MONTHS_MODEL_KEY, _workContext.WorkingLanguage.Id, _storeContext.CurrentStore.Id); var cachedModel = _cacheManager.Get(cacheKey, () => { var model = new List<BlogPostYearModel>(); var blogPosts = _blogService.GetAllBlogPosts(_storeContext.CurrentStore.Id, _workContext.WorkingLanguage.Id); if (blogPosts.Count > 0) { var months = new SortedDictionary<DateTime, int>(); var first = blogPosts[blogPosts.Count - 1].CreatedOnUtc; while (DateTime.SpecifyKind(first, DateTimeKind.Utc) <= DateTime.UtcNow.AddMonths(1)) { var list = blogPosts.GetPostsByDate(new DateTime(first.Year, first.Month, 1), new DateTime(first.Year, first.Month, 1).AddMonths(1).AddSeconds(-1)); if (list.Count > 0) { var date = new DateTime(first.Year, first.Month, 1); months.Add(date, list.Count); } first = first.AddMonths(1); } int current = 0; foreach (var kvp in months) { var date = kvp.Key; var blogPostCount = kvp.Value; if (current == 0) current = date.Year; if (date.Year > current || model.Count == 0) { 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; }); return PartialView(cachedModel); }
上面调用了ICacheManager的Get方法去取数据,ICacheManager的Get方法在CacheExtensions中,缓存不存在时会自动添加:
namespace Nop.Core.Caching { /// <summary> /// Extensions /// </summary> public static class CacheExtensions { /// <summary> /// Variable (lock) to support thread-safe /// </summary> private static readonly object _syncObject = new object(); /// <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, 60, 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) { lock (_syncObject) { if (cacheManager.IsSet(key)) { return cacheManager.Get<T>(key); } var result = acquire(); if (cacheTime > 0) cacheManager.Set(key, result, cacheTime); return result; } } } }
ICacheManager的实际实现是MemoryCacheManager,顺便看一下它的代码:
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);
}
}
}
可以看到:
1、使用的是System.Runtime.Caching.MemoryCache。关于.net的缓存,可以看这里:http://www.cnblogs.com/hjf1223/archive/2010/07/16/net_4_caching.html
2、移除缓存时根据正则匹配了key,并移除了全部缓存。
那么,内容变化时,如何刷新缓存呢?从services中开始跟一下代码:
namespace Nop.Services.Blogs { /// <summary> /// Blog service /// </summary> public partial class BlogService : IBlogService { /// <summary> /// Deletes a blog post /// </summary> /// <param name="blogPost">Blog post</param> public virtual void DeleteBlogPost(BlogPost blogPost) { if (blogPost == null) throw new ArgumentNullException("blogPost"); _blogPostRepository.Delete(blogPost); //event notification _eventPublisher.EntityDeleted(blogPost); } } }
可以看到,删除某一条的时候,调用了_eventPublisher.EntityDeleted(blogPost)方法,我们看一下EntityDeleted方法:
namespace Nop.Services.Events { public static class EventPublisherExtensions { public static void EntityInserted<T>(this IEventPublisher eventPublisher, T entity) where T : BaseEntity { eventPublisher.Publish(new EntityInserted<T>(entity)); dd7a } public static void EntityUpdated<T>(this IEventPublisher eventPublisher, T entity) where T : BaseEntity { eventPublisher.Publish(new EntityUpdated<T>(entity)); } public static void EntityDeleted<T>(this IEventPublisher eventPublisher, T entity) where T : BaseEntity { eventPublisher.Publish(new EntityDeleted<T>(entity)); } } }
实际上调用了EventPublisher的Publish方法。
publish方法中调用了SubscriptionService.GetSubscriptions<T>()找到了 订阅者,然后调用PublishToConsumer方法,后者调用了消费者自己的HandleEvent方法:
namespace Nop.Services.Events { /// /// Evnt publisher /// public interface IEventPublisher { /// /// Publish event /// /// Type /// Event message void Publish(T eventMessage); } } 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)); } } }其中调用订阅者服务来获取相关的订阅者然后发布给对应的消费者,来看一下SubscriptionService:
namespace Nop.Services.Events { /// <summary> /// Event subscription service /// </summary> public class SubscriptionService : ISubscriptionService { /// <summary> /// Get subscriptions /// </summary> /// <typeparam name="T">Type</typeparam> /// <returns>Event consumers</returns> public IList<IConsumer<T>> GetSubscriptions<T>() { return EngineContext.Current.ResolveAll<IConsumer<T>>(); } } }
事件发布者和订阅者是在下面被注册的:
namespace Nop.Web.Framework { public class DependencyRegistrar : IDependencyRegistrar { ... //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(); } }
消费者(这里只摘抄了blog相关的):
namespace Nop.Web.Infrastructure.Cache { /// <summary> /// Model cache event consumer (used for caching of presentation layer models) /// </summary> public partial class ModelCacheEventConsumer: IConsumer<EntityInserted>, IConsumer<EntityUpdated>, IConsumer<EntityDeleted>, { /// /// Key for blog tag list model /// /// /// {0} : language ID /// {1} : current store ID /// public const string BLOG_TAGS_MODEL_KEY = "Nop.pres.blog.tags-{0}-{1}"; /// /// Key for blog archive (years, months) block model /// /// /// {0} : language ID /// {1} : current store ID /// 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("nop_cache_static"); } //Blog posts public void HandleEvent(EntityInserted eventMessage) { _cacheManager.RemoveByPattern(BLOG_PATTERN_KEY); } public void HandleEvent(EntityUpdated eventMessage) { _cacheManager.RemoveByPattern(BLOG_PATTERN_KEY); } public void HandleEvent(EntityDeleted eventMessage) { _cacheManager.RemoveByPattern(BLOG_PATTERN_KEY); } }
可以看到,调用了MemoryCacheManager的RemoveByPattern方法。至此,MemoryCacheManager的跟踪结束。
做一下总结:
Nop.Core.Caching.MemoryCacheManager是通过.net 4.0数据缓存框架实现的,负责管理相关数据缓存。RemoveByPattern方法会根据正则表达式找出相关key移除其所有缓存,在数据变动时会被调用;
Nop.Core.Caching.CacheExtensions实现了一些扩展方法,比如Get方法,如果不存在时将会通过传入的参数Func<T> acquire获取相应的数据并存入缓存。此方法在获取时可以调用,比如:Nop.Web.Controllers.BlogController.BlogMonths方法中就进行了调用;
Nop.Services.Events.EventPublisher是缓存事件通知处理类,Publish方法会调用消费者(IConsumer<T>)的HandleEvent方法,来执行对应的数据变化相关操作;
Nop.Services.Events.EventPublisherExtensions实现了增删改的扩展方法,Services层在数据有变化时,将通过调用起对应的增删改方法发送通知,调用消费者的HandleEvent方法做相应的处理;
Nop.Services.Events.SubscriptionService是用来获取订阅者的。
还有一个缓存:PerRequestCacheManager,是基于HttpContextBase实现的。
相关文章推荐
- Varnish purges 缓存清除技术研究
- 一起谈.NET技术,.NET Framework源码研究系列之---Delegate
- 分布式缓存技术redis学习系列(八)——JedisCluster源码解读:集群初始化、slot(槽)的分配、值的存取
- NopCommerce 源码研究(四)数据库相关
- Liferay研究之廿五:缓存技术的使用
- Mybatis源码研究7:缓存的设计和实现
- OpenJDK源码研究笔记(五)-缓存Integer等类型的频繁使用的数据和对象,大幅度提升性能(一道经典的Java笔试题)
- [置顶] 缓存技术研究01---浏览器缓存技术
- Varnish purges 缓存清除技术研究[原创]
- Mybatis源码研究7:缓存的设计和实现
- Mybatis源码研究7:缓存的设计和实现
- Varnish purges 缓存清除技术研究[转]
- 一起谈.NET技术,.NET Framework源码研究系列之---万法归宗Object
- Mybatis源码研究7:缓存的设计和实现
- NopCommerce 源码研究(二)
- NopCommerce 源码研究(三)
- MyBatis源码研究-缓存
- OpenJDK源码研究笔记(五)-缓存Integer等类型的频繁使用的数据和对象,大幅度提升性能(一道经典的Java笔试题)
- ANT技术研究--与VSS整合自动获取源码进行编译
- 缓存技术研究01---浏览器缓存技术