近期在排查一個棘手的網路超時問題時,我們發現異常連線的持續時間多集中在 1 秒左右。抓包分析顯示,這些封包經歷了重傳,且重傳間隔固定為 1 秒。這引發了核心疑問:為什麼重傳時間(RTO)會固定在 1 秒?相關的 RFC 標準與 Linux 核心實際實現究竟有何差異?本文基於 CentOS 2.6.32-358 核心版本,深入剖析 TCP RTO 的計算邏輯、Linux 的具體實現細節,並提供實戰調優方案。
現行的重傳超時標準為 RFC 6298,它更新了 RFC 1122 並廢棄了 RFC 2988。核心演算法採用 Jacobson/Karels 算法,透過平滑往返時間(SRTT)與往返時間變異數(RTTVAR)動態計算 RTO:
RTO_MIN(建議 1 秒)每獲得新的 RTT 樣本 R':
RTTVAR = 3/4 × RTTVAR + 1/4 × |SRTT - R'|
SRTT = 7/8 × SRTT + 1/8 × R'
RTO = SRTT + max(RTO_MIN, 4 × RTTVAR)
RTO_MIN 靠攏。我們在實驗環境中捕獲三類場景的重傳間隔,發現 Linux 並未完全照搬 RFC:
1s, 2s, 4s, 8s, 16s, 32s (第 6 次超時才通知上層,總計 63 秒)
符合 RFC 指數退避,初始 RTO = 1 秒。
1s, 2s, 4s, 8s, 16s, 32s (同樣從 1 秒起雙倍增長)
0.2s, 0.4s, 0.8s, 1.6s ... 最大 120s,共 15 次重傳,總耗時約 15 分 25 秒
關鍵差異:資料傳輸階段初始 RTO 僅 200 ms(Ubuntu 甚至低至 50 ms),遠低於 RFC 建議的 1 秒;最大 RTO 為 120 秒,超過 RFC 的 60 秒下限要求。
實測發現:
閱讀核心原始碼(net/ipv4/tcp_output.c)發現,Linux 在 RTT 劇烈抖動時引入兩項平滑機制,避免 RTO 震盪導致誤重傳:
當 |R' - SRTT| > RTTVAR 時,判定新樣本波動過大,更新公式改為:
RTTVAR' = 31/32 × RTTVAR + 1/32 × |R' - SRTT| (Linux)
RTTVAR' = 3/4 × RTTVAR + 1/4 × |R' - SRTT| (RFC)
平滑係數從 1/4 降為 1/32,使 RTTVAR 變化更緩和,RTO 曲線更平滑。
若計算出的新 RTTVAR' 小於舊值(且不低於 RTO_MIN),再施加一次平滑:
RTTVAR'' = 3/4 × RTTVAR + 1/4 × max(RTTVAR', RTO_MIN)
目的:防止 RTTVAR 驟降導致 RTO 過小,引發虛假重傳。核心開發者認為 RTO 變大無妨,變小風險較大。
ip route 人為調整 RTO 下界RTO 初始值(含握手階段與 FALLBACK)寫死在核心,無法動態修改。但資料傳輸階段的 RTO 可透過調整路由屬性 rto_min 來影響。
RTO_MIN < 2R:RTO = SRTT + 4×RTTVAR = 3RRTO_MIN ≥ 2R:RTO = SRTT + RTO_MIN = R + RTO_MIN因此,降低 rto_min 可直接壓低 RTO 下界。
# 針對單一目的 IP 設定 rto_min = 20ms
ip route add 180.97.33.108/32 via 172.16.3.1 rto_min 20
# 驗證:ss -eipn '( dport = :www )'
# 輸出顯示 rto:81 rtt:27/13.5 → RTO = 3 × RTT = 81ms (符合預期)
# 內網極低延遲場景
ip route add 172.16.3.16/32 via 172.16.3.1 rto_min 20
# ss 顯示 rto:21 rtt:1/0.5 → RTO = RTT + RTO_MIN = 1 + 20 = 21ms
# 整張網卡生效
ip route change dev eth0 rto_min 20ms
ip route rto_min 精準控制。rto_min 設為 10~20 ms,顯著縮短故障切換時間。net.ipv4.tcp_timestamps = 1,獲得更精準的 RTT 採樣並繞過保守的 FALLBACK 邏輯。