TCP資料重傳時間細節探祕及資料中心優化

發表於2015-03-26

在資料中心網路內,機器之間資料傳輸的往返時間(rtt)一般在10ms以內,為此調內部服務的超時時間一般會設定成50ms、200ms、500ms等,如果在傳輸過程中出現丟包,這樣的服務超時時間,tcp層有機會發現並重傳一次資料麼?如果設定成200ms以內,答案是沒有機會,原因是linux系統下第一次重傳時間等於傳輸的往返時間上至少加上200ms的預測偏差值,即如果rtt值是7ms,第一次重傳超時時間至少是207ms,這樣如果對某個介面的超時時間設定成200ms以內, 即便是rtt時間很小,仍然無法容忍一次丟包,因為在tcp發現丟包之前,該介面已經超時了。

本文針對linux系統tcp資料包第一次重傳時間的計算進行探究,結果會讓人大吃一驚。提出的優化方法,理論上能夠降低內部服務呼叫時延和出錯量。

tcp傳送資料包後,會設定一個定時器,到期後如果還沒有收到對方的回覆(ack),就會重傳資料包。從發出資料包到第一次重傳之間的間隔時間稱為retransmission timeout(RTO),rto由資料包的往返時間(rtt)加上rtt的預測偏差(波動值)計算出來。

即 rto = srtt + rttvar,其中srtt是rtt的平滑值,而rttvar是波動值,代表可能的預測偏差。

接下來我們做一個試驗。

先ping一下www.weibo.com,看一下資料包的往返時間,如下:

再看一下tcp對到www.weibo.com的rtt相關資料,下面的命令是針對centos7(如果是以下的版本,執行的命令是ip route list tab cache)如下:

由上面看出,平滑後的rtt值約為7ms,rttvar約為7ms,那按理說rto值應該是14ms左右,也就是等14ms後,如果沒有收到對方的響應,就會重傳資料。實際的情況會是這樣麼?

在一個命令視窗裡,執行下面的命令:

同時再開一個命令列視窗裡,執行下面的命令:

從上面的結果可以看出,實際的rto值是207ms,相當於rtt值加上200ms,為什麼呢?

下面從核心tcp原始碼中分析原因。

設定超時時間的函式是tcp_set_rto,在net/ipv4/tcp_input.c中,如下:

可以看出,重傳的定時值isck_rto實際上是呼叫 __tcp_set_rto,接著看它的原始碼,這個在檔案include/tcp/net/tcp.h中,如下:

為了避免浮點數運算,rtt乘以8儲存在socket資料結構中,從程式碼可以確認:

而計算和影響srtt和rttvar的函式是tcp_rtt_estimator,在檔案net/ipv4/tcp_input.c中,程式碼如下:

從上面的程式碼可以看出,srtt  = 7/8 old srtt + 1/8 new rtt,這個跟RFC一致,沒有啥可以說的。

獲得第一個往返時間資料時(一般是建立連線完成時,對於客戶端就是發出sync請求,收到服務端的回應時,而對於伺服器端就是發出syc+ack後,收到客戶端的ack時)的計算分析如下:

再看tcp_rto_min的程式碼,在檔案include/net/tcp.h中:

結合起來看,如果第一個資料包往返時間在100ms以內,rtt預測初始的偏差值就固定為200ms,當資料包往返時間超過100ms,rtt預測偏差的初始值是2倍的rtt值,也就是說rttvar最小值是200ms。

接著分析計算和影響srtt和rttvar的函式是tcp_rtt_estimator的程式碼:

也就是說,rtt預測偏差值rttvar會跟著實際的rtt預測偏差值變化,如果波動變大,則跟著變大,反之,如果波動變小,也會跟著變小。但因為每個傳送週期內,偏差的最大值會重置為tcp_rto_min,所以,rtt預測偏差值rttvar不會小於200ms。

那這200ms的限制,有啥簡單的方法調整麼?繼續看tcp_rto_min的程式碼,前面也貼過,如下:

從上面的程式碼可以看出,如果對應的目標的路由表項中設定了rto_min值,則以設定的值為準。這可以通過netlink機制來修改,具體可以通過ip route命令,增加rto_min選項來完成。

分析完原始碼,接著試驗一下。

執行下面的命令修改成20ms:

看以下修改後的結果:

清除以下路由表的快取,這樣可以立即檢視效果:

再測試訪問weibo.com:

在另外的終端中確認一下結果:

可以看出,本次的rtt值是2ms,rto為22ms,即已經生效。

歡迎一起討論,拍磚也可以。呵呵。

相關文章