TCP/IP重傳超時--RTO

概述:本文討論主機在發送一個TCP數據包後,如果遲遲沒有收到ACK,主機多久後會重傳這個數據包。主機從發出數據包到第一次TCP重傳開始,RFC中這段時間間隔稱爲retransmission timeout,縮寫做RTO。本文會先看看RFC中如何定義RTO,然後看看Linux中如何實現。本文旨在分享:當遇到了TCP層問題改如何去查找、閱讀文檔,該如何去在Linux源碼中尋求答案。

1. 起源

在分析MySQL Semi-sync故障時,我們用Tcpdump+Wireshark(感謝淘寶雕樑)抓住當時的網絡包傳送細節,觀察到了一次TCP重傳最終導致了Semi-sync超時:

第一次傳輸 13:55:11.893291 master => slave Binlog pos:319890197 重傳: 13:55:12.094596 master => slave Binlog pos:319890197

看到兩次傳送間隔約201毫秒,即第一次傳輸201毫秒後,還沒有收到ACK響應,TCP認爲傳輸超時,開始重傳。

疑問:host和host之間的RTT大約是0.5毫秒,爲什麼第一次重傳需要等200毫秒?(我希望是<20ms)socket程序可以配置嗎RTO嗎?TCP有參數可配置RTO嗎?

2. Google/書籍/RFC

翻開TCP/IP詳解找到關於TCP Retransmission章節,較詳細的介紹TCP的超時機制,書中是個概述,於是又找到RFC1122。

RFC1122的4.2.2.15和4.2.3.1都介紹了Retransmission Timeout的處理(說來慚愧,這是第一次閱讀TCP相關RFC)。

RFC中搜索Retransmission發現RFC 793 1122 2988 6298都有對重傳算法、和初次重傳超時的描述。於是開始閱讀這個四個RFC,耗時約2小時,瞭解了大致的重傳超時算法。

3. RFC中如何計算RTO(Retransmission Timeout)
3.1 RFC-793如何計算RTO

概述:先根據該socket的RTT計算出SRTT(Smoothed Round Trip Time),然後根據一個最大、最小超時時間確定當前RTO。說明:srtt可以理解爲“平滑化”的RTT,即在保持計算簡單的情況儘量考慮歷史RTT。

詳細計算:SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT)

基於SRTT,我們再來計算RTO:RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]]

UBOUND是RTO上線,ALPHA是平滑因子(smoothing factor, e.g., .8 to .9),BETA是一個延遲方差因子(BETA is a delay variance factor (e.g., 1.3 to 2.0))。

仔細看這兩個公式大概就能理解了RTO的計算了。

這裏對上面兩個公式做一個簡單的註釋:公式1中計算SRTT,ALPHA越接近於0,則表示SRTT越相信這一次的RTT;越接近於1,則表示SRTT越相信上次統計的RTT。公式二給RTO分別設置了一個上限和下限。

3.2 RTO重傳間隔是指數增加的

上面我們介紹的是初次重傳時的RTO,如果重傳後還沒收到另一端的響應,下一次重傳RTO則會指數增加,例如第一次重傳RTO是1,之後分別2,4,8,16...。

3.3 RFC-2988和RFC-6298中的RTO計算

在RFC-2988和RFC-6298中又重新改進了RTO的計算方法,Linux中的實現即使參考RFC-2988。算法核心公式:

初始: SRTT <- R RTTVAR <- R/2 RTO <- SRTT + max (G, K*RTTVAR) where K = 4. 根據RTT計算SRTT: RTTVAR <- (1 - beta) * RTTVAR + beta * |SRTT - R'| SRTT <- (1 - alpha) * SRTT + alpha * R' 最後RTO: RTO <- SRTT + max (G, K*RTTVAR)
4. Linux中的RTO(Retransmission Timeout)

這裏說的是RHEL5.4的2.6.18內核,RFC-2988實現參考net/ipv4/tcp_input.c中的tcp_rtt_estimator和tcp_set_rto。可以看到,在Linux中alpha=1/8,RTO最小爲TCP_RTO_MIN。因爲我們的系統中RTT總是很小,所以RTO取值總是能夠取到TCP_RTO_MIN。

在看看TCP_RTO_MIN在Linux中的定義:

123#define TCP_RTO_MAX ((unsigned)(120*HZ)) 124#define TCP_RTO_MIN ((unsigned)(HZ/5))

(這裏簡單的介紹介紹一下HZ,HZ可以理解爲1s,所以120*HZ就是120秒,HZ/5就是200ms。詳細的:HZ表示CPU一秒種發出多少次時間中斷--IRQ-0,Linux中通常用HZ來做時間片的計算,參考)

5. 其他:Linux中可配置重傳參數

/proc/sys/net/ipv4/tcp_retries1 (integer; default: 3)

TCP嘗試了3次(tcp_retries1默認3)重傳後,還沒有收到ACK的話,則後續每次重傳都需要network layer先更新路由。

/proc/sys/net/ipv4/tcp_retries2 (integer; default: 15)

TCP默認最多做15次重傳。根據RTO(retransmission timeout)不同,最後一次重傳間隔大概是13到30分鐘左右。如果15次重傳都做完了,TCP/IP就會告訴應用層說:“搞不定了,包怎麼都傳不過去!”

6. 最後

回答前面的問題:即使RTT很小(0.8ms),但是因爲RTO有下限,最小必須是200ms,所以這是RTT再小也白搭;RTO最小值是內核編譯是決定的,socket程序中無法修改,Linux TCP也沒有任何參數可以改變這個值。

發佈了41 篇原創文章 · 獲贊 4 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章