在最近的工作中需要對同乙個網域名稱下的源站同時發起多次請求,有時甚至達到了6000次,發生了很嚴重的效能問題,追查了下原因是被瀏覽器(chrome)stalled
了,因為瀏覽器只支援對同乙個網域名稱下保持6個連線,擁有更多連線時,就只能被掛起,直到上乙個連線完成被復用。所以同時發起6000次請求,100ms的耗時也將會導致100秒的處理時間,在實際過程中復用連線將會導致更高的耗時。最終的解決方案當然是要求對方介面人改批量介面啦,皆大歡喜。
在追查的過程中也發現了前人關於請求被掛起導致載入緩慢的分析,最終追查到chrome日誌,其中關於網路連線引用到了tcp reset
的文章,對自己有一些啟發,這裡翻譯並總結下。
在網路服務中,經常性會遇到reset導致的連線斷開問題,每一次追查問題可能都會讓人大為惱火,但是首先我們需要了解reset是怎麼工作的。reset事實上是一件好事情,它關閉那些已經沒有必要繼續存在的連線,舉個例子,我們的程式建立了許多的tcp短連線,這些連線在斷開時將會保持一段時間的time wait state
狀態,預設4分鐘,用於保證連線能夠被正確的斷開,但是當我並不想這麼做,想降低資源的消耗快速重用這些埠和資源時,就需要使用reset來重置這些連線了。從這裡我們也能看出,重置實質上就是強制斷開連線。
首先來說一下具體的tcp連線,老生常談的三次握手,它適用於基於ip協議的穩定網路傳輸,保證資訊是準確無誤的。當乙個節點a想要跟另外乙個網路節點b通訊時,a節點會先傳送乙個同步包syn包
,這個包將會包含建聯需要的基本資訊,如源ip和埠以及目的ip和埠,以及序列值等資訊。
如上面的包可以看到tcp:flags=......s
字段,這標識著這是乙個syn包,裡面同時包含著source ip, srcport, destination ip, dstport
資訊用於建聯,還有內容長度以及序列值等資訊。這裡的445埠是常用的smb直連埠,為了本次能夠正確建聯服務端應該監聽這個埠並捕獲這個syn包。
第二個包是ack, syn包
,代表著服務端接收到了第乙個syn包
並傳送了自己的syn包
,這兩個動作發生在同乙個包裡,此時的包裡可以看到源站ip和埠已經與目的ip和埠的位置互換了。
最後乙個包是客戶端確認接收到了服務端的syn包
並完成了本次建聯。
上面就是大家常說的三次握手的實際傳輸文字,現在兩個節點已經建聯並能夠進行穩定的資料傳輸了。這個穩定是對應於只基於ip協議的傳輸無法確認收到的是否是正確的資料。
之前提到了time wait state
,它的存在有什麼意義呢?當三次握手建立tcp連線後,我們還需要四次分手來斷開連線,其中的包與上面類似,過程為:
客戶端a傳送完了所有資料,向服務端b傳送fin包
來請求斷開,a進入fin_wait_1
狀態。
服務端b傳送ack包
給客戶端a,表示收到了斷開請求,a進入fin_wait_2
狀態。
當服務端b也傳送完了自己的所有資料,向客戶端a傳送fin包
請求斷開,b進入last_ack
狀態。
客戶端a收到了服務端b的請求,返回ack包
給b,然後進入time_wait
狀態。
服務端b收到後,就斷開連線,不再確認。當經過2msl(maximum segment lifetime) 最大分段壽命
後a沒有收到b的任何重傳資訊,就代表連線被正常斷開了,a此時也斷開連線。
其中的msl最大段壽命是乙個tcp分段可以存在於網際網路系統中的最大時間,它通常被定義為兩分鐘長。保持2msl的time wait state保證了這個連線的雜散資料能夠被正確傳遞到並且連線相關的資源被正確釋放。比如步驟4中a的ack包並沒有被接收到,就會被b的重傳機制要求重傳,重傳成功後才可以正確斷開連線。
reset是立即斷開tcp連線,釋放連線占用的資源並使得系統可以再次使用這些資源,如客戶端的埠號和服務端的fd(檔案描述符)。
下面講幾個常見的reset的場景,用於了解具體的操作。
從windows 2000開始,作業系統更希望監聽445埠而不是139埠來提供smb服務,為了向下相容,使用者需要同時發起兩個埠的syn
包,如果服務端也同時監聽了兩個埠,那麼將會針對這兩個請求返回兩個ack + syn
包。此時客戶端拿到兩個響應,將會給更偏愛的埠返回最後一次syn
包,同時對另乙個埠進行reset操作,如下圖所示:
對於乙個syn
包響應ack reset
包,是在服務端接收到了客戶端的建聯請求,但是無法在請求的埠進行建聯的場景操作,原因可能是:
請求建聯的服務端並沒有監聽這個埠。
一些原因導致服務端無法在這個埠建聯成功,如資源被耗盡,從而無法建立起新的連線。
在一些裝置上如果沒有監聽該埠,請求將會被預設丟棄,而不是返回ack + reset
包,這是出於安全性的考慮,比如防火牆。
在三次握手建聯之後,當乙個網路傳輸包傳輸失敗(沒有接收到該包的ack包超時)時,會進行重傳,並嘗試等待一段時間ack包,當重傳五次依然失敗,就會reset該連線。這裡的重傳次數可以設定,預設為5。reset的原因是我們認為此時在兩個網路節點間或希望傳送ack包的節點上發生了問題,這也意味著本次連線變得不再有效。這裡需要注意幾點:
重傳指的是同乙個包重傳5次。
重傳的包之前的包傳輸不受影響。
遲到的ack不會導致reset,遲到的包會在後續超時重傳時成功被確認。
對用於tcp建聯的syn
包重傳的次數限制可以通過tcp-maxconnectretransmission
字段設定,預設為2。
許多應用程式也會reset連線,這些很難發現,如果我們排查了網路問題和tcp本身沒有重置該連線,以及上面幾種情況的話,我們基本可以斷定是應用程式重置了該連線。乙個很常見的場景,我們會在應用上建立很多的短連線,這樣的應用由於保持了太多的time wait state
會導致埠資源被耗盡,所以這些等待的連線會被應用程式主動重置。但此時開發者應該明白time wait state
存在的意義。
我們可以通過檢視應用中關於socket的**或檢視socket日誌來確認是否是應用重置了連線。如果三次握手的連線正常關閉,應該是採用fin
包的四次握手。
還有一些更為罕見的場景是埠被搶占,常出現在系統重啟時不同應用搶占同乙個埠來進行監聽。
來自於網路的reset,聽起來並不是那麼靠譜,但事實上確實會發生。在建聯的兩個網路節點之間的網路裝置會主動重置該連線,這種問題很難追查,最好的方法是使用traceroot來檢視節點之間的連線,並同時對接收方和傳送方的資料同時進行捕獲。在這種場景下,我們會發現發起reset請求的源ip並不是建聯的兩個網路節點的任何乙個,發生這種場景的原因有很多,比如交換機重啟,網路裝置故障等。有時候我們甚至會發現中間的裝置對雙方都發起了reset請求。
如果乙個埠已經處於time wait state
,這時希望復用源埠和目的埠傳送syn
包,根據rfc 1122協議,是被允許的,但需要注意該狀態的含義(用於上個連線最後的fin包被正確接收),此時的包seq
值需要大於上次連線的最後乙個包,否則將會被reset。
tcp重置是乙個好的事情,如果沒有reset,我們將會遇到各種各樣的tcp網路連線問題。需要注意的是導致reset的原因是多種多樣的,不僅僅是兩端的節點還有可能是應用程式,追查問題時最重要的是檢視包的狀態以及其重傳。
reset來自**:
tcp的三次握手和四次分手:
tcp三次握手 TCP 三次握手總結
tcp特點概述 tcp segment structure 段結構 step2 server host receives syn,replie with syn ack segment 答覆syn ack報文段 step3 client receives synack,replies with ac...
tcp 狀態以及三次握手
在tcp層,有個flags欄位,這個欄位有以下幾個標識 syn,fin,ack,psh,rst,urg.其中,對於我們日常的分析有用的就是前面的五個字段。它們的含義是 syn表示建立連線,fin表示關閉連線,ack表示響應,psh表示有 data資料傳輸,rst表示連線重置。其中,ack是可能與sy...
tcp的三次握手 傳輸層 TCP 三次握手
使用tcp協議進行通訊的雙方必須先建立連線,然後才能開始傳輸資料。為了確保連線雙方可靠性,在雙方建立連線時,tcp協議採用了三次握手策略。如圖 客戶端傳送帶有syn標誌的連線請求報文段,然後進入syn send狀態,等待服務端的確認。服務端接收到客戶端的syn報文段後,需要傳送ack資訊對這個syn...