Asp.Net Mvc分布式缓存方案

首先,认识一下.Net的Cache组件

在web开发过程中,刚开始,我们可能会这么使用缓存:

//添加
HttpContext.Cache.Add(key,
                     value,
                     null,
                     DateTime.Now.AddMinutes(min),
                     System.Web.Caching.Cache.NoSlidingExpiration,
                     System.Web.Caching.CacheItemPriority.Normal, null);
//获取
object obj = HttpContext.Cache["key"]; 

有一回,我想缓存一些东西到Cache里,然后定期处理,于是在Global.asax中的Application_Start()方法中写了个Timer,去访问Cache

HttpContext.Cache["key"];//HttpContext空引用错误

HttpContext是http请求的上下文对象,在Timer这种定时处理的逻辑中,是不存在http上下文的,那要如何访问Cache? 问度娘,发现了HttpContext.Current.Cache&&HttpRuntime.Cache

原来HttpContext.Current.Cache,HttpRuntime.Cache是同一个对象。 那么,在使用Cache时我们可以直接使用HttpRuntime.Cache。 但是,HttpRuntime.Cache是存在于单台web服务器的内存中的本机缓存,不利于水平扩展,需要替代者。

进入正题,分布式缓存Memcache

这个替代者就是Memcache .Net使用Memcache

使用MemCache固然很好,但是一开始没有这个资源去额外配置一个MemCache,或者网站规模还没到玩集群的时候,怎么从代码上去方便的切换这个状态呢,不如封装下Cache操作。

using Enyim.Caching;
using Enyim.Caching.Memcached;
using System;
using System.Configuration;
using System.Web;
namespace wCacheService
{
    public class CacheHelper
    {
        /// <summary>
        /// CacheHelper的配置信息
        /// </summary>
        public static CacheHelperSection config = (CacheHelperSection)ConfigurationManager.GetSection("CacheHelperSection");
        /// <summary>
        /// 读取缓存
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static object Get(string key)
        {
            if (config.IsUseMemCache)
            {
                key = config.MemCacheKeyPre + key;
                MemcachedClient mc = Singleton_MemCacheClient.GetInstance();
                return mc.Get(key);
            }
            else
            {
                return HttpRunTimeCache_Get(key);
            }
        }
        /// <summary>
        /// 添加缓存,注意value不可以是IList类型
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="min"></param>
        public static void Add(string key, object value, int min)
        {
            if (!string.IsNullOrEmpty(key) && value != null && min > 0)
            {
                if (config.IsUseMemCache)
                {
                    key = config.MemCacheKeyPre + key;
                    MemcachedClient mc = Singleton_MemCacheClient.GetInstance();
                    mc.Store(StoreMode.Set, key, value, DateTime.Now.AddMinutes(min));
                }
                else
                {
                    HttpRunTimeCache_Add(key, value, min);
                }
            }
        }
        /// <summary>
        /// 清理缓存
        /// </summary>
        /// <param name="key"></param>
        public static void Remove(string key)
        {
            if (config.IsUseMemCache)
            {
                key = config.MemCacheKeyPre + key;
                MemcachedClient mc = Singleton_MemCacheClient.GetInstance();
                mc.Remove(key);
            }
            else
            {
                HttpRunTimeCache_Remove(key);
            }
        }
        #region asp.net原生缓存
        /// <summary>
        /// 原生的.Net缓存组件的操作
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static object HttpRunTimeCache_Get(string key)
        {
            return HttpRuntime.Cache[key];
        }
        /// <summary>
        /// 原生的.Net缓存组件的操作
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="min"></param>
        public static void HttpRunTimeCache_Add(string key, object value, int min)
        {
            HttpRuntime.Cache.Add(key,
                             value,
                             null,
                             DateTime.Now.AddMinutes(min),
                             System.Web.Caching.Cache.NoSlidingExpiration,
                             System.Web.Caching.CacheItemPriority.Normal, null);
        }
        /// <summary>
        /// 原生的.Net缓存组件的操作
        /// </summary>
        /// <param name="key"></param>
        public static void HttpRunTimeCache_Remove(string key)
        {
            HttpRuntime.Cache.Remove(key);
        }
        #endregion
    }
    /// <summary>
    /// 单件模式 memcache client
    /// </summary>
    public class Singleton_MemCacheClient
    {
        private static MemcachedClient _mc;
        private static object _lock = new object();
        private Singleton_MemCacheClient() { }
        public static MemcachedClient GetInstance()
        {
            if (_mc == null)
            {
                lock (_lock)
                {
                    _mc = new MemcachedClient();
                }
            }
            return _mc;
        }
    }
    /// <summary>
    /// CacheHelper配置类
    /// </summary>
    public class CacheHelperSection : ConfigurationSection
    {
        public CacheHelperSection() { }
        [ConfigurationProperty("MemCacheKeyPre", DefaultValue = "")]
        public string MemCacheKeyPre
        {
            get
            {
                return (string)this["MemCacheKeyPre"];
            }
            set
            {
                this["MemCacheKeyPre"] = value;
            }
        }
        [ConfigurationProperty("IsUseMemCache", DefaultValue = "false")]
        public bool IsUseMemCache
        {
            get
            {
                return (bool)this["IsUseMemCache"];
            }
            set
            {
                this["IsUseMemCache"] = value;
            }
        }
    }
}

配置方式:

 <configSections>
    <section name="CacheHelperSection" type="wCacheService.CacheHelper,wCacheService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  </configSections>
  <CacheHelperSection MemCacheKeyPre="" IsUseMemCache="false" />

如此,码代码的时候可以兼顾分布式缓存,日后配置了Memcache就设置IsUseMemCache为true即可。当然为了多一种选择,这里也开放了调用.net原生缓存的方法。

注意,使用memcache时,需要配置memcache相关参数,详见 一个.Net用Memcache Client库

Published: February 23 2014

prize