C#開發微信門戶及應用(48) - 在微信框架中整合CacheManager 緩存框架

 在咱們的不少框架或者項目應用中,緩存在必定程度上能夠提升程序的響應速度,以及減輕服務器的承載壓力,所以在一些地方咱們都考慮引入緩存模塊,這篇隨筆介紹使用開源緩存框架CacheManager來實現數據的緩存,在微信開發框架中,咱們有一些經常使用的處理也須要應用到緩存,所以本隨筆以微信框架爲例介紹緩存的實際使用,實際上,在咱們不少框架中,如混合式開發框架、Web開發框架、Bootstrap開發框架中,這個模塊都是通用的。html

一、框架的緩存設計

在咱們的微信開發框架中,緩存做爲數據庫和對外接口之間的一個分層,提供數據的緩存響應處理,以下結構所示是Web API層對緩存的架構設計。git

在緩存的處理中,我側重於使用CacheManager,這個緩存框架是一個集大成者,關於CacheManager 的介紹,咱們能夠回顧下我以前的隨筆《.NET緩存框架CacheManager在混合式開發框架中的應用(1)-CacheManager的介紹和使用》。github

CacheManager是一個以C#語言開發的開源.Net緩存框架抽象層。它不是具體的緩存實現,但它支持多種緩存提供者(如Redis、Memcached等)並提供不少高級特性。
CacheManager 主要的目的使開發者更容易處理各類複雜的緩存場景,使用CacheManager能夠實現多層的緩存,讓進程內緩存在分佈式緩存以前,且僅需幾行代碼來處理。
CacheManager 不單單是一個接口去統一不一樣緩存提供者的編程模型,它使咱們在一個項目裏面改變緩存策略變得很是容易,同時也提供更多的特性:如緩存同步、併發更新、序列號、事件處理、性能計算等等,開發人員能夠在須要的時候選擇這些特性。redis

CacheManager的GitHub源碼地址爲:https://github.com/MichaCo/CacheManager,若是須要具體的Demo及說明,能夠訪問其官網:http://cachemanager.michaco.net算法

 

二、在微信框架中整合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的配置文件中配置緩存的相關信息,以下文件所示。編程

若是直接使用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方法,這個方法的定義以下所示。api

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;
        }
相關文章
相關標籤/搜索