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

在我們的很多框架或者項(xiàng)目應(yīng)用中,緩存在一定程度上可以提高程序的響應(yīng)速度,以及減輕服務(wù)器的承載壓力,因此在一些地方我們都考慮引入緩存模塊,這篇隨筆介紹使用開源緩存框架CacheManager來實(shí)現(xiàn)數(shù)據(jù)的緩存,在微信開發(fā)框架中,我們有一些常用的處理也需要應(yīng)用到緩存,因此本隨筆以微信框架為例介紹緩存的實(shí)際使用,實(shí)際上,在我們很多框架中,如混合式開發(fā)框架、Web開發(fā)框架、Bootstrap開發(fā)框架中,這個(gè)模塊都是通用的。

1、框架的緩存設(shè)計(jì)

在我們的微信開發(fā)框架中,緩存作為數(shù)據(jù)庫和對(duì)外接口之間的一個(gè)分層,提供數(shù)據(jù)的緩存響應(yīng)處理,如下結(jié)構(gòu)所示是Web API層對(duì)緩存的架構(gòu)設(shè)計(jì)。



在緩存的處理中,我側(cè)重于使用CacheManager,這個(gè)緩存框架是一個(gè)集大成者,關(guān)于CacheManager 的介紹,我們可以回顧下我之前的隨筆《.NET緩存框架CacheManager在混合式開發(fā)框架中的應(yīng)用(1)-CacheManager的介紹和使用》。
CacheManager是一個(gè)以C#語言開發(fā)的開源.Net緩存框架抽象層。它不是具體的緩存實(shí)現(xiàn),但它支持多種緩存提供者(如Redis、Memcached等)并提供很多高級(jí)特性。CacheManager 主要的目的使開發(fā)者更容易處理各種復(fù)雜的緩存場(chǎng)景,使用CacheManager可以實(shí)現(xiàn)多層的緩存,讓進(jìn)程內(nèi)緩存在分布式緩存之前,且僅需幾行代碼來處理。CacheManager 不僅僅是一個(gè)接口去統(tǒng)一不同緩存提供者的編程模型,它使我們?cè)谝粋€(gè)項(xiàng)目里面改變緩存策略變得非常容易,同時(shí)也提供更多的特性:如緩存同步、并發(fā)更新、序列號(hào)、事件處理、性能計(jì)算等等,開發(fā)人員可以在需要的時(shí)候選擇這些特性。
CacheManager的GitHub源碼地址為:https://github.com/MichaCo/CacheManager,如果需要具體的Demo及說明,可以訪問其官網(wǎng):http://cachemanager.michaco.net

2、在微信框架中整合CacheManager 緩存框架

在使用CacheManager 緩存的時(shí)候,我們可以直接使用相關(guān)對(duì)象進(jìn)行處理,首先需要定義一個(gè)類來進(jìn)行初始化緩存的設(shè)置,然后進(jìn)行調(diào)用,調(diào)用的時(shí)候可以使用IOC的方式構(gòu)建對(duì)象,如下代碼所示創(chuàng)建一個(gè)自定義的緩存管理類

    /// <summary>
    /// 基于CacheManager的接口處理
    /// </summary>
    public class CacheManager : ICacheManager
    {
        /// <summary>
        /// ICacheManager對(duì)象
        /// </summary>
        public ICacheManager<object> Manager { get; set; }

        /// <summary>
        /// 默認(rèn)構(gòu)造函數(shù)
        /// </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的配置文件中配置緩存的相關(guān)信息,如下文件所示。



如果直接使用Autofac的構(gòu)造類來處理,那么調(diào)用緩存處理的代碼如下所示。

//通過AutoFac工廠獲取對(duì)應(yīng)的接口實(shí)現(xiàn)
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;
    }
} 

如果為了使用方便,我們還可以對(duì)這個(gè)輔助類進(jìn)行進(jìn)一步的封裝,以便對(duì)它進(jìn)行統(tǒng)一的調(diào)用處理即可。

    /// <summary>
    /// 基于.NET CacheManager的緩存管理,文檔參考:http://cachemanager.michaco.net/documentation
    /// </summary>
    public class CacheManagerHelper
    {
        /// <summary>
        /// 鎖定處理變量
        /// </summary>
        private static readonly object locker = new object();
     
        /// <summary>
        /// 創(chuàng)建一個(gè)緩存的鍵值,并指定響應(yīng)的時(shí)間范圍,如果失效,則自動(dòng)獲取對(duì)應(yīng)的值
        /// </summary>
        /// <typeparam name="T">對(duì)象類型</typeparam>
        /// <param name="key">對(duì)象的鍵</param>
        /// <param name="cachePopulate">獲取緩存值的操作</param>
        /// <param name="expiration">失效的時(shí)間范圍</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工廠獲取對(duì)應(yīng)的接口實(shí)現(xiàn)
            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只有在空值的情況下執(zhí)行加入并返回true,Put總會(huì)替換并返回True
                            //如果按下面的方式加入,那么會(huì)留下歷史丟棄的鍵值: 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配置參數(shù)錯(cuò)誤,請(qǐng)檢查autofac.config是否存在ICacheManager的定義");
            }
        }
    }

不過由于官方已經(jīng)提供了一個(gè)類似上面的代碼邏輯的TryGetOrAdd方法,這個(gè)方法的定義如下所示。
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<String, String, 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.

我們根據(jù)這個(gè)參數(shù)的定義,可以進(jìn)一步簡(jiǎn)化上面的輔助類代碼。

    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;

整個(gè)類的代碼如下所示

/// <summary>
/// 基于.NET CacheManager的緩存管理,文檔參考:http://cachemanager.michaco.net/documentation
/// </summary>
public class CacheManagerHelper
{     
    /// <summary>
    /// 創(chuàng)建一個(gè)緩存的鍵值,并指定響應(yīng)的時(shí)間范圍,如果失效,則自動(dòng)獲取對(duì)應(yīng)的值
    /// </summary>
    /// <typeparam name="T">對(duì)象類型</typeparam>
    /// <param name="key">對(duì)象的鍵</param>
    /// <param name="cachePopulate">獲取緩存值的操作</param>
    /// <param name="expiration">失效的時(shí)間范圍</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工廠獲取對(duì)應(yīng)的接口實(shí)現(xiàn)
        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配置參數(shù)錯(cuò)誤,請(qǐng)檢查autofac.config是否存在ICacheManager的定義");
        }
    }
}

這樣代碼就簡(jiǎn)化了不少,而且不用自己控制讀取的線程鎖了,下面代碼是使用輔助類實(shí)現(xiàn)緩存的添加及獲取處理。

        /// <summary>
        /// 為避免頻繁的對(duì)數(shù)據(jù)庫檢索,提高獲取賬號(hào)信息的速度
        /// 我們把賬號(hào)信息根據(jù)ID緩存起來,方便快速使用,提高效率。
        /// </summary>
        public static AccountInfo GetAccountByID(string accountId)
        {
            AccountInfo accountInfo = null;

            #region 使用.NET CacheManager緩存
            //正常情況下access_token有效期為7200秒,這里使用緩存設(shè)置短于這個(gè)時(shí)間即可
            var key = "GetAccountByID_" + accountId;
            accountInfo = CacheManagerHelper.GetCacheItem<AccountInfo>(key, () =>
            {
                return BLLFactory<Account>.Instance.FindByID(accountId);
            }, TimeSpan.FromMinutes(TimeOut_Minutes));

            return accountInfo;
        }

通過這樣的輔助類封裝,我們可以在需要緩存的函數(shù)里面,統(tǒng)一使用輔助類對(duì)數(shù)據(jù)進(jìn)行緩存或者讀取緩存的操作。

我們也可以直接使用Autofac構(gòu)建的緩存管理進(jìn)行操作,如在小程序里面,我們對(duì)用戶敏感數(shù)據(jù)的解密處理函數(shù),如下所示。

/// <summary>  
/// 根據(jù)微信小程序平臺(tái)提供的解密算法解密數(shù)據(jù)
/// </summary>  
[HttpGet]
public SmallAppUserInfo Decrypt(string encryptedData, string iv, string thirdkey)
{
    SmallAppUserInfo userInfo = null;

    //通過AutoFac工廠獲取對(duì)應(yīng)的接口實(shí)現(xiàn)
    var cache = AutoFactory.Instatnce.Container.Resolve<ICacheManager>();
    if (cache != null)
    {
        //從緩存里面,獲取對(duì)應(yīng)的SessionKey
        var sessionkey = cache.Manager.Get(thirdkey);
        if (sessionkey != null)
        {
            //對(duì)用戶身份加密數(shù)據(jù)進(jìn)行解析,獲取包含openid等屬性的完整對(duì)象
            IBasicApi api = new BasicApi();
            userInfo = api.Decrypt(encryptedData, iv, sessionkey.ToString());
        }
    }
    return userInfo;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容