使用缓存防击穿,解决微信”被动回复用户消息”重试回复问题

背景

 作微信公众号开发的时候,其中有个接收普通消息、接收事件推送 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读取。

相关文章
相关标签/搜索