關於微信觸發2次請求的問題 [終極方案請直接點此跳轉]
現象描述
測試組在測試環境驗證問題的時候,發現在手機微信頁面點擊某個按鈕,卻觸發了2次ajax請求.
於是開發組小伙伴在修正這個bug的過程中, 一會懷疑前端js問題, 一會又懷疑后端java代碼問題, 卻沒考慮到網絡鏈路轉發機制的問題.
后來筆者提議不改前台,只改后台,注釋全部業務代碼,改成讓線程直接睡眠 Thread.currentThread(N * 1000 ) , 動態修改N參數來模擬業務時間, 發現 sleep 10s以下的請求只會觸發一次, 當sleep 10s以上時,請求會無緣無故觸發2次.
於是有了如下猜想
微信在請求其它服務器時, 如果目標服務器需要10秒以上才能響應,那么微信會自動再次觸發相同請求. 時序圖如下:
我的現象
於是開始驗證, 找各種機型和APP試驗,得出下表:
類型 | 客戶端 | 請求類型 | 服務端 | 服務端 Sleep 秒數 | 服務端收到請求次數 | 補充說明 |
手機android | 微信 | post | 阿里雲ECS主機 | 2 | 1次 | |
手機android | 微信 | post | 阿里雲ECS主機 | 18 | 2次 | |
手機android | qq瀏覽器 | post | 阿里雲ECS主機 | 2 | 1次 | |
手機android | qq瀏覽器 | post | 阿里雲ECS主機 | 18 | 2次 | |
手機android | uc瀏覽器 | post | 阿里雲ECS主機 | 2 | 1次 | |
手機android | uc瀏覽器 | post | 阿里雲ECS主機 | 18 | 1次 | |
手機android | 傲游瀏覽器 | post | 阿里雲ECS主機 | 2 | 1次 | |
手機android | 傲游瀏覽器 | post | 阿里雲ECS主機 | 18 | 1次 | |
蘋果iphone | 微信 | post | 阿里雲ECS主機 | 2 | 1次 | |
蘋果iphone | 微信 | post | 阿里雲ECS主機 | 18 | 1次 | |
蘋果iphone | qq瀏覽器 | post | 阿里雲ECS主機 | 未試驗 | ||
蘋果iphone | qq瀏覽器 | post | 阿里雲ECS主機 | 未試驗 | ||
蘋果iphone | uc瀏覽器 | post | 阿里雲ECS主機 | 未試驗 | ||
蘋果iphone | uc瀏覽器 | post | 阿里雲ECS主機 | 未試驗 | ||
聯想/華碩筆記本PC | 微信 | post | 阿里雲ECS主機 | 2 | 1次 | |
聯想/華碩筆記本PC | 微信 | post | 阿里雲ECS主機 | 18 | 1次 | |
聯想/華碩筆記本PC | qq瀏覽器 | post | 阿里雲ECS主機 | 2 | 1次 | |
聯想/華碩筆記本PC | qq瀏覽器 | post | 阿里雲ECS主機 | 18 | 1次 | |
聯想/華碩筆記本PC | 傲游瀏覽器 | post | 阿里雲ECS主機 | 2 | 1次 | |
聯想/華碩筆記本PC | 傲游瀏覽器 | post | 阿里雲ECS主機 | 18 | 1次 |
最終得出結論為: android手機端通過微信或手機QQ瀏覽器 , 用ajax觸發post請求, 調用任意目標服務器,如果服務器業務處理需要10秒以上才能響應, 會導致騰訊(微信服務器)自動再次觸發相同請求.
普通方案
在請求地址后追加 &connect_redirect=1 即可讓請求不再重發.
怎么找到?非常感謝下鏈接中的9樓, 真是茫茫人海 , 眼前一亮 , 微信開放破平台都沒提及該問題, 真是蛋疼.
而在第二頁中還有人提及"加&connect_redirect=1 可以解決80%左右,但極小部分android手機加上后仍舊無效"的說法, 但在筆者目前的自測用例中尚未發生 , 忽略不計了.
如何自測
筆者在阿里雲一台ECS服務器, 很早前就專門搭建了一個網頁版的接口對接工具,地址為http://www.kingtool.top/kingtool (注ECS服務器2018年12月底到期)
延遲返回接口地址: http://www.kingtool.top/kingtool/httptest?delay=18&connect_redirect=1
- delay=18 是我自己配置的服務端動態參數,意為讓我的java后台sleep(18)秒,即18秒后返回響應報文
- connect_redirect=1 是騰訊(微信)服務器配置的動態參數, 意為只觸發一次請求, 超時也不重發。
- 為了解決ajax跨域問題, 請求鏈路為 前台頁面-->觸發ajax請求-->阿里雲ecs-->阿里雲ecs java代碼調用目標服務器真實地址
此時post請求一次我的阿里雲延遲返回接口, 18秒結束后, 后台日志只打印一次,成功解決.
我的請求報文"天行健,君子以自強不息,地勢坤,君子以厚德載物"在后台日志中的的確確只打印一了一次.
終極方案, 使用redis或memcache緩存【推薦】at 2018/05/04
不知為何之前方案的connect_redirect=1參數突然MMP失靈了,只能另尋出路。
由於面向互聯網的接口,多次相同報文的請求的事實終究無法避免,要從根本上解決該問題, 還是得從服務方的角度出發:
- 要么精簡自身系統以縮短處理時間繞開微信2次觸發問題,
- 要么采用鎖機制控制第2次請求
於是又畫了以下時序圖,核心思想在於讓第2次的處理從第1次的請求結果中去拿。
而在集群模式下,A系統集群中的各server無法感知其它server的處理情況,所以解決重點在於使用獨立的redis或memcache緩存,並配以自動失效時間以保證內存的穩定性(防緩存無限增大),采用該方案后可看到第2次綠條的處理時間明顯比第1次紅條短多了。
(在下圖樣例中 假設A系統處理業務需要11秒, 那么第2次不會再處理業務,最多比第一次多等1~2秒左右 , 最終就算2次觸發響應時間最多也不會超過12秒,)
簡要流程說明
- 以第一次紅條運行時間11秒為前提
- 首先計算請求報文的md5值, 作為緩存的key,也就是查詢要用的唯一識別碼,
- 0.5秒時,第一次的紅條請求到達, 以md5為key去緩存中查詢對應的value,必然為空,1秒時,設置對應value為0,作為正在處理的標志.
- 然后第一次紅條請求一直在處理中,時間往下嘀嗒嘀嗒走到10秒,
- 此時時間超過了10秒,
- 10.1秒時,第二次的綠條請求到達,還是以md5為key去緩存中查詢對應的value,10.8秒去查詢的時候會查到md5對應的value為0,發現該條數據正在被第一次的紅條處理中,好吧,那綠條每隔1秒再去查詢md5對應的value, 判斷是否已處理完(非0)了
- 11秒時,第一次的紅條請求順利結束,把緩存中md5對應value從0更新成返回的業務報文(非0),作為處理完成的標志.
- 11.8秒(相當於12秒),第二次的綠條發現,緩存中md5為key對應的value,已從0更新成了返回業務報文(非0),那么就把該返回業務報文作為真正需要的業務報文返回出去,經歷時間大約12秒左右.
- 於是,當第一次請求如果需要17秒處理時間,那么每二次會在17+1秒返回, 當第一次如果需要23秒處理時間,第二次請求會在23+1秒返回,依次類推.
補充說明
- 至於是否用0作為正在處理標志位,依據你的業務而定, 要是業務本身就返回0或1,那肯定需要換成true,false或其它特殊字符作為正在處理標志了.
- 另外如果您的業務本身請求就很頻率(比如平均每隔個5秒就會有請求過來, 而每次請求時間又需要個20來秒)那么本方法由於第二次請求需要線程阻塞在那,等着第一次處理結束,所以會不太適合, 可能會浪費大量線程資源 , 此時最好從業務本身角度出發看看能不能縮短處理時間, 要想魚和熊掌都能兼得,那各位哥請把自家的服務器和網絡硬件設備鍍層金吧.
他人現象
求助微信一次ajax請求,會訪問兩次 [問題點數:20分,無滿意結帖,結帖人showbo]--https://bbs.csdn.net/topics/391816470 24樓說:
我也遇到這樣的問題,微商城提交訂單,超過10秒鍾,微信瀏覽器重發,導致訂單重復提交的問題。攔截器里看到確實有兩次提交,確定我們代碼里不做超時重試,然后這種情況似乎跟手機有關,我的手機就沒有出現這樣的問題,其他兩個同事的手機就出現,巨坑
后台收到微信重復請求問題--https://blog.csdn.net/gotohomebye/article/details/78508741 作者說:
都困擾快2周了,網上各種查理不出頭緒,假如真是查出來問題這么嚴重的話,微信瀏覽器研發團隊不可能不重視,現在運行中的這么多微信公眾號不可能沒遇到類似問題。其他瀏覽器都是正常的,肯定代碼沒問題,微信瀏覽器的問題也不大
作者:whatlonelytear
本文地址:https://www.cnblogs.com/whatlonelytear/p/8934738.html
歡迎轉載,請在明顯位置給出出處及鏈接。