因微信支付API對IPv6支持不完整引起的一次故障
作者介紹
林偉壕,網絡安全DevOps新司機,先後在中國電信和網易遊戲從事數據網絡、網絡安全和遊戲運維工作。對Linux運維、虛擬化和網絡安全防護等研究頗多,目前專注於網絡安全自動化檢測、防禦係統構建。
眾所周知,隨著“微信紅包”等熱門應用的紅火,微信支付在我們的生活中已無處不在。今天我要分享的內容,正是關於微信支付麵向商戶的API 在IPv6支持上的一個問題。關注點不僅在於問題本身,更在排查故障過程中一些思路和方法論。
故障簡述
最近有同事反饋去訪問微信支付API https://api.mch.weixin.qq.com/pay/unifiedorder有一個奇怪的現象,比如第一次訪問url需要等到10-30s不等的時間才有結果返回,之後連續多次訪問卻又是毫秒級的結果返回。等到一段時間不去訪問,在相同環境下再次訪問又會出現第一次訪問很慢,之後連續多次訪問很快返回的現象。因為是比較敏感的應用,所以這樣的響應時間是不可接受,我們決定刨根問底徹查一下。
故障詳情
發現故障,在處理的第一步經常是做故障重現,先使用shell腳本進行測試:
測試結果如下:
可以看到,第一次連接花費了15秒+的時間,之後都是毫秒級的返回。所以如何定位首次連接耗時過長的問題是當前重點。
處理過程
節點1
由於發生這個問題之前,我們有一個內部應用也有類似問題,比如第一次訪問網頁需要加載超過10s才能出來,之後訪問就非常快了。當時通過抓包的方式定位到同一網絡中有其他IP劫持了流量,增加了中間建立連接的時間,後來去除劫持源頭後才恢複正常。於是,受過往經驗影響,我首先猜想是可能是網絡原因或者劫持。按照這個思路,先排查發起請求的客戶端所在網絡,發現是獨立的外網IP,沒有NAT等相對複雜的網絡環境。同時,切換了一個網絡環境進行測試,又發現每次請求響應都很快,測試結果如下:

因此網絡問題仍無法徹底排查,於是決定在curl的同時抓包。
tcpdump -w test.pcap
抓包下來,用wireshark打開,看看是怎麼連接建立過程是怎樣的。
發現第一次請求有TCP重傳,於是更加懷疑是網絡問題,但從這裏又無法直觀看到是具體原因。所以開始往客戶端所在服務器環境進行排查。
節點2
處理過程中同樣是想起,之前遇到一個iptables規則配置不合理導致數據包梳理效率很低的問題,所以一開始在服務器環境排查時優先檢查了iptables配置規則,發現也挺合理的,對於首次建立的連接沒有set tag,也沒有對New有特別設置,然後又臨時清除了所有iptables規則發現問題依舊,於是跳過iptables。

節點3
到了這裏,我開始懷疑DNS解析異常也會引起首次建立連接耗時過長的問題。於是開始檢查/etc/resolve.conf,不管是檢查配置,還是更換DNS服務器問題都是一樣的。但是,“有心栽花花不開,無心栽柳柳成蔭”,這個過程中我還是抓了包,這麼一來,發現IPv6地址解析異常了!
可以看到,DNS服務器返回的IPv6解析結果是”server failure AAAA”,而且耗時超過5s,這裏就有問題了。
節點4
下麵開始把焦點集中到微信支付域名api.mch.weixin.qq.com的IPv6解析上,發現有SERVFAIL,而且重現率很高。

同時指定了IPv4的解析,發現A的查詢很快:

既然確定了IPv6解析有問題,IPv4解析正常,於是修改了相關代碼,確認強製域名解析IPv4的話,都是正常的。
原因分析
從DNS原理看問題
1、整個DNS解析過程
如上圖,當一個客戶端發起DNS解析請求時,會有一個優先級的,按照本地DNS緩存->/etc/hosts的DNS配置->isp或本地域名解析緩存服務器->根域名服務器->頂級域名服務器->...->目標站點權威服務器的順序一路解析下來。
結合本次故障來看,問題應該是在權威服務器這裏。究竟如何,且看下文解說。
先來看看對比的解析結果:
A.api.mch.weixin.qq.com的IPv4解析結果

B.api2.mch.weixin.qq.com的IPv4解析結果

C.api.mch.weixin.qq.com的IPv6解析結果

D.api2.mch.weixin.qq.com的IPv6解析結果

從上麵的測試結果可看出,api2.mch.weixin.qq.com IPv6解析正常,api.mch.weixin.qq.com.IPv6解析失敗是因為多了一層:
api.mch.weixin.qq.com. 600 IN CNAME forward.qq.com.,然後 forward.qq.com AAAA 的解析返回是錯誤的,這裏是權威服務器處理失敗了。
為什麼客戶端解析的是 api.mch.weixin.qq.com AAAA,而緩存回去查詢 forward.qq.com AAAA 呢?
這是因為 api.mch.weixin.qq.com. 600 IN CNAME forward.qq.com. 這條記錄被緩存了,導致後邊後續的查詢都會變成查 forward.qq.com 對應的記錄。
下麵再對比一下解析www.163.com和api.mch.weixin.qq.com看看在權威服務器層麵有何不同。

這裏api.mch.weixin.qq.com的AAAA 解析返回有問題,是SOA,按協議應該還是返回 CNAME 記錄;可以對比 www.163.com 的解析結果發現後者就是返回的SOA。
我們會注意到api.mch.weixin.qq.com status的值 NOERROR ,如果一個返回為 NOERROR 且 沒有 ANSWER SECTION,按照 RFC 2308 section 2.2(https://tools.ietf.org/html/rfc2308#section-2.2)這是一個 NO DATA 返回,表明這是一個指示返回(例如根權威返回頂級權威的 NS)或者是域名記錄不存在(例如查詢 163.com AAAA)。
從上文可以看到 forward.qq.com. 實際上是做了一層 NS,雖然沒有真正的授權出去(ns-os1.qq.com. 上沒有 forward.qq.com SOA 記錄),但緩存看來,它已經授權了一層,而返回的 SOA 有誤,因此緩存服務器無法判定該 NODATA 返回的類型,所以認為該返回不合法,最終導致了api.mch.weixin.qq.com和forward.qq.com 的AAAA記錄一直返回 SERVFAIL,所以緩存服務器會嚐試繼續解析,從而導致首次解析時間過長。等到之後解析成功後,就使用的本地緩存,所以解析很快,等到緩存失效後,問題就會重現。
2、關於curl的DNS解析順序
一般來說,如果服務器開啟了IPv6,curl默認會優先解析 IPv6,在對應域名沒有 IPv6 的情況下,會等待 IPv6 DNS解析失敗 timeout 之後才按以前的正常流程去找 IPv4。DNS服務都是支持解析IPv6的,如果沒有設置IPv6解析,查詢結果就是空,不會是serverfail。
3、關於nslookup和dig的工作原理
不同操作係統nslookup和dig的原理並不同,比如Win7的nslookup每發一次IPv4請求就會產生2個A記錄請求,換到Win XP下nslookup沒發一次IPv4請求則隻會產生一個A記錄請求,所以在抓包分析時需要根據不同操作係統的應用特性進行區分。
微信原來已有解決方案
針對這個問題,其實微信官方文檔
https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=23_2已經給出了答案。
商戶訪問微信支付
-
商戶側使用api.mch.weixin.qq.com(主)和api2.mch.weixin.qq.com(備)這兩個支付api域名訪問微信支付,這兩個域名功能完全一樣。
-
這兩個域名分別配hosts綁定專線訪問微信的兩個131開頭的策略IP,具體電信IP和聯通IP那個綁定到api和api2, 商戶側可以測速後自己決定。
-
商戶側的訪問支付接口的代碼邏輯需具備失敗換域名重試的機製,即訪問api.mch.weixin.qq.com不通的話,就換成api2.mch.weixin.qq.com重試。
-
商戶側訪問微信支付時,若兩條專線都不通,要有能切回走公網訪問的能力。若無法走公網,則商戶側需要知曉這裏的風險。
可行的解決辦法
-
關閉服務器IPv6功能
1、使用ifconfig驗證是否開啟IPv6:

2、查看服務監聽的IP驗證是否開啟IPv6:

3、使用lsmod驗證是否開啟IPv6:

關閉IPv6:修改/etc/modprobe.conf加入如下的兩條,重啟係統即可。
alias net-pf-10 off
alias IPv6 off
開啟IPv6:IPv6是默認支持的,所以隻需將/etc/modprobe.conf中的上麵兩條指令注釋掉即可。
-
在/etc/hosts文件中指定域名ip解析記錄
按照下麵格式配置,但是如果指定的服務器故障了,業務也會受牽連,這不是很穩妥的方法。
api.mch.weixin.qq.com 183.3.235.18
-
在代碼中指定域名解析使用ipv4結果
比如PHP:

-
使用微信官方方案,設置主備域名進行冗餘,支持域名解析失敗切換。
經驗教訓
-
熟讀用戶文檔
很多人學習技術知識時喜歡在搜索引擎找別人的博客或者問題解答來作為知識學習的入口,其實這裏有個問題,博客或者問題答案經過別人的消化,一方麵提高了易讀性,另外也引入了偏見誤解,同時無法保證完整性。反觀官方的用戶文檔,特別是外文的,易讀性相對差,不過很權威也很全麵,很多細節問題都有。但很多人不願意好好看用戶文檔,而是通過網絡上很零碎的知識去學,這樣難免有遺漏。像這次我們使用微信支付API過程中用戶文檔沒有看到這一塊,繞了一圈,反而增加了學習成本。因此,學習或者使用新係統新技術,熟讀用戶文檔反而是一個很基礎的地方呢。
-
不能思維固化
從上麵的處理過程來看,可以發現由於之前有類似問題的處理經驗,一開始我往網絡或劫持,或iptables等方向去定位問題,並沒有帶來太大作用。很多時候有相關經驗起碼能提供思路,不失為一種優勢。而有時候,太重視經驗也會導致思維固化,沒能把基礎的信息全麵收集後再做定性,後做定位,就會事倍功半。
原文發布時間為:2017-02-28
本文來自雲棲社區合作夥伴DBAplus
最後更新:2017-05-15 10:03:46