C#开发微信门户及用(48) – 在微信框架中整理并CacheManager 缓存框架

 在我们的大队人马框架或项目用被,缓存在定水准达到得以加强程序的响应速度,以及减轻服务器的承载压力,因此于一些地方我们都考虑引入缓存模块,这首随笔介绍下开源缓存框架CacheManager来实现多少的缓存,在微信支付框架中,我们来一些常用的拍卖为欲动用至缓存,因此依随笔以微信框架为条例介绍缓存的实际上用,实际上,在咱们有的是框架中,如混合式开发框架、Web开发框架、Bootstrap开发框架中,这个模块都是通用的。

1、框架的缓存设计

以我们的微信支付框架中,缓存作为数据库及对外接口之间的一个分,提供数据的缓存响应处理,如下结构所示是Web API层对缓存的架构设计。

图片 1

图片 2

每当缓存的处理面临,我看重于用CacheManager,这个缓存框架是一个集大成者,关于CacheManager 的牵线,我们得回忆下我事先的随笔《.NET缓存框架CacheManager在混合式开发框架中之行使(1)-CacheManager的介绍与运用》。

CacheManager是一个因C#语言开发之开源.Net缓存框架抽象层。它不是现实性的缓存实现,但它们支持多缓存提供者(如Redis、Memcached等)并提供多高级特性。
CacheManager
主要的目的而开发者重新易于处理各种复杂的复苏存场景,使用CacheManager可以实现多叠的缓存,让过程内缓存在分布式缓存之前,且仅需要几尽代码来处理。
CacheManager
不仅仅是一个接口去联合不同缓存提供者的编程模型,它一旦我们在一个品种里面转缓存策略变得非常容易,同时为提供再多的表征:如缓存同步、并发更新、序列号、事件处理、性能计算等等,开发人员可以于用之时节择这些特征。

CacheManager的GitHub源码地址也:https://github.com/MichaCo/CacheManager,如果急需实际的Demo及说明,可以看该官网:http://cachemanager.michaco.net

 

2、在微信框架中理并CacheManager 缓存框架

每当应用CacheManager 缓存的时段,我们可以直接采用相关对象开展处理,首先需定义一个类来开展初始化缓存的设置,然后开展调用,调用的早晚可以动用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;
        }