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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市勉躺,隨后出現(xiàn)的幾起案子癌瘾,更是在濱河造成了極大的恐慌,老刑警劉巖饵溅,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妨退,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡兔魂,警方通過查閱死者的電腦和手機(jī)滑废,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來织咧,“玉大人幸乒,你說我怎么就攤上這事懦底。” “怎么了罕扎?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵聚唐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我腔召,道長(zhǎng)杆查,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任臀蛛,我火速辦了婚禮亲桦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浊仆。我一直安慰自己客峭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布氧卧。 她就那樣靜靜地躺著桃笙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沙绝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天鼠锈,我揣著相機(jī)與錄音闪檬,去河邊找鬼。 笑死购笆,一個(gè)胖子當(dāng)著我的面吹牛粗悯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播同欠,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼样傍,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了铺遂?” 一聲冷哼從身側(cè)響起衫哥,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎襟锐,沒想到半個(gè)月后撤逢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蚊荣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了初狰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡互例,死狀恐怖奢入,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情媳叨,我是刑警寧澤腥光,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站肩杈,受9級(jí)特大地震影響柴我,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扩然,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一艘儒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧夫偶,春花似錦界睁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至说铃,卻和暖如春访惜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腻扇。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國打工债热, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幼苛。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓窒篱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親舶沿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子墙杯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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