C#开发微信门户及选拔(48) – 在微信框架中整合CacheManager 缓存框架

 在大家的许多框架只怕项目选拔中,缓存在必然水平上能够增加程序的响应速度,以及减轻服务器的承上启下压力,由此在有个别地方大家都考虑引入缓存模块,那篇散文介绍使用开源缓存框架CacheManager来达成多少的缓存,在微信支付框架中,大家有一些常用的拍卖也供给使用到缓存,由此本随笔以微信框架为例介绍缓存的实际利用,实际上,在大家有的是框架中,如混合式开发框架、Web开发框架、Bootstrap开发框架中,那个模块都以通用的。

壹 、框架的缓存设计

在大家的微信支付框架中,缓存作为数据库和对外接口之间的3个分段,提供数据的缓存响应处理,如下结构所示是Web API层对缓存的架构设计。

图片 1

图片 2

在缓存的拍卖中,小编尊重于接纳CacheManager,那么些缓存框架是二个集大成者,关于CacheManager 的介绍,大家可以纪念下自个儿后边的小说《.NET缓存框架CacheManager在混合式开发框架中的应用(1)-CacheManager的牵线和应用》。

CacheManager是一个以C#言语开发的开源.Net缓存框架抽象层。它不是实际的缓存实现,但它帮助两种缓存提供者(如Redis、Memcached等)并提供见惯司空高级天性。
CacheManager
首要的指标使开发者更易于处理各个繁复的缓存场景,使用CacheManager能够兑现多层的缓存,让进度内缓存在分布式缓存此前,且仅需几行代码来处理。
CacheManager
不仅仅是三个接口去联合区别缓存提供者的编制程序模型,它使我们在1个体系里面改变缓存策略变得万分不难,同时也提供越来越多的特色:如缓存同步、并发更新、种类号、事件处理、品质总结等等,开发职员能够在急需的时候选用那个特征。

CacheManager的GitHub源码地址为:https://github.com/MichaCo/CacheManager,假如供给切实的德姆o及表达,能够访问其官网:http://cachemanager.michaco.net

 

二 、在微信框架中整合CacheManager 缓存框架

在应用CacheManager 缓存的时候,大家得以一贯运用有关对象开始展览拍卖,首先须求定义1个类来进展开端化缓存的设置,然后进行调用,调用的时候能够接纳IOC的方法创设对象,如下代码所示创立二个自定义的缓存管理类

    /// <summary>
    /// 基于CacheManager的接口处理
    /// </summary>
    public class CacheManager : ICacheManager
    {
        /// <summary>
        /// ICacheManager对象
        /// </summary>
        public ICacheManager<object> Manager { get; set; }

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public CacheManager()
        {
            // 初始化缓存管理器
            Manager = CacheFactory.Build("getStartedCache", settings =>
            {
                settings
                .WithSystemRuntimeCacheHandle("handleName")
                .And
                .WithRedisConfiguration("redis", config =>
                {
                    config.WithAllowAdmin()
                        .WithDatabase(0)
                        .WithEndpoint("localhost", 6379);
                })
                .WithMaxRetries(100)
                .WithRetryTimeout(50)
                .WithRedisBackplane("redis")
                .WithRedisCacheHandle("redis", true)
                ;
            });
        }
    }
}

下一场在Autofac的安顿文件中配备缓存的连锁音信,如下文件所示。

图片 3

只要一贯行使Autofac的结构类来处理,那么调用缓存处理的代码如下所示。

            //通过AutoFac工厂获取对应的接口实现
            var cache = AutoFactory.Instatnce.Container.Resolve<ICacheManager>();
            if (cache != null)
            {
                accountInfo = cache.Manager.Get(key) as AccountInfo;
                if (accountInfo == null)
                {
                    var value = BLLFactory<Account>.Instance.FindByID(accountId);
                    var item = new CacheItem<object>(key, value, ExpirationMode.Absolute, TimeSpan.FromMinutes(TimeOut_Minutes));
                    cache.Manager.Put(item);

                    accountInfo = cache.Manager.Get(key) as AccountInfo;
                }
            } 

 

设若为了使用方便,大家还足以对那几个辅助类实行特别的包装,以便对它举行联合的调用处理即可。

    /// <summary>
    /// 基于.NET CacheManager的缓存管理,文档参考:http://cachemanager.michaco.net/documentation
    /// </summary>
    public class CacheManagerHelper
    {
        /// <summary>
        /// 锁定处理变量
        /// </summary>
        private static readonly object locker = new object();

        /// <summary>
        /// 创建一个缓存的键值,并指定响应的时间范围,如果失效,则自动获取对应的值
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="key">对象的键</param>
        /// <param name="cachePopulate">获取缓存值的操作</param>
        /// <param name="expiration">失效的时间范围</param>
        /// <param name="mode">失效类型</param>
        /// <returns></returns>
        public static T GetCacheItem<T>(string key, Func<T> cachePopulate, TimeSpan expiration, 
            string region = "_", ExpirationMode mode = ExpirationMode.Sliding) where T :class 
        {
            CacheItem<object> outItem = null;
            //通过AutoFac工厂获取对应的接口实现
            var cache = AutoFactory.Instatnce.Container.Resolve<ICacheManager>();
            if (cache != null)
            {
                if (cache.Manager.Get(key, region) == null)
                {
                    lock (locker)
                    {
                        if (cache.Manager.Get(key, region) == null)
                        {
                            //Add、Put差异,Add只有在空值的情况下执行加入并返回true,Put总会替换并返回True
                            //如果按下面的方式加入,那么会留下历史丢弃的键值: cache.Manager.Put(key, value);

                            var value = cachePopulate();
                            var item = new CacheItem<object>(key, region, value, mode, expiration);
                            cache.Manager.Put(item);
                        }
                    }
                }

                return cache.Manager.Get(key, region) as T;
            }
            else
            {                
                throw new ArgumentNullException("AutoFac配置参数错误,请检查autofac.config是否存在ICacheManager的定义");
            }
        }
    }

可是由于官方已经提供了二个近乎上边的代码逻辑的TryGetOrAdd方法,这些法子的定义如下所示。

TryGetOrAdd(String, String, Func<String, String, TCacheValue>, out TCacheValue)

Tries to either retrieve an existing item or add the item to the cache
if it does not exist. The valueFactory will be
evaluated only if the item does not exist.

 

Declaration
bool TryGetOrAdd(string key, string region, Func<string, string, TCacheValue> valueFactory, out TCacheValue value)
Parameters
Type Name Description
String key

The cache key.

String region

The cache region.

Func<StringString, TCacheValue> valueFactory

The method which creates the value which should be added.

TCacheValue value

The cache value.

Returns
Type Description
Boolean

True if the operation succeeds, False in case there are too many retries or the valueFactory returns null.

大家根据那几个参数的定义,能够更进一步简化上面包车型客车扶助类代码。

                cache.Manager.TryGetOrAdd(key, region, (_key, _region) =>{ 
                    var value = cachePopulate();
                    var item = new CacheItem<object>(key, region, value, mode, expiration);
                    return item;
                }, out outItem);
                return outItem as T;

整个类的代码如下所示

    /// <summary>
    /// 基于.NET CacheManager的缓存管理,文档参考:http://cachemanager.michaco.net/documentation
    /// </summary>
    public class CacheManagerHelper
    {     
        /// <summary>
        /// 创建一个缓存的键值,并指定响应的时间范围,如果失效,则自动获取对应的值
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="key">对象的键</param>
        /// <param name="cachePopulate">获取缓存值的操作</param>
        /// <param name="expiration">失效的时间范围</param>
        /// <param name="mode">失效类型</param>
        /// <returns></returns>
        public static T GetCacheItem<T>(string key, Func<T> cachePopulate, TimeSpan expiration, 
            string region = "_", ExpirationMode mode = ExpirationMode.Sliding) where T :class 
        {
            CacheItem<object> outItem = null;
            //通过AutoFac工厂获取对应的接口实现
            var cache = AutoFactory.Instatnce.Container.Resolve<ICacheManager>();
            if (cache != null)
            {
                cache.Manager.TryGetOrAdd(key, region, (_key, _region) =>{ 
                    var value = cachePopulate();
                    var item = new CacheItem<object>(key, region, value, mode, expiration);
                    return item;
                }, out outItem);
                return outItem as T;
            }
            else
            {                
                throw new ArgumentNullException("AutoFac配置参数错误,请检查autofac.config是否存在ICacheManager的定义");
            }
        }
    }

如此代码就简化了不少,而且不用本身决定读取的线程锁了,上面代码是应用协理类实现缓存的丰裕及获得处理。

        /// <summary>
        /// 为避免频繁的对数据库检索,提高获取账号信息的速度
        /// 我们把账号信息根据ID缓存起来,方便快速使用,提高效率。
        /// </summary>
        public static AccountInfo GetAccountByID(string accountId)
        {
            AccountInfo accountInfo = null;

            #region 使用.NET CacheManager缓存
            //正常情况下access_token有效期为7200秒,这里使用缓存设置短于这个时间即可
            var key = "GetAccountByID_" + accountId;
            accountInfo = CacheManagerHelper.GetCacheItem<AccountInfo>(key, () =>
            {
                return BLLFactory<Account>.Instance.FindByID(accountId);
            }, TimeSpan.FromMinutes(TimeOut_Minutes));

            return accountInfo;
        }

因而如此的协助类封装,我们得以在须要缓存的函数里面,统一接纳帮忙类对数据开始展览缓存恐怕读取缓存的操作。

大家也能够平昔行使Autofac创设的缓存管理进行操作,如在小程序里面,大家对用户敏感数据的解密处理函数,如下所示。

        /// <summary>  
        /// 根据微信小程序平台提供的解密算法解密数据
        /// </summary>  
        [HttpGet]
        public SmallAppUserInfo Decrypt(string encryptedData, string iv, string thirdkey)
        {
            SmallAppUserInfo userInfo = null;

            //通过AutoFac工厂获取对应的接口实现
            var cache = AutoFactory.Instatnce.Container.Resolve<ICacheManager>();
            if (cache != null)
            {
                //从缓存里面,获取对应的SessionKey
                var sessionkey = cache.Manager.Get(thirdkey);
                if (sessionkey != null)
                {
                    //对用户身份加密数据进行解析,获取包含openid等属性的完整对象
                    IBasicApi api = new BasicApi();
                    userInfo = api.Decrypt(encryptedData, iv, sessionkey.ToString());
                }
            }
            return userInfo;
        }