<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 驗證 TCP SYN 超時重傳機制

          共 3730字,需瀏覽 8分鐘

           ·

          2022-04-18 02:56

          背景

          最近寫了一個壓測代碼,測試一個 http 接口,代碼大概是這個樣子,代碼跑在 Linux 機器上,內(nèi)核版本:3.10.107。

          package?main

          import?(
          ?"context"
          ?"fmt"
          ?"io/ioutil"
          ?"net/http"
          ?"time"
          )

          func?main()?{
          ?for?{
          ??time.Sleep(time.Millisecond?*?10)
          ??cli?:=?http.Client{
          ???Timeout:?5?*?time.Second,
          ??}
          ??req,?err?:=?http.NewRequestWithContext(context.Background(),?"GET",?"http://xxx.com",?nil)
          ??if?err?!=?nil?{
          ???fmt.Println(err)
          ???continue
          ??}
          ??now?:=?time.Now()
          ??rsp,?err?:=?cli.Do(req)
          ??if?err?!=?nil?{
          ???fmt.Printf("%v,?cost:%v\n",?err,?time.Since(now))
          ???continue
          ??}
          ??body,?err?:=?ioutil.ReadAll(rsp.Body)
          ??defer?rsp.Body.Close()
          ??if?err?!=?nil?{
          ???fmt.Println(err)
          ???continue
          ??}
          ??fmt.Printf("len?of?body:%v",?len(body))
          ?}
          }

          預(yù)期 error

          提供 http 服務(wù)的 server,在這樣的壓測條件下會來不及處理這么多的請求,因此會存在 5s 超時的情況。5s 超時的時候,cli.Do(req) 會返回下面的錯誤信息。原因是 http.Client 經(jīng)歷 5s 沒有收到結(jié)果,context 到達了 Deadline。

          context?deadline?exceeded?(Client.Timeout?exceeded?while?awaiting?headers),?cost:5.00031874s

          其他 error

          在壓測過程中,還出現(xiàn)了其他的 error,并且數(shù)量要多于預(yù)期的 context deadline exceeded 錯誤。這是錯誤信息,錯誤信息里隱去了 ip、port。

          dial?tcp?($ip):($port):?connect:?connection?timed?out,?cost:3.017266219s

          出現(xiàn)這個錯誤的調(diào)用耗時只有 3s,但是在代碼中初始化 cli 的時候設(shè)置了 5s 的超時,這是為什么呢?

          分析

          上面的 dial tcp 錯誤顯示是發(fā)起 tcp 調(diào)用時出的錯,那就需要從 tcp 的方面進行分析。

          祖?zhèn)魅挝帐宙?zhèn)樓。

          client 向 server 發(fā)起第一次握手的時候,會發(fā)送 SYN 信號。如果 client 等待了一個超時時間之后沒有收到 server 的 ACK,client 則會重試。如果重試之后還是等待超時了,就再重試。

          在 Linux 中,client 重傳 SYN 的次數(shù)由內(nèi)核參數(shù) net.ipv4.tcp_syn_retries 控制,默認為 6。

          通過以下指令在壓測機器上查看 SYN 重傳次數(shù),可以看到壓測機器上發(fā) tcp 請求時只會超時重傳一次 SYN。

          $:?sysctl?-a?|?grep?tcp_syn_retries
          net.ipv4.tcp_syn_retries?=?1

          重傳間隔是怎么規(guī)定的呢?

          SYN 重傳間隔存在過一個 bug: kernel/git/torvalds/linux.git - Linux kernel source tree

          https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4d22f7d372f5769c6c0149e427ed6353e2dcfe61

          在 bug 修復(fù)前,超時時間是由TCP_RTO_MIN這個參數(shù)計算的,該參數(shù)在內(nèi)核代碼/include/net/tcp.h中定義。

          #define?TCP_RTO_MIN?((unsigned(HZ/5))

          bug 修復(fù)之后,超時時間由TCP_TIMEOUT_INIT計算,代碼地址:

          https://elixir.bootlin.com/linux/v3.10.107/source/include/net/tcp.h#L136

          #define?TCP_TIMEOUT_INIT((unsigned(1*HZ)

          這個值在RFC 6298中,定義為1秒。在RFC 1122中為3秒。這是最開始的超時等待時間,如果在這段時間內(nèi)沒有收到 ACK,超時等待時間按 2 的指數(shù)倍增長。如果重試次數(shù)為 6 次,那么 RFC 6298 的超時重傳間隔就是 1, 2, 4, 8, 16, 32;RFC 1122 中就是 3, 6, 9, 18, 36, 72。

          通過壓測機器的內(nèi)核版本號,查證源碼得到該機器的初始超時重傳時間為 1s。

          那么這就解釋的通了,壓測機器 SYN 重傳次數(shù)為 1,所以 tcp 握手的時候第一次發(fā) SYN,等待了 1s 沒有收到 ACK,又重傳一次,等待 2s 也沒有收到 ACK,這樣總共耗時了 3s,就報了 dial tcp: connection timeout

          復(fù)現(xiàn)

          接下來復(fù)現(xiàn)一下這種情況。

          找一臺服務(wù)器,將 net.ipv4.tcp_syn_retries 設(shè)置為 1。通過編輯 /etc/sysctl.conf 文件實現(xiàn):

          vim?/etc/sysctl.conf
          net.ipv4.tcp_syn_retries?=?1

          在終端中:

          $:?iptables?-A?INPUT?--protocol?tcp?--dport?5000?--syn?-j?DROP
          $:?tcpdump?-i?lo?-Ss0?-n?src?127.0.0.1?and?dst?127.0.0.1?and?port?5000

          開一個新終端:

          $: date?'+?%F?%T';?telnet?127.0.0.1?5000;?date?'+?%F?%T';

          可以看到 tcpdump 中,只收到了兩次 SYN(16:50:20 和 16:50:21),并且兩次間隔為 1s。

          而在新終端中,看到整個調(diào)用的耗時為 3s(16:50:20 - 16:50:23)。

          總結(jié)

          http 或 tcp 調(diào)用時的 dial tcp (ip):(port): connect: connection timed out 錯誤是 SYN 的超時重傳機制引起的。如果遇到這種錯誤,一方面需要考慮 server 可以處理請求的 QPS,另一方面也要檢查 client 端重傳相關(guān)參數(shù)的設(shè)置。

          參考文獻

          [1] 理解 timeout,這一篇就夠了 - poslua | ms2008 Blog

          https://ms2008.github.io/2017/04/14/tcp-timeout/

          [2] net.ipv4.tcp_syn_retries參數(shù)的含義_來自萬古的憂傷的博客-CSDN博客

          https://blog.csdn.net/weixin_45413603/article/details/113891804

          [3] [TCP] tcp連接SYN超時重傳次數(shù)和超時時間_陶士涵的菜地的技術(shù)博客_51CTO博客

          https://blog.51cto.com/u_15274085/2919125

          [4] 《關(guān)于TCP SYN包的超時與重傳》——那些你應(yīng)該知道的知識(四)_BBIE的博客-CSDN博客_syn重傳

          https://blog.csdn.net/sinat_17736151/article/details/82804404

          [5] SYN retransmits: Add new parameter to retransmits_timed_out()

          https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4d22f7d372f5769c6c0149e427ed6353e2dcfe61

          [6] tcp.h - include/net/tcp.h - Linux source code (v3.10.107) - Bootlin

          https://elixir.bootlin.com/linux/v3.10.107/source/include/net/tcp.h#L136



          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學習資料禮包,包含學習建議:入門看什么,進階看什么。關(guān)注公眾號 「polarisxu」,回復(fù)?ebook?獲取;還可以回復(fù)「進群」,和數(shù)萬 Gopher 交流學習。

          瀏覽 303
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人操国产高清使劲操91 | 污污网站在线免费观看 | 黄色视频网站在线 | 精品无无码一区二区 | 黄片免费视频在线观看 |