HTTP請求的TCP瓶頸分析

jopen 9年前發布 | 95K 次閱讀 HTTP 網絡技術

 HTTP請求的TCP瓶頸分析

針對三次握手、流量控制(接收窗口)、慢啟動(cwnd,擁塞窗口)、隊首阻塞等方面看下TCP對HTTP的影響


這篇文章基本是對《Web性能權威指南》第一章和第二章的讀書筆記,另外加一些擴展內容,這本書確實贊,推薦

高帶寬和低延遲

所有網絡通信都有決定性影響的兩個方面:延遲和帶寬

  • 延遲 分組從信息源發送到目的地所需的時間。
  • 帶寬 邏輯或物理通信路徑最大的吞吐量
  • </ul>  HTTP請求的TCP瓶頸分析

    延遲的因素

    • 傳播延遲 消息從發送端到接收端需要的時間(不超過光速)
    • 傳輸延遲(帶寬/窗口) 把消息中的所有比特轉移到鏈路中需要的時間,是消息長度和鏈路速率的函數(10M/s和1M/s的線路,時間不一樣)
    • 處理延遲 處理分組首部、檢查位錯誤及確定分組目標所需的時間(路由器分包)
    • 排隊延遲 到來的分組排隊等待處理的時間
    • </ul>

      速度延時

      假定光通過光纖的速度 約為每秒 200 000 000 米,對應的折射率約為 1.5,從紐約到悉尼的一個往返(RTT)也要花 160 ms,分組旅行的距離比這要長得多。這條線路中的 每一跳都會涉及尋路、處理、排隊和傳輸延遲。結果呢,紐約到悉尼的實際 RTT, 大約在 200~300 ms 之間。

      中美骨干網單向數據延時≈60ms,所以中國用戶訪問美國主機數據傳輸的延時理論值高于120ms(RTT)

      </blockquote>

      帶寬延時

      核心數據路徑的骨干或光纖連接,每秒能夠處理數百太比特信息,比如中美之間的海底光纖。光纖就是一根“光導管”,傳送光信號。金屬線則用于傳送電信號,但信號損失、電磁干擾較大,同時維護成本也較高。

      通過波分復用(WDM,Wavelength-Division Multiplexing)技術,光纖可以同時傳輸很多不同波長(信道)的光,2010年初,研究人員已經可以在每個信道中耦合400多種波長的光線,最大容量可達171Gbit/s,而一條光纖的總帶寬能夠達到70Tbit/s

      最后一公里延時-tracerouter

      骨干線路可以有TB的帶寬,但是網絡邊緣的容量就小得多了,而且很大程度上取決于部署技術,比如拔號連接、 DSL、電纜、各種無線技術、光纖到戶。akamai每季度都會發布全球的帶寬報告

      通過tracerouter工具,可以查看路由拓撲,最后一公里的延遲與提供商、部署方法、網絡拓撲,甚至一天中的哪個時段都有很 大關系。作為最終用戶,如果你想提高自己上網的速度,那選擇延遲最短的ISP是最關鍵的

      Traceroute sends out three packets per TTL increment. Each column corresponds to the 
      time is took to get one packet back (round-trip-time).

      traceroute to xx.com (121.41.167.43), 30 hops max, 60 byte packets 1 198.11.175.248 (198.11.175.248) 0.879 ms 0.898 ms 0.950 ms 2 10.107.64.14 (10.107.64.14) 0.945 ms 10.107.64.22 (10.107.64.22) 1.033 ms 10.107.64.18 (10.107.64.18) 75.379 ms 3 198.11.128.162 (198.11.128.162) 135.636 ms 198.11.128.154 (198.11.128.154) 0.913 ms 198.11.128.178 (198.11.128.178) 5.472 ms 4 218.30.53.93 (218.30.53.93) 4.542 ms 218.30.53.97 (218.30.53.97) 2.144 ms 218.30.53.126 (218.30.53.126) 2.334 ms 5 202.97.51.253 (202.97.51.253) 160.089 ms 160.170 ms 160.077 ms 6 202.97.35.105 (202.97.35.105) 188.541 ms 190.518 ms 188.903 ms 7 202.97.33.37 (202.97.33.37) 168.075 ms 168.109 ms 168.016 ms 8 202.97.55.14 (202.97.55.14) 192.583 ms 192.572 ms 192.591 ms 9 220.191.135.106 (220.191.135.106) 201.476 ms 201.542 ms 201.465 ms 10 115.236.101.209 (115.236.101.209) 211.315 ms 211.305 ms * 11 42.120.244.194 (42.120.244.194) 270.211 ms 42.120.244.178 (42.120.244.178) 163.768 ms 163.700 ms 12 42.120.244.238 (42.120.244.238) 191.543 ms 42.120.244.246 (42.120.244.246) 248.825 ms 248.910 ms</pre>

      目標

      高帶寬
      目前還沒有理由認為帶寬會停止增長的腳步,就算技術停滯不前,還是可以鋪設更多的光纜

      低延時

      減少延遲相比帶寬困難的多,從很多方面來看,我們的基礎設施似乎也已經達到了這個極限。這就顯得理解和調優網絡協議顯得特別重要

      </blockquote>


      TCP三次握手

       HTTP請求的TCP瓶頸分析

      客戶端可以在發送 ACK分組之后立即發送數據,而服務器必須等接收到ACK分組之后才能發送數據。這個啟動通信的過程適用于所有 TCP 連接,因此對所有使用TCP的應用具有非常大的性能影響,每次傳輸應用數據之前,都必須經歷一次完整的往返

      中美之間一次RTT最低120,假設你發起一個簡單的HTTP請求,需要一次握手+一次數據傳輸 = 240ms,浪費了50%的時間,這也意味著提高TCP應用性能的關鍵在于想辦法重用連接

      </blockquote>

      擴展:TFO(TCP Fast Open,TCP 快速打 開)機制,致力于減少新建 TCP 連接帶來的性能損失

      流量控制(窗口rwnd)

      rwnd是端到端的控制機制,預防發送過多的數據,TCP連接的每一方都會通告自己的接收窗口,其中包含能夠保存數據的緩沖區空間大小信息。TCP 連接的整個生命周期:每個 ACK 分組都會攜帶相應的最新rwnd 值,以便兩端動態調整數據流速,使之適應發送端和接收端的容量及處理能力

      窗口的值原來只有16位,即65535,所以以前rwnd的最大值不能超過64K。現在基本都有了“TCP 窗口縮放”(TCP Window Scaling),把接收窗口大小由 65 535 字節提高到 1G 字節,在 Linux 中,可以通過如下命 令檢查和啟用窗口縮放選項:

      </tr> </tbody> </table>

      rwnd的設置

      如果我們出于傳輸性能的考慮,當然這個值設置的越大越好,Linux中通過配置內核參數里接收緩沖的大小,進而可以控制接收窗口的大小:

      $> sysctl net.ipv4.tcp_window_scaling  $> sysctl -w net.ipv4.tcp_window_scaling=1 

      </tr> </tbody> </table>

      還有個問題,當大量請求同時到達時,內存會不會爆掉?通常不會,因為Linux本身有一個緩沖大小自動調優的機制,窗口的實際大小會自動在最小值和最大值之間浮動,以期找到性能和資源的平衡點。

      shell> sysctl -a | grep mem net.ipv4.tcp_rmem = <MIN> <DEFAULT> <MAX> 

      </tr> </tbody> </table>

      慢啟動(cwnd,擁塞窗口)

      兩端流量控制確實可以防止發送端向接收端過多發送數據,但卻沒有機制預防任何一端向潛在網絡過多發送數據。換句話說,發送端和接收端在連接建立之初,誰也不知道可用帶寬是多少,因此需要一個估算機制,然后根據網絡中不斷變化的條件 而動態改變速度:TCP能傳輸的最大數據 = MIN(rand,cwnd)

      慢啟動的算法如下(cwnd全稱Congestion Window):

      • 1)連接建好的開始先初始化cwnd = 1,表明可以傳一個MSS大小的數據。
      • 2)每當收到一個ACK,cwnd++; 呈線性上升
      • 3)每當過了一個RTT,cwnd = cwnd*2; 呈指數讓升
      • 4)還有一個ssthresh(slow start threshold),是一個上限,當cwnd >= ssthresh時,就會進入“擁塞避免算法”(后面會說這個算法)
      • </ul>

        最初,cwnd 的值只有1個TCP segment。99 年 4 月,RFC 2581 將其增加到了 4 個 TCP segment。2013 年 4 月,RFC 6928 再次將其提高到 10 個 TCP segment

        </blockquote>

        慢啟動過程

        服務器向客戶端發送 4 個 TCP segment,然后就必須停下來等待確認。此后,每收到一個 ACK, 慢啟動算法就會告訴服務器可以將它的 cwnd 窗口增加 1 個 TCP segment.這個階段通常被稱為指數增長階段,因為客戶端和服務器都在向兩者之間網絡路徑的有效帶寬迅速靠攏

         HTTP請求的TCP瓶頸分析

        計算達到指定窗口所需要的時間公式:

         HTTP請求的TCP瓶頸分析

        • 客戶端和服務器的接收窗口為 65 535 字節(64 KB);
        • 初始的擁塞窗口:4 個segment(一個segment 一般是1460B);
        • 往返時間是 56 ms(倫敦到紐約)。
        • </ul>

          為了達到64KB限制,我們將需要把擁塞窗口增加到45個segment,這將花費224毫秒。

           HTTP請求的TCP瓶頸分析

          慢啟動的影響

          無論帶寬多大,每個 TCP 連接都必須經過慢 啟動階段。換句話說,我們不可能一上來就完全利用連接的最大帶寬。

          慢啟動導致客戶端與服務器之間經過幾百 ms 才能達到接近最大速度的問題,對于大型流式下載服務的影響不顯著,因為慢啟動的時間可以分攤到整個傳輸周期內消化掉。

          對于很多 HTTP 連接,特別是一些短暫、突發的連接而言,常常會出現還沒 有達到最大窗口請求就被終止的情況。換句話說,很多 Web 應用的性能經常受到服 務器與客戶端之間往返時間的制約。因為慢啟動限制了可用的吞吐量,而這對于小 文件傳輸非常不利。

          </blockquote>

          慢啟動對HTTP影響的一次計算

          假設通過HTTP傳輸一個20K的文件,初始條件:

          • 往返時間:56 ms。
          • 客戶端到服務器的帶寬:5 Mbit/s。
          • 客戶端和服務器接收窗口:65 535 字節。
          • 初始的擁塞窗口:4 segment(4×1460 字節 ≈ 5.7 KB)。
          • 服務器生成響應的處理時間:40 ms。
          • 沒有分組丟失、每個分組都要確認、GET 請求只占 1 段。
          • </ul>  HTTP請求的TCP瓶頸分析

            • 0 ms:客戶端發送 SYN 分組開始 TCP 握手。
            • 28 ms:服務器響應 SYN-ACK 并指定其 rwnd 大小。
            • 56 ms:客戶端確認 SYN-ACK,指定其 rwnd 大小,并立即發送 HTTP GET 請求。
              8 84 ms:服務器收到 HTTP 請求。
            • 124 ms:服務器生成 20 KB 的響應,并發送 4 個 TCP 段(初始 cwnd 大小為 4),
              然后等待 ACK。
            • 152 ms:客戶端收到 4 個段,并分別發送 ACK 確認。
            • 180 ms:服務器針對每個 ACK 遞增 cwnd,然后發送 8 個 TCP 段。
            • 208 ms:客戶端接收 8 個段,并分別發送 ACK 確認。
            • 236 ms:服務器針對每個 ACK 遞增 cwnd,然后發送剩余的 TCP 段。
            • 264 ms:客戶端收到剩余的 TCP 段,并分別發送 ACK 確認。
            • </ul>

              作為對比,重用連接,再發一次請求

              • 0 ms:客戶端發送 HTTP 請求。
              • 28 ms:服務器收到 HTTP 請求。
              • 68 ms:服務器生成 20 KB 響應,但 cwnd 已經大于發送文件所需的 15 段了,因
                此一次性發送所有數據段。
              • 96 ms:客戶端收到所有 15 個段,分別發送 ACK 確認。
              • </ul>

                同一個連接、同樣的請求,但沒有三次握手和慢啟動,只花了 96 ms,性能提升幅 度達 275% !

                擁塞窗口的合適值

                Google在這方面做了大量的研究,權衡了效率和穩定性之后,最終給出的建議是10MSS。如果你的Linux版本不太舊的話,那么可以通過如下方法來調整「cwnd」初始值:

      通過如下方式可以確認緩沖大小自動調優機制的狀態(0:關閉、1:開啟): shell> sysctl -a | grep tcp_moderate_rcvbuf 

      </tr> </tbody> </table>

      需要提醒的是片面的提升發送端「cwnd」的大小并不一定有效,這是因為前面我們說過網絡中實際傳輸的未經確認的數據大小取決于「rwnd」和「cwnd」中的小值,所以一旦接收方的「rwnd」比較小的話,會阻礙「cwnd」的發揮。

      擁塞預防

      擁塞預防算法把丟包作為網絡擁塞的標志,即路徑中某個連接或路由器已經擁堵了, 以至于必須采取刪包措施。因此,必須調整窗口大小,以避免造成更多的包丟失, 從而保證網絡暢通。

      重置擁塞窗口后,擁塞預防機制按照自己的算法來增大窗口以盡量避免丟包。某個 時刻,可能又會有包丟失,于是這個過程再從頭開始。如果你看到過 TCP 連接的吞 吐量跟蹤曲線,發現該曲線呈鋸齒狀,那現在就該明白為什么了。這是擁塞控制和 預防算法在調整擁塞窗口,進而消除網絡中的丟包問題。

      最初,TCP 使用 AIMD(Multiplicative Decrease and Additive Increase,倍減加增) 算法,即發生丟包時,先將擁塞窗口減半,然后每次往返再緩慢地給窗口增加一 個固定的值。不過,很多時候 AIMD 算法太過保守,因此又有了很多新的算法,比如DSACK:可以讓協議知道是什么原因丟包,是重傳還是丟失

      </blockquote>

      帶寬延遲積

      發送端和接收端之間在途未確認的最大數據量,取決于擁塞窗 口(cwnd)和接收窗口(rwnd)的最小值。接收窗口會隨每次 ACK 一起發送,而 擁塞窗口則由發送端根據擁塞控制和預防算法動態調整.

      BDP(Bandwidth-delay product,帶寬延遲積)
      數據鏈路的容量與其端到端延遲的乘積。這個結果就是任意時刻處于在途未確認 狀態的最大數據量。無論發送端發送的數據還是接收端接收的數據超過了未確認的最大數據量,都必須停 下來等待另一方 ACK 確認某些分組才能繼續

       HTTP請求的TCP瓶頸分析

      那么,流量控制窗口(rwnd)和擁塞控制窗口(cwnd)的值多大合適?實際上,計算過程很簡單。首先,假設 cwnd 和 rwnd 的最小值為 16 KB,往返時間為 100 ms:

      </blockquote>  HTTP請求的TCP瓶頸分析

      不管發送端和接收端的實際帶寬多大,這個 TCP 連接的數據傳輸速率不會超過 1.31 Mbit/s !想提高吞吐量,要么增大最小窗口值,要么減少往返時間。窗口至少需要 122.1 KB 才能充分利用 10 Mbit/s 帶寬!如果沒有“窗口 縮放(RFC 1323)”,TCP 接收窗口最大只有 64 KB

      </blockquote>

      隊首阻塞造成的延時

      每個 TCP 分組都會帶著一個唯一的序列號被發出,而 所有分組必須按順序傳送到接收端。如果中途有一個分組沒能到達接收 端,那么后續分組必須保存在接收端的 TCP 緩沖區,等待丟失的分組重發并到達接 收端。這一切都發生在 TCP 層,應用程序對 TCP 重發和緩沖區中排隊的分組一無所 知,必須等待分組全部到達才能訪問數據。在此之前,應用程序只能在通過套接字 讀數據時感覺到延遲交付。這種效應稱為 TCP 的隊首(HOL,Head of Line)阻塞

      隊首阻塞造成的延遲可以讓我們的應用程序不用關心分組重排和重組,從而讓代碼 保持簡潔。然而,代碼簡潔也要付出代價,那就是分組到達時間會存在無法預知的 延遲變化。這個時間變化通常被稱為抖動

       HTTP請求的TCP瓶頸分析
      事實上,某些場景下,丟包是讓 TCP 達到最佳性能的關鍵。有些應用程序可以容忍丟失一 定數量的包,比如語音和游戲狀態通信,就不需要可靠傳輸或按序交付

      針對TCP的優化建議

      每個算法和反饋機制的具體細節可能會繼續發展,但核心原理以及 它們的影響是不變的:

      shell> ip route | while read p; do      ip route change $p initcwnd 10;   done 
sesese色