• nopCommerce 数据缓存


    为了提高一个系统或网站的性能和IO吞吐量,我们一般都会采用缓存技术。当然NopCommerce也不例外,本文我们就来给大家分析一下nop中Cache缓存相关类设计、核心源码及实现原理。

    一、Nop.Core.Caching.ICacheManager

    Nop首先抽象出了一个缓存存储和读取相关管理接口Nop.Core.Caching.ICacheManager。

    1. namespace Nop.Core.Caching
      {
      /// <summary>
      /// Cache manager interface
      /// </summary>
      public interface ICacheManager
      {
      /// <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>
      T Get<T>(string 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>
      void Set(string key, object data, int cacheTime);
      /// <summary>
      /// Gets a value indicating whether the value associated with the specified key is cached
      /// </summary>
      /// <param name="key">key</param>
      /// <returns>Result</returns>
      bool IsSet(string key);
      /// <summary>
      /// Removes the value with the specified key from the cache
      /// </summary>
      /// <param name="key">/key</param>
      void Remove(string key);
      /// <summary>
      /// Removes items by pattern
      /// </summary>
      /// <param name="pattern">pattern</param>
      void RemoveByPattern(string pattern);
      /// <summary>
      /// Clear all cache data
      /// </summary>
      void Clear();
      }
      }
      二、Nop.Core.Caching.MemoryCacheManager
      
      接口ICacheManager具体实现是在类Nop.Core.Caching.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
      {
      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);
      }
      }
      }

    可以看到上面Nop的缓存数据是使用的的MemoryCache.Default来存储的,MemoryCache.Default是获取对默认 System.Runtime.Caching.MemoryCache 实例的引用,缓存的默认实例,也就是程序运行的内存中。

    Nop除了提供了一个MemoryCacheManager,还有一个Nop.Core.Caching.PerRequestCacheManager类,它提供的是MemoryCacheManager相同的功能,不过它是把数据存在HttpContextBase.Items中,如下:

    1. using System;
      using System.Collections;
      using System.Collections.Generic;
      using System.Text.RegularExpressions;
      using System.Web;
      namespace Nop.Core.Caching
      {
      /// <summary>
      /// Represents a manager for caching during an HTTP request (short term caching)
      /// </summary>
      public partial class PerRequestCacheManager : ICacheManager
      {
      private readonly HttpContextBase _context;
      /// <summary>
      /// Ctor
      /// </summary>
      /// <param name="context">Context</param>
      public PerRequestCacheManager(HttpContextBase context)
      {
      this._context = context;
      }
      /// <summary>
      /// Creates a new instance of the NopRequestCache class
      /// </summary>
      protected virtual IDictionary GetItems()
      {
      if (_context != null)
      return _context.Items;
      return null;
      }
      //省略其它代码....
      }
      }

    三、缓存接口ICacheManager依赖注入

    缓存接口ICacheManager使用了依赖注入,我们在Nop.Web.Framework.DependencyRegistrar类中就能找到对ICacheManager的注册代码:

    1. //cache manager
      builder.RegisterType<MemoryCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_static").SingleInstance();
      builder.RegisterType<PerRequestCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_per_request").InstancePerLifetimeScope();
      //pass MemoryCacheManager as cacheManager (cache settings between requests)
      builder.RegisterType<ProductTagService>().As<IProductTagService>()
      .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
      .InstancePerLifetimeScope();
      //pass MemoryCacheManager as cacheManager (cache settings between requests)
      builder.RegisterType<PermissionService>().As<IPermissionService>()
      .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
      .InstancePerLifetimeScope();
      //pass MemoryCacheManager as cacheManager (cache settings between requests)
      builder.RegisterType<AclService>().As<IAclService>()
      .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
      .InstancePerLifetimeScope();
      //pass MemoryCacheManager as cacheManager (cache settings between requests)
      builder.RegisterType<PriceCalculationService>().As<IPriceCalculationService>()
      .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
      .InstancePerLifetimeScope();
      //pass MemoryCacheManager as cacheManager (cache settings between requests)
      builder.RegisterType<CustomerActivityService>().As<ICustomerActivityService>()
      .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
      .InstancePerLifetimeScope();

    上面最开始对接口ICacheManager两实现分别是MemoryCacheManager和PerRequestCacheManager并通过.Named来区分。Autofac高级特性--注册Named命名和Key Service服务

    接下来可以配置不同的Service依赖不同的ICacheManager的实现:.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))或者.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_per_request"))。

    四、具体实例BlogController

    下面我们来举例看一下怎么使用这个缓存的。我们就以Nop.Web.Controllers.BlogController的方法BlogTags为例:

    1. [ChildActionOnly]
      public ActionResult BlogTags()
      {
      if (!_blogSettings.Enabled)
      return Content("");
      var cacheKey = string.Format(ModelCacheEventConsumer.BLOG_TAGS_MODEL_KEY, _workContext.WorkingLanguage.Id, _storeContext.CurrentStore.Id);
      var cachedModel = _cacheManager.Get(cacheKey, () =>
      {
      var model = new BlogPostTagListModel();
      //get tags
      var tags = _blogService.GetAllBlogPostTags(_storeContext.CurrentStore.Id, _workContext.WorkingLanguage.Id)
      .OrderByDescending(x => x.BlogPostCount)
      .Take(_blogSettings.NumberOfTags)
      .ToList();
      //sorting
      tags = tags.OrderBy(x => x.Name).ToList();
      foreach (var tag in tags)
      model.Tags.Add(new BlogPostTagModel()
      {
      Name = tag.Name,
      BlogPostCount = tag.BlogPostCount
      });
      return model;
      });
      return PartialView(cachedModel);
      }

    上面var cachedModel = _cacheManager.Get就是从缓存中读取数据,_cacheManager的Get方法第二个参数是一个lambda表达式,可以传一个方法,这时我们就可以把数据的从数据库中的逻辑放在里面,注意:当第二次请求数据时,如果缓存中有数据,这个Lambda方法是不会执行的。为什么呢?我们可以选中_cacheManager的Get方法按F12进去看这个方法的实现就知道了:

    1. 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, 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)
      {
      if (cacheManager.IsSet(key))
      {
      return cacheManager.Get<T>(key);
      }
      else
      {
      var result = acquire();
      if (cacheTime > 0)
      cacheManager.Set(key, result, cacheTime);
      return result;
      }
      }
      }
      }

    可以看到其实上面_cacheManager.Get调用的是类型ICacheManager的一个扩展方法。第二个方法就可以知道,当缓存中有数据直接返回cacheManager.Get<T>(key),如果没有才进入else分支,执行参数的Lambda表达方式acquire()。

    博客园的这篇文章写的不错:http://www.cnblogs.com/gusixing/archive/2012/04/12/2443799.html

  • 相关阅读:
    HTML
    python io
    python 线程进程
    python socket
    python 面向对象2
    python 面向对象
    python hashlib模块
    python configparser模块
    python logging模块
    数组去重方法汇总
  • 原文地址:https://www.cnblogs.com/xchit/p/5085740.html
Copyright © 2020-2023  润新知