背景
公司近期開通了一條訪問美國機房的 1G 專線,並基於 TCP 建立了一套數據傳輸服務。上線后發現一個嚴重的問題:應用程序發送隊列中的數據大量積壓,最終導致程序 OOM Kill,但觀察監控發現專線帶寬利用率只有 50% - 60%。
經過溝通,發現運維同事當時使用 iperf3 測試專線帶寬使用的是 UDP 協議,於是在運維同事協助下使用 TCP 進行二次測試,發現了比較嚴重的問題:
- 在國內機房使用 iperf3 測試單個 socket 流量,在同機房內部(1G交換機)可以達到的最大帶寬約為 110Mb(對照組)
- 在跨境專線使用 iperf3 測試單個 socket 流量,發現極不穩定
Connecting to host US_NY_VM_01, port 5001
[ 4] local HK_QW_VM_01 port 58341 connected to US_NY_VM_01 port 5001
[ ID] Interval Transfer Bandwidth Retr Cwnd
[ 4] 0.00-1.00 sec 109 KBytes 892 Kbits/sec 0 42.4 KBytes
[ 4] 1.00-2.00 sec 1.04 MBytes 8.75 Mbits/sec 0 324 KBytes
[ 4] 2.00-3.00 sec 5.45 MBytes 45.7 Mbits/sec 0 1.52 MBytes
[ 4] 3.00-4.00 sec 7.50 MBytes 62.9 Mbits/sec 6 1.88 MBytes
[ 4] 4.00-5.00 sec 10.0 MBytes 83.9 Mbits/sec 0 2.10 MBytes
[ 4] 5.00-6.00 sec 11.2 MBytes 94.4 Mbits/sec 0 2.27 MBytes
[ 4] 6.00-7.00 sec 8.75 MBytes 73.4 Mbits/sec 0 2.38 MBytes
[ 4] 7.00-8.00 sec 12.5 MBytes 105 Mbits/sec 0 2.48 MBytes
[ 4] 8.00-9.00 sec 12.5 MBytes 105 Mbits/sec 0 2.56 MBytes
[ 4] 9.00-10.00 sec 11.2 MBytes 94.4 Mbits/sec 0 2.62 MBytes
[ 4] 10.00-11.00 sec 11.2 MBytes 94.4 Mbits/sec 0 2.65 MBytes
[ 4] 11.00-12.00 sec 12.5 MBytes 105 Mbits/sec 3 1.93 MBytes
[ 4] 12.00-13.00 sec 7.50 MBytes 62.9 Mbits/sec 92 1.42 MBytes
[ 4] 13.00-14.00 sec 6.23 MBytes 52.3 Mbits/sec 0 1.51 MBytes
[ 4] 14.00-15.00 sec 7.50 MBytes 62.9 Mbits/sec 0 1.58 MBytes
[ 4] 15.00-16.00 sec 8.75 MBytes 73.4 Mbits/sec 0 1.63 MBytes
[ 4] 16.00-17.00 sec 6.25 MBytes 52.4 Mbits/sec 0 1.66 MBytes
[ 4] 17.00-18.00 sec 8.75 MBytes 73.4 Mbits/sec 0 1.68 MBytes
[ 4] 18.00-19.00 sec 7.50 MBytes 62.9 Mbits/sec 0 1.69 MBytes
[ 4] 19.00-20.00 sec 8.75 MBytes 73.4 Mbits/sec 0 1.69 MBytes
[ 4] 20.00-21.00 sec 6.25 MBytes 52.4 Mbits/sec 0 1.69 MBytes
[ 4] 21.00-22.00 sec 8.75 MBytes 73.4 Mbits/sec 0 1.69 MBytes
[ 4] 22.00-23.00 sec 8.75 MBytes 73.4 Mbits/sec 0 1.70 MBytes
[ 4] 23.00-24.00 sec 6.25 MBytes 52.4 Mbits/sec 0 1.71 MBytes
[ 4] 24.00-25.00 sec 8.75 MBytes 73.4 Mbits/sec 0 1.73 MBytes
[ 4] 25.00-26.00 sec 8.75 MBytes 73.4 Mbits/sec 0 1.77 MBytes
[ 4] 26.00-27.00 sec 8.75 MBytes 73.4 Mbits/sec 0 1.82 MBytes
[ 4] 27.00-28.00 sec 7.50 MBytes 62.9 Mbits/sec 0 1.88 MBytes
[ 4] 28.00-29.00 sec 10.0 MBytes 83.9 Mbits/sec 0 1.99 MBytes
[ 4] 29.00-30.00 sec 10.0 MBytes 83.9 Mbits/sec 0 2.12 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Retr
[ 4] 0.00-30.00 sec 249 MBytes 69.6 Mbits/sec 101 sender
[ 4] 0.00-30.00 sec 248 MBytes 69.3 Mbits/sec receiver
iperf Done.
從上圖可以觀察到存在以下問題:
- 網絡狀況不穩定,低速率時也會偶爾會出現 TCP 重傳
- 傳輸速率波動較大,無法長時間維持在最佳的 105M 帶寬,導致帶寬利用率低
長肥管道 (LFN)
跨境專線具有較長的往返時間RTT
與較高帶寬 Bandwidth
,這類具有大管道容量 BDP = RTT * Bandwidth
的網絡我們稱之為長肥管道LFN (Long Fat Networks)
。
LFN 的 RTT 較長,因此一個包從發送到接收到 ACK 需要經歷的時間比普通的網絡更長。由於 TCP 滑動窗口的特性,網絡會存在較長的空閑時間,導致網絡的利用率不高。這篇文章中的動畫很好的展示了這一點,推薦觀看。因此一個簡單的方案就是:增大在途未確認數據量amount inflight
,使其填滿整個管道。
決定在途數據量的因素有以下幾個方面:
- 發送端:擁塞窗口大小
cwnd
以及 發送緩沖大小 - 接收端:接收窗口大小
rwnd
以及 接收緩沖大小
由於 cwnd
和 rwnd
大小是系統根據網絡狀況進行自適應調整的,無法無法直接干預。因此決定先嘗試調整 TCP 緩沖配置,觀察傳輸效果是否有提升。
TCP 緩沖調參
首先計算管道容量:已知專線帶寬為 1Gb,通過 ping 查看專線 RTT 約為 210ms,據此 計算專線 BDP 約為 25MB。
做過 TCP 開發的同學應該都熟悉 SO_RECVBUF 和 SO_SENDBUF 這兩個 socekt 選項,我們可以通過這兩個參數來設置接收與發送緩沖區的大小。如果我們在建立連接 TCP 時指定了這兩個參數,那么操作系統就會使用一個固定的緩沖區大小,而不再會根據網絡進行動態調整,因此這兩個選項要慎用。
然而當指定參數過后,會發現實際的 TCP 緩沖區大小與參數有所出入。這是什么原因造成的呢?首先來看幾個重要的內核參數:
[lhop@localhost ~]$ sysctl -a 2>&1 | egrep 'core.wmem_max|core.rmem_max|ipv4.tcp_wmem|ipv4.tcp_rmem|tcp_adv_win_scale|tcp_moderate_rcvbuf'
net.core.wmem_max = 124928
net.core.rmem_max = 124928
net.ipv4.tcp_wmem = 4096 16384 4194304
net.ipv4.tcp_rmem = 4096 87380 4194304
net.ipv4.tcp_adv_win_scale = 2
net.ipv4.tcp_moderate_rcvbuf = 1
-
net.ipv4.tcp_wmem
與net.ipv4.tcp_rmem
是單個 tcp socket 緩沖區的最小值min
、默認值default
、最大值max
,單位為字節。 -
net.core.wmem_max
與net.core.rmem_max
是單個 socket 所能使用的緩沖區大小上限,單位為字節。該參數的優先級高於tcp_wmem
與tcp_rmem
的最大值,調整參數時需要注意兩者的對應關系。 -
net.ipv4.tcp_moderate_rcvbuf
是否允許操作系統動態調整 tcp 緩沖。開啟后,系統會根據當前可用資源對接收緩沖進行動態調整,此時接收緩沖的大小會在tcp_rmem
最大與最小值之間浮動。當 tcp socket 連接較多時,可以系統會酌情減少每個連接的緩存內存,避免資源耗盡。 -
net.ipv4.tcp_adv_win_scale
是單個 tcp socket 接收緩沖預留給應用的比例。tcp 的接收緩沖可以分為兩部分,一部分是用作接收窗口保存未確認報文,另一部分則是緩存未被應用程序讀取的已確認報文,因此需要預留 1/2tcp_adv_win_scale 內存空間給未讀報文。這也是tcp_rmem
的初始默認值比tcp_wmem
大的原因。
根據 sysctl 給出的結果,我們需要調整有:
- net.core.wmem_max = BDP = 26214400
- net.core.rmem_max = BDP/(1-1/2tcp_adv_win_scale) = 34952000
[lhop@localhost ~]$ cat <<EOF>> /etc/sysctl.conf
net.core.wmem_max = 26214400
net.core.rmem_max = 34952000
net.ipv4.tcp_wmem = 4096 87380 26214400
net.ipv4.tcp_rmem = 4096 87380 34952000
EOF
sysctl -p
調整后重新使用 ipref3 進行測試,發現測試結果基本上沒有變化。基本可以斷定:緩存不足不是造成 TCP 流量瓶頸的主因。
從前面的 iperf3 結果中可以看出,當出現重新傳時,擁塞窗口急劇縮小,最終導致了傳輸速度的下降。決定擁塞窗口大小的就是擁塞控制算法,因此我們將目光轉移到擁塞算法上。
擁塞控制
TCP擁塞控制算法的目的可以簡單概括為:公平 與 效率。
- 當網絡擁塞時,TCP 連接降低傳輸速率,減少由於競爭導致的網絡資源浪費。
- 當網絡空閑時,TCP 連接提升傳輸速率,提高通信效率。
根據實現方式不同,擁塞算法可以分為兩類:基於丟包反饋 與 基於傳輸延時。
其最終目的是找到一個適合當前網絡狀況的最佳擁塞窗口 Wbest。
基於丟包反饋
基於丟包反饋的擁塞算法是目前應用最廣泛且較為成熟的算法。
Linux 系統中默認的擁塞算法 reno
與 cubic
都屬於這類:
[lhop@localhost ~]$ sysctl -a 2>&1 | egrep 'tcp_available_congestion_control|tcp_congestion_control'
net.ipv4.tcp_congestion_control = cubic
net.ipv4.tcp_available_congestion_control = cubic reno
Reno 算法
reno
是最經典的擁塞算法,其核心是基於 加性增窗/乘性減窗 AIMD
的反饋控制:當檢測到信道擁塞時,擁塞窗口會呈指數級快速減小(減少網絡丟包),然后窗口緩慢地線性增長(避免再次擁塞)。算法流程如下圖所示:
然而加性增窗的特性決定了 reno
存在一個明顯缺點:如果算法進入擁塞避免與快速恢復狀這兩個階段時,每經過一個 RTT 才會將窗口大小加1,假設我們鏈路狀況好,但如果RTT很長的話,reno
需要很長時間才能達到 Wbest。實際上受限於丟包率,reno
的另一個典型問題就是還沒等 cwnd 增長到 Wbest,就已經發生丟包並削減cwnd了。
BIC 算法
為了提高 TCP 在 LFN 上的傳輸效率,后續提出了 bic
擁塞算法:
bic
也采用乘法減小的方式減小窗口,並引入一個參數 β 作為減窗因子。若發生丟包時的 cwnd 大小為 Wmax,則減小后的窗口大小為 W = β * Wmax。
bic
假定 W < Wbest < Wmax,因此在恢復階段 bic
是一個變速過程:
- 在遠離 Wmax 時,快速增大窗口,使 cwnd 盡快恢復至 Wbest(對應圖中的
Addtitive Increase
過程) - 在靠近 Wmax 時,緩慢增大窗口,使 cwnd 盡可能長期保持在 Wbest 附近(對應圖中的
Binary Search
過程) - 當到達 Wmax 時,如果仍未發生丟包,那么網絡是空閑的,應該積極地去搶占網絡資源。此時 TCP 連接會嘗試增加擁塞窗口大小,並且增加速度越來越快,直到發現新的 Wmax 為止(對應圖中的
Max Probing
過程)
CUBIC 算法
然而在 RTT 較小的非 LFN 環境中,bic
的增長策略顯得過於激進,會搶占其他其他擁塞算法的 TCP 連接資源。后來,該算法的作者提出了普適性更好的 cubic
算法。
為了減少 RTT(受物理設備影響)對算法的影響,cubic
會記錄最近一次發生網絡擁塞的時間 treduce,距離最近一次擁塞發生的時間可以表示為 t = tnow - treduce。
cubic
的減窗與增窗過程可以簡化為一個的與時間 t 為自變量的窗口增長函數 W(t):
cubic
的窗口增長函數僅僅取決於與擁塞事件的時間間隔,從而將窗口增長獨立於網絡的時延RTT,因而能夠在多條共享瓶頸鏈路的 TCP 連接之間保持良好的RTT公平性。
cubic
與 reno
的擁塞窗口行為比較:
cubic
保證了 TCP 盡可能的長時間保持在 Wbest,從而避免了 reno
算法漫長增窗過程導致傳輸效率低下的問題。
Bufferbloat 現象
隨着內存越來越便宜,TCP 鏈路上的網絡設備的 Buffer 傾向於配置的特別大。但這一做法對於基於丟包反饋的 TCP 擁塞算法相當不友好:
- 發送方無法及時感知到擁塞:當數據開始在隊列排隊時,鏈路已經出現擁塞,但因為 Buffer 很大,數據包不會被丟棄,發送端根本無法感知到擁塞的發生。
- 無效的數據包占用網絡資源:等真的出現丟包時候,重傳的包放在鏈路上還得等之前積壓在 Buffer 的數據包都送達接收端后才能被處理,對網絡網絡資源造成了浪費。
- 接收方隊頭阻塞並丟棄數據:接收端在等待重傳的過程中,如果 Buffer 不足夠大,大量數據送達接收端后都會被丟棄,無形中增加了重傳率,極大增加傳輸延遲。
Bufferbloat 造成的高重傳率,無形中增加了網絡傳輸的延遲,並且還會導致網絡傳輸不穩定,有時候延遲很小,有的時候延遲又很大。這一表現正好符合我們之前的 iperf3 的測試結果:
- 較長的 RTT 導致網絡非擁塞時頻繁丟包,基於丟包反饋的擁塞算法的窗口會比較小,對帶寬的利用率很低,吞吐量下降很明顯,但是實際上網絡負載並不高。
- 在網絡擁塞但無丟包情況下算法一直加窗,丟包事件很快就發生了,乘性減窗使發送速率迅速降低,造成整個網絡的瞬時抖動性,總體呈現較大的鋸齒狀波動。
基於傳輸時延
下圖是 TCP 傳輸鏈路上某個緩存隊列,根據網絡狀況變化,隊列可能處於以下 3 種狀態之一:
- State 1: 網絡空閑,沒有排隊的數據。網絡延遲最低
- State 2: 網絡占滿,數據開始排隊,網絡延遲開始增大
- State 3: 隊列溢出,網絡出現丟包
cubic
的擁塞避免策略是讓擁塞窗口盡可能保持在上一個 Wmax 附近,即 State 2 與 State 3 之間的狀態。相當於盡可能把鏈路資源(線路帶寬+中繼Buffer)占滿,也因此造成了較高的網絡延遲。
而理想狀態是維持在 State 1 和 State 2 之間,即沒有出現排隊導致延遲升高,又能完全占滿鏈路帶寬發送數據,高效且低延遲。為了實現這一點,需要使用 基於傳輸時延 的擁塞避免算法:
- 速率提高后 RTT 不變(無需排隊),則此時鏈路處於 State 1,可以繼續提高發送速率
- 速率提高后 RTT 升高(開始排隊),說明此時鏈路從 State 1 切換到了 State 2,需要降低發送效率,使其恢復到 State 1
最優控制點
下面通過一張圖直觀地感受一下網絡擁塞的變化過程:
- RTprop
round-trip propagation time
:鏈路固有的傳播時延 - BtlBw
bottleneck bandwidth
:鏈路的帶寬上限 - Amount Inflight:在途數據量
- Delivery Rate:數據送達速率
橫向將網絡狀態分為 3 個階段:
- app limited:性能瓶頸是應用本身(網絡空閑)
- bandwidth limited:性能瓶頸是網絡帶寬(網絡擁塞)
- buffer limit:性能瓶頸是中繼 buffer 容量(網絡耗盡)
縱向兩幅圖分別描繪了網絡延遲與吞吐量的變化:
-
上圖展示了 RTT 與 在途數據量 的之間的關系:
- app limited:網絡空閑時,RTT 大小僅取決於 線路 RTprop
- bandwidth limited:網絡擁塞時,RTT 大小取決於 網絡帶寬 BtlBw 與 buffer 數據量(BtlBw限制了隊列的消費速率)
- buffer limit:網絡資源耗盡,開始丟包
-
下圖展示了 送達率 與 在途數據量 的之間的關系:
- app limited:網絡空閑時,送達速率取決於 應用發送速率 與 線路 RTprop(RTprop限制了ACK響應速率)
- bandwidth limited:網絡擁塞時,送達速率僅取決於 網絡帶寬 BtlBw
- buffer limit:網絡資源耗盡,開始丟包
注意圖中的兩個點:
- 最優點
optimum operating point is here
:理想的擁塞算法應該將在途數據量控制在 BDP 附近。 - 丟包點
loss-based congestion control opreates here
:基於丟包的擁塞算法將在途數據量控制在 BDP + BtlneckBufSize 附近。
在最優點附近,發送速率達到 BltBw,從而占滿鏈路帶寬,最高效的利用帶寬;在途數據量不超過鏈路的 BDP = BtlBw * RTprop,從而有最優的延遲。
當鏈路上 Buffer 較小時,最優點與丟包點十分接近,此時基於丟包的算法延遲就不會很高。當鏈路上 Buffer 很大時,基於丟包的擁塞算法就容易導致將 Buffer 占滿,就容易出現 BufferBloat。
反過來說,如果鏈路的 BtlBw 與 RTprop 已知,那么擁塞控制算法就可以根據這兩個值,計算得出最優的發送速率 BDP。這也為擁塞算法的開發提供了新的思路。
BBR 算法
bbr
是 Google 開源的擁塞算法,其目的就是為了解決基於丟包的擁塞控制算法存在的弊端:
- 在 Buffer 較大的鏈路中,容易造成 BufferBloat,增加了傳輸延遲
- 在 Buffer 較小的長距離鏈路中,會對突發流量造成的丟包反應過度,導致吞吐量極低
其核心理念就是建立一套探測模型,動態評估鏈路上 BtlBw 與 RTprop 的變化情況:
- 將最近一個時間窗口 WR(一般是在幾十秒到幾分鍾之間)內監控到的 RTT 最小值作為近似的 RTprop
- 將最近一個時間窗口 WB(一般是 10 個 RRT)內監控到的 送達率 最大值作為近似的 BtlBw
bbr
算法可以分為 4 個階段:
StartUp
bbr
以指數形式增大發送速率,每經過一個 RTT 發送速率會增大 $2/ln(2)$ 倍,從而保證能夠在 O(log(BDP)) 個 RRT 內找到 BtlBw。 當
bbr
發現增大速率后,送達率沒有明顯上升時,會認為鏈路上的 Buffer 已經開始積壓,於是認為已經探測到 BtlBw。
Drain
bbr
會監控在途數據量 Inflight,當 Inflight 與估計的 BDP 接近時,會進入穩定狀態。
ProbeBW
bbr
的主要階段。
ProbeBW 狀態分為兩個周期:
- 增周期:提高發送率到穩定期的 1.25 倍,直到出現丟包或 Inflight 達到 1.25 倍 BDP 為止。如果觀察到延遲升高且送達率不變,說明鏈路上帶寬沒有變化且產生了隊列堆積。
- 減周期:降低發送率到穩定期的 0.75 倍,等待一個 RTprop 或 Inflight低於 BDP 為止。讓鏈路上在增周期出現堆積的隊列清空。
ProbeRTT
bbr
運行了很長時間一直沒有更新 RTprop 時,會進入 ProbeRTT 狀態並試圖探測到更小 RTprop。探測完成之后再根據最新數據來確定進入 StartUp 還是 ProbeBW 階段。
cubic
、reno
與 bbr
的擁塞窗口行為比較:
bbr
保證了 TCP 盡可能的長時間保持對網絡的 100% 的利用率,規避了 cubic
過分占用網絡資源引起 Bufferbloat 現象。
此外,由於 bbr
算法本身不依賴於丟包反饋,因此在高丟包率的網絡下的表現明顯優於 cubic
。
優化效果
經過前面的分析,發現 bbr
算法對於正好能夠解決之前 ipref3 測試中發現的問題。於是跟運維同時協商將 CentOS6 升級為高內核版本的 CentOS7,並開啟 bbr
算法:
[lhop@localhost ~]$ sysctl -a 2>&1 | egrep 'tcp_available_congestion_control|tcp_congestion_control|default_qdisc'
net.core.default_qdisc = fq
net.ipv4.tcp_available_congestion_control = reno cubic bbr
net.ipv4.tcp_congestion_control = bbr
再次使用 iperf3 進行測試,結果如下:
Connecting to host US_NY_VM_01, port 5001
[ 4] local HK_QW_VM_01 port 58341 connected to US_NY_VM_01 port 5001
[ ID] Interval Transfer Bandwidth Retr Cwnd
[ 4] 0.00-1.00 sec 740 KBytes 6.06 Mbits/sec 0 97.6 KBytes
[ 4] 1.00-2.00 sec 4.17 MBytes 35.0 Mbits/sec 0 1.89 MBytes
[ 4] 2.00-3.00 sec 12.5 MBytes 105 Mbits/sec 0 8.76 MBytes
[ 4] 3.00-4.00 sec 13.8 MBytes 115 Mbits/sec 0 8.79 MBytes
[ 4] 4.00-5.00 sec 11.2 MBytes 94.4 Mbits/sec 3 8.79 MBytes
[ 4] 5.00-6.00 sec 13.8 MBytes 115 Mbits/sec 0 8.79 MBytes
[ 4] 6.00-7.00 sec 15.0 MBytes 126 Mbits/sec 0 8.79 MBytes
[ 4] 7.00-8.00 sec 12.5 MBytes 105 Mbits/sec 0 8.79 MBytes
[ 4] 8.00-9.00 sec 13.8 MBytes 115 Mbits/sec 0 8.79 MBytes
[ 4] 9.00-10.00 sec 13.8 MBytes 115 Mbits/sec 0 8.79 MBytes
[ 4] 10.00-11.00 sec 13.8 MBytes 115 Mbits/sec 0 8.79 MBytes
[ 4] 11.00-12.00 sec 12.5 MBytes 105 Mbits/sec 0 8.79 MBytes
[ 4] 12.00-13.00 sec 15.0 MBytes 126 Mbits/sec 0 8.79 MBytes
[ 4] 13.00-14.00 sec 8.75 MBytes 73.4 Mbits/sec 0 8.79 MBytes
[ 4] 14.00-15.00 sec 15.0 MBytes 126 Mbits/sec 0 8.79 MBytes
[ 4] 15.00-16.00 sec 12.5 MBytes 105 Mbits/sec 0 8.79 MBytes
[ 4] 16.00-17.00 sec 13.8 MBytes 115 Mbits/sec 0 8.79 MBytes
[ 4] 17.00-18.00 sec 13.8 MBytes 115 Mbits/sec 0 8.79 MBytes
[ 4] 18.00-19.00 sec 13.8 MBytes 115 Mbits/sec 0 8.79 MBytes
[ 4] 19.00-20.00 sec 12.5 MBytes 105 Mbits/sec 0 8.79 MBytes
[ 4] 20.00-21.00 sec 15.0 MBytes 126 Mbits/sec 0 8.79 MBytes
[ 4] 21.00-22.00 sec 13.8 MBytes 115 Mbits/sec 0 8.79 MBytes
[ 4] 22.00-23.00 sec 12.5 MBytes 105 Mbits/sec 0 8.79 MBytes
[ 4] 23.00-24.00 sec 11.2 MBytes 94.4 Mbits/sec 3 8.79 MBytes
[ 4] 24.00-25.00 sec 11.2 MBytes 94.4 Mbits/sec 0 7.44 MBytes
[ 4] 25.00-26.00 sec 11.2 MBytes 94.4 Mbits/sec 0 6.30 MBytes
[ 4] 26.00-27.00 sec 12.5 MBytes 105 Mbits/sec 0 6.30 MBytes
[ 4] 27.00-28.00 sec 11.2 MBytes 94.4 Mbits/sec 0 6.30 MBytes
[ 4] 28.00-29.00 sec 12.5 MBytes 105 Mbits/sec 0 6.30 MBytes
[ 4] 29.00-30.00 sec 11.2 MBytes 94.4 Mbits/sec 0 6.30 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Retr
[ 4] 0.00-30.00 sec 365 MBytes 102 Mbits/sec 6 sender
[ 4] 0.00-30.00 sec 364 MBytes 102 Mbits/sec receiver
iperf Done.
觀察測試結果發現網絡的吞吐量明顯上升,並且重傳率保持在一個較低的水平。
並且擁塞窗口變化也更穩定,不再出現之前劇烈波動的情況。
使用 10 個 socket 基本上可以跑滿整個專線,優化目標達成。
附錄
在丟包率較高的網絡中,調整 TCP 參數並不能起到很好的效果。但着這並不意味着 TCP 調參沒有意義。
某個服務在同機房內,單個 TCP 連接流量能達到 80M,但在 RTT 1.5ms 且無丟包的跨機房網絡環境上,突發流量峰值最多只能達到 60M 且不穩定。我們嘗試通過調參,將 TCP 傳輸峰值較好地穩定在 70M 左右:
調整前:
[lhop@localhost ~]$ sysctl -a 2>&1 | egrep 'net.core.[r|w]mem_|net.ipv4.tcp_[r|w]*mem|tcp_adv_win_scale'
net.core.rmem_default = 212992
net.core.wmem_default = 212992
net.core.rmem_max = 212992
net.core.wmem_max = 212992
net.ipv4.tcp_mem = 765918 1021224 1531836
net.ipv4.tcp_rmem = 4096 87380 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304
net.ipv4.tcp_adv_win_scale = 1
調整后:
[lhop@localhost ~]$ sysctl -a 2>&1 | egrep 'net.core.[r|w]mem_|net.ipv4.tcp_[r|w]*mem|tcp_adv_win_scale'
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 65536
net.core.wmem_default = 65536
net.ipv4.tcp_mem = 4096 87380 4194304
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 87380 16777216
net.ipv4.tcp_adv_win_scale = 2
可以看到,對於大流量的 TCP 傳輸通道,調整參數仍然能顯著提升跨機房傳輸質量。
但想要獲得一組適合的參數,需要依賴大量的測試才能獲得。很多時候還是得靠業務設計來規避這類問題。
參考資料與圖片來源
- https://blog.csdn.net/zhangskd/article/details/6715751
- https://blog.csdn.net/zk3326312/article/details/91375314
- https://blog.csdn.net/dog250/article/details/81393009
- https://blog.csdn.net/m0_37055174/article/details/102548937
- https://blog.csdn.net/c359719435/article/details/8815499
- https://zhuanlan.zhihu.com/p/64797781
- https://www.cnblogs.com/lshs/p/6038663.html
- https://www.sohu.com/a/388805449_100081454
- https://nextfe.com/tcp-congestion-control
- https://blog.apnic.net/2017/05/09/bbr-new-kid-tcp-block/
- https://www.cs.princeton.edu/courses/archive/fall16/cos561/papers/Cubic08.pdf
- https://wiki.untangle.com/index.php/Bufferbloat
- https://web.cs.wpi.edu/~claypool/papers/bbr-prime/claypool-final.pdf
- https://aws.amazon.com/cn/blogs/china/talking-about-network-optimization-from-the-flow-control-algorithm/