使用緩存防擊穿,解決微信」被動回覆用戶消息」重試回覆問題

背景

 作微信公衆號開發的時候,其中有個接收普通消息、接收事件推送 API。數據庫

有這麼條規則,  」微信服務器在五秒內收不到響應會斷掉鏈接,而且從新發起請求,總共重試三次。假如服務器沒法保證在五秒內處理並回復,能夠直接回復空串,微信服務器不會對此做任何處理,而且不會發起重試。詳情請見「發送消息-被動回覆消息」」。json

歸納起來就2點緩存

一、就是說5s沒響應,這個請求就會被放棄;服務器

二、會從新發起請求,具備冪等性;微信

問題

這樣就會產生2個問題。併發

一、假設個人方法就正好須要6s,那麼即便返回結果也是沒用的,由於請求被放棄了。編輯器

二、我須要返回給用戶正確的回信,假設第一次超時無法及時回信,好比綁定操做,第一次沒回信,第二次再來總不能回覆綁定過了,這樣顯然不合理。spa

 

 

或者直接回復 success ,這樣顯然無法正常的進行消息提醒。code

那麼怎麼作到既執行了操做(第一次超時了),(第二次微信重試)又及時回覆正確的回信呢 。orm

 

 

代碼實現

一、定義緩存的key,就是消息MsgId。

  string cacheKey = model.MsgId.ToString();

二、使用緩存機制,把結果緩存起來,下次進來,直接回覆上次執行的結果。

 TimeSpan expired = new TimeSpan(0, 0, 20);
                    string cacheKey = model.MsgId.ToString();
                    return _cacheLayer.Get(cacheKey, () =>
                    {
                        MsgReply param = new MsgReply() { ToUserName = model.FromUserName, FromUserName = model.ToUserName };
                        string Jsonstr = WeiXinHelper.ReadAccess(HttpRuntime.AppDomainAppPath.ToString() + "/App_Data/WeChat/KeyWordReplay.json");
                        var r = JsonConvert.DeserializeObject<AutoReplay>(Jsonstr);
                        param.Content = r.content;
                        if (String.Equals(model.MsgType, "text", StringComparison.CurrentCultureIgnoreCase))
                        {
                            var item = r.keywordcontent.FirstOrDefault(o => o.keyword.Contains(model.Content));
                            if (item != null)
                            {
                                param.Content = item.content;
                            }
                        }

                        string response = _weChatAlertsService.SubscribeReply(param);
                        AddReceiveLog(model, xml, response);
                        return response;
                    }, expired);

三、這樣既解決冪等問題,也返回了正確的結果。

四、這裏須要注意,緩存取得每一個 Key專有的 lock object;若同時有多個 thread要求相同資料,只會(到數據庫)查第一次,剩下的從 cache讀取。

  public T Get<T>(string key, Func<T> getDataWork, TimeSpan absoluteExpireTime, bool forceRefresh = false, bool returnCopy = true) where T : class
        {
            try
            {
                lock (GetMemoryCacheLockObject(key))
                {
 private static object GetMemoryCacheLockObject(string key)
        {
            string cacheLockKey = string.Format(MemoryCacheLockObjectFormat, key);
            lock (CacheObject)
            {
                var lockObject = CacheObject[cacheLockKey];
                if (lockObject == null)
                {
                    // 取得每個 Key專屬的 lock object;若同時有多個 thread要求相同資料,只會(到資料庫)查第一次,剩下的從 cache讀取
                    lockObject = new object();
                    CacheObject.Set(
                        cacheLockKey,
                        lockObject,
                        new System.Runtime.Caching.CacheItemPolicy()
                        {
                            AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(10)
                        }
                    );
                }

                return lockObject;
            }
        }

總結

一、使用緩存機制,把第一次的結果保存下來,對方重試的時候,直接返回上次的結果。

二、使用lock ,保證併發的時候,若同時有多個 thread要求相同資料,只會(到數據庫)查第一次,剩下的從 cache讀取。

相關文章
相關標籤/搜索