1. 程式人生 > >SYN 和 RTO

SYN 和 RTO

我們 efi 網絡延遲 pre 網絡 dex one ket work

轉自:https://huoding.com/2017/08/13/628

前兩天,我在微博上推薦了一篇朝花夕拾的文章:The story of one latency spike,文章中介紹了 cloudflare 工程師如何一步一步 debug 網絡延遲問題,細細讀來受益良多,不過我並不打算詳細介紹那篇文章的細枝末節, 本文只摘錄一個點:

When debugging network problems the delays of 1s, 30s are very characteristic. They may indicate packet loss since the SYN packets are usually retransmitted at times 1s, 3s, 7s, 15, 31s.

為什麽是 1 秒、3 秒、7 秒、15 秒、31 秒?說來慚愧,我以前從沒有註意過 SYN 重建時的時間特征,知恥而後勇,正好借此機會來一探究竟。

下面讓我們通過一個實驗來重現一下 SYN 超時重傳的現象:

  1. 在服務端屏蔽請求:「iptables -A INPUT –dport 1234 –syn -j DROP」
  2. 在服務端監聽 1234 端口:「nc -l 1234」
  3. 在客戶端開啟抓包:「tcpdump -nn -i any port 1234」
  4. 在客戶端發起請求:「date; nc <SERVER> 1234; date」

經過一段時間的等待後,我們可以看到 tcpdump 輸出如下:

12:53:15.511826 IP CLIENT.53384 > SERVER.1234: Flags [S], ...
12:53:16.511042 IP CLIENT.53384 > SERVER.1234: Flags [S], ...
12:53:18.511058 IP CLIENT.53384 > SERVER.1234: Flags [S], ...
12:53:22.511042 IP CLIENT.53384 > SERVER.1234: Flags [S], ...
12:53:30.511065 IP CLIENT.53384 > SERVER.1234: Flags [S], ...
12:53:46.511068 IP CLIENT.53384 > SERVER.1234: Flags [S], ...

第一行是正常發出的 SYN,後面五行是超時重傳發出的 SYN,相對於正常發出的 SYN,它們的延遲分別是:1 秒、3 秒、7 秒、15 秒、31 秒,正好符合開頭的描述。之所以重傳五次是因為 net.ipv4.tcp_syn_retries 的缺省值是 5。

此外,我們可以看到兩次 date 命令輸出的時間如下:

Sun Aug 13 12:53:15 CST 2017
Sun Aug 13 12:54:18 CST 2017

可見整個握手過程從開始到超時一共持續了 63 秒,其中 31 秒是總計五次 SYN 發送的時間,剩下的 32 秒是確認第五次 SYN 超時的時間(2 的 5 次方)。由此可見,在 TCP 握手階段一旦出現丟包,網絡延遲會非常久,很多時候這是沒有必要的,比如 Web 服務器,可以考慮設置 net.ipv4.tcp_syn_retries 為 2 或者 3 比較合適。

如果要獲取更為明確的證據,我們還需要了解 RTO(retransmission timeout),即超時重傳時間,具體計算方法很復雜,我就不多說了,有興趣的可以參考本文解決的推薦鏈接,你只要知道系統會根據網絡連接的情況動態調整該值的大小即可。不過在 SYN 握手階段,網絡連接還沒有建立起來,如果此時發生丟包,那麽因為系統沒有可以參照的 RTT(Round-Trip Time),所以此時只能給出系統缺省設置的 RTO:

#define TCP_RTO_MAX	((unsigned)(120*HZ))
#define TCP_RTO_MIN	((unsigned)(HZ/5))
#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ))

...

unsigned long timeo;

if (req->num_timeout++ == 0)
    atomic_dec(&queue->young);
timeo = min(TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX);
mod_timer(&req->rsk_timer, jiffies + timeo);
return;

可見 RTO 的最大值是 120 秒,最小值是 200 毫秒,在連接建立前的初始值是 1 秒,如果經過多次重傳,每次 RTO 的值翻倍,但最大不得超過 120 秒:

  1. 第 1 次重傳:2 的 0 次方,也就是 1 秒(延遲:1 秒)。
  2. 第 2 次重傳:2 的 1 次方,也就是 2 秒(延遲:1 + 2 = 3 秒)。
  3. 第 3 次重傳:2 的 2 次方,也就是 4 秒(延遲:3 + 4 = 7 秒)。
  4. 第 4 次重傳:2 的 3 次方,也就是 8 秒(延遲:7 + 8 = 15 秒)。
  5. 第 5 次重傳:2 的 4 次方,也就是 16 秒(延遲:15 + 16 = 31 秒)。

順便說一句,在建立連接後,因為目前大部分網絡都很快,所以大部分連接的 RTO 都會接近 TCP_RTO_MIN,也就是 200ms,可以通過「ss -int」命令來確認。

  • TCP/IP重傳超時–RTO
  • RTO對tcp超時的影響
  • linux下超時重傳時間(RTO)的實現探究

SYN 和 RTO