目前我們部分公眾號粉絲量達到400萬以上,當給大量的粉絲發送模板消息的時候,微信會將如下xml的成功回調消息返給我們系統端造成短時間內系統壓力很大【目前是Nginx+PHP集群架構】,影響我們正常業務的處理,如何解決這個問題呢??
<xml> <ToUserName><![CDATA[gh_7f083739789a]]></ToUserName> <FromUserName><![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8]]></FromUserName> <CreateTime>1395658920</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[TEMPLATESENDJOBFINISH]]></Event> <MsgID>200163836</MsgID> <Status><![CDATA[success]]></Status> </xml>
方案一:抽離出模板消息這個業務單獨用高性能的服務去處理這個請求。目前我們的業務場景不適合,因為模板消息里面包含了用戶信息、業務處理邏輯等。
方案二:采用Nginx+Lua進行處理【業務中PHP框架導致請求一個hello world這樣簡單請求用時都在百毫秒左右】
我采用Nginx+Lua 將回調成功的請求擋在調用后端服務前進行處理,具體方案如下:
- 微信公眾號后台配置回調地址【我們公眾號采用是第三方平台授權方式】配置位置如下:消息與事件接受URL https://abcapi.baidu.com/weixin/callback/$APPID$
- Nginx核心配置文件如下:
location ^~ /weixin/callback/ { default_type 'text/plain'; rewrite_by_lua_file 'conf/scripts/weixin_callback_filter.lua'; }
- 核心lua 腳本如下:
root@devops:/usr/local/nginx/conf/scripts# cat weixin_callback_filter.lua -- 將請求交給PHP處理 function return_declined() ngx.req.set_uri('/index.php' .. ngx.var.uri .. '?' .. ngx.var.args, true); ngx.exit(ngx.DECLINED); end -- 讀取請求體 ngx.req.read_body(); local content = ngx.req.get_body_data(); if (not content) then -- 如果請求體是空的, 不處理 return_declined(); end -- 如果含有TEMPLATESENDJOBFINISH字符串, 響應success local event = content:match('<Event><!%[CDATA%[(.*)]]></Event>'); if (event == 'TEMPLATESENDJOBFINISH' or event == 'VIEW') then ngx.say('success'); ngx.exit(ngx.HTTP_OK); end -- 讀取加密字符串 local encrypted = content:match('<Encrypt><!%[CDATA%[(.*)]]></Encrypt>'); if (not encrypted) then -- 如果加密字符串是空的, 不處理 return_declined(); else -- base64解碼 local _encrypted = ngx.decode_base64(encrypted); if (not _encrypted) then -- 解碼失敗? ngx.log(ngx.ERR, 'ngx.decode_base64() failed: ' .. encrypted); return_declined(); end encrypted = _encrypted; end -- 此處key需要替換為上圖中的消息加解密Key local aes_key = ngx.decode_base64('d9172b8a31042d1d60c8af719acddd89DdWeilyi663='); local aes = require 'resty.aes'; -- 解密 local decrypted = aes:new(aes_key, nil, aes.cipher(256, 'cbc'), {iv=aes_key:sub(0, 16)}, 5):decrypt(encrypted); if (not decrypted) then -- 解密失敗? ngx.log(ngx.ERR, 'openssl.cipher:decrypt() failed: ' .. content); return_declined(); end -- ngx.log(ngx.ERR, 'event: ' .. decrypted:match('<Event><!%[CDATA%[([%a]+)]')); -- 如果含有TEMPLATESENDJOBFINISH字符串, 響應success if (decrypted:find('TEMPLATESENDJOBFINISH') or decrypted:find('VIEW')) then ngx.say('success'); ngx.exit(ngx.HTTP_OK); end return_declined(); - 后期如果官方微信公眾號模板消息那邊發生變化的話,要根據實際情況對lua腳本進行修改
- 相關文章已同步到頭條號:不良帥的江湖
