摘要:此次定位,頗為複雜,而且有時讓人沒有思路,因為同一個介面,換一個環境又好使了,又不敢去懷疑程式碼;然而有問題的環境,換一個介面也好使了,又不得不懷疑程式碼
本文分享自華為雲社群《一次網路請求超時分析》,作者:xiewenci 。
問題現象
發起http請求,後端服務介面/v5/iot/11c9c88e6fb26bead43b75514dc380eb/routing-rule/rules?limit=10&marker=ffffffffffffffffffffffff&offset=0,一直等待,一分鐘後返回響應,且中文亂碼顯示
分析過程
1.首先定界,在正常環境和異常環境換一樣的版本程式碼,測試同樣介面,但是正常環境正常,異常環境還是等待1分鐘,所以覺得是環境問題,於是下一步,抓包
2.分別再異常環境和正常環境抓包,具體流資訊見下圖:
異常環境的流資訊
正常環境的流資訊
從圖中看出正常環境是,服務端傳送響應完成之後,由客戶端正常返回ACK,然後由客戶端發起FIN斷鏈請求。而異常環境則是服務端傳送響應完成之後,客戶端也回了ACK,但是沒有主動發起斷鏈請求,直到超過keep-alive時間之後,由服務端發起了斷鏈請求(因為超時主動斷鏈)。現象是客戶端一直在等服務端傳送響應(可能沒有傳送完),所以懷疑服務端有快取之類,沒有及時將響應流傳送出去,所以繼續檢視程式碼
3.檢視程式碼,在返回客戶端的時候,沒有flush和close操作,程式碼如下
然後以為找到問題原因所在了,就嘗試修改程式碼,如下:
增加了flush操作和close操作(放在了try語快中),最後測試執行,還是跟最初的現象一致,還是會等待1分鐘後才會返回響應。這時就有點納悶了,再重新仔細分析下流,其實從流中可以看出來,服務端的響應是立馬返回給了客戶端的,如下圖
既然給了響應,那為什麼客戶端為什麼還要繼續等待呢?這裡有注意到圖中有幾個問號,這其實是中文字元亂碼了,所以這裡又懷疑是不是編碼格式導致客戶端收到的Content-Length長度和收到的響應的長度不一致,也就是客戶端實際收到的響應的長度小於Content-Length的長度,然後一直等待下去,所以繼續修改程式碼
4.修改程式碼,指定編碼格式為UTF-8,程式碼如下:
替換環境版本,測試執行,響應就立馬返回了,果然是這個編碼格式的鍋
原因回顧
1.編碼格式不一致就會導致響應流的實際長度不一致?答案是一定的,編碼格式不一致,實際長度就是會不一致,測試結果如下:
2.為什麼之前的沒有出現過這個問題?是什麼原因導致編碼格式丟失的
檢視後端服務jar包,發現spring版本已經升級到5.2.21.RELEASE,此版本中沒有指定預設編碼格式為UTF-8
所以需要後端服務去指定編碼格式,或者閘道器服務統一處理
總結與思考
此次定位,頗為複雜,而且有時讓人沒有思路,因為同一個介面,換一個環境又好使了,又不敢去懷疑程式碼;然而有問題的環境,換一個介面也好使了,又不得不懷疑程式碼;還有就是在容器中去呼叫介面,也是一樣的問題,所以感覺又跟網路沒啥關係。其實應該靜下心來去思考為什麼客戶端那裡一直等待,沒有主動發起斷鏈,這裡還是說明了對HTTP協議細節,不夠熟練掌握,才導致中間走了一些誤區,需要去鞏固和加深對http協議的理解。