每個 TCP 連接都有發送緩沖區和接收緩沖區,發送緩沖區存已發送未確認數據和待發送數據,接收緩沖區存接收但是沒有被上層服務讀取的數據。
# cat /proc/net/sockstat
sockets: used 1885
TCP: inuse 537 orphan 0 tw 3 alloc 959 mem 10其中 mem 代表當前 TCP 連接占用的頁面數
# 擴大窗口協議,之前只有 16 位,最多標識 0-65535 字節,開啟后最大可到 1GB(2^30)
sysctl net.ipv4.tcp_window_scaling
# 單個TCP連接的寫緩沖區 分別代表 最小值,默認值,最大值,單位 Byte
# sysctl net.ipv4.tcp_wmem
net.ipv4.tcp_wmem = 4096 16384 4194304
# 單個TCP連接的讀緩沖區 分別代表 最小值,默認值,最大值,單位 Byte
# sysctl net.ipv4.tcp_rmem
net.ipv4.tcp_rmem = 4096 87380 6291456
發送緩沖區的調節功能是自動開啟的,而接收緩沖區則需要配置 tcp_moderate_rcvbuf 為 1 來開啟調節功能
# 接收緩沖區自動調解功能
# sysctl net.ipv4.tcp_moderate_rcvbuf
net.ipv4.tcp_moderate_rcvbuf = 1
# Linux 對 TCP接收緩沖區的調解控制 分別代表 最小值,默認值,最大值 單位是頁(page)
# sysctl net.ipv4.tcp_mem
net.ipv4.tcp_mem = 3087300 4116401 6174600# 獲取頁面大小 單位 Byte
# getconf PAGESIZE
4096
tcp_mem 是 Linux 判斷系統內存是否緊張的依據,當 TCP 內存小于第 1 個值時,不需要進行自動調節;在第 1 和第 2 個值之間時,內核開始調節接收緩沖區的大小;大于第 3 個值時,內核不再為 TCP 分配新內存,此時新連接是無法建立的。
在高并發服務器中,為了兼顧網速與大量的并發連接,我們應當保證緩沖區的動態調整上限達到帶寬時延積,而下限保持默認的 4K 不變即可。而對于內存緊張的服務而言,調低默認值是提高并發的有效手段。
同時,如果這是網絡 IO 型服務器,那么,**調大 tcp_mem 的上限可以讓 TCP 連接使用更多的系統內存,這有利于提升并發能力。**需要注意的是,tcp_wmem 和 tcp_rmem 的單位是字節,而 tcp_mem 的單位是頁面大小。而且,千萬不要在 socket 上直接設置 SO_SNDBUF (寫緩沖區上限)或者 SO_RCVBUF(讀緩沖區上限),這樣會關閉緩沖區的動態調整功能。
在擁塞控制中,首先會進行慢開始(每收到1個 ACK 窗口就+1,相當于1個 RTT 翻一倍),慢開始指初始窗口比較小,而不是增長的慢,增長是指數級的,其實很快。這就涉及到初始擁塞窗口的大小:
# 查看當前連接的初始擁塞窗口
ss -nli | fgrep cwnd
# 修改初始擁塞窗口 10 MSS,如果網絡特別好可以繼續加大,有些高速 CDN 站點,甚至把初始擁塞窗口提升到 70 個 MSS
ip route | while read r; doip route change $r initcwnd 10;done
初始擁塞窗口越大,小請求就可以更快發送完畢,也會更快的結束慢開始,結束慢開始的情況一般有以下三類:
- 定時器超時,觸發重傳
- 擁塞窗口的增長到達了慢啟動閾值 ssthresh(全稱為 slow start threshold)
- 接收到重復的 ACK 報文,可能存在丟包
在第一種情況下,說明網絡擁塞已經比較嚴重了,不同的算法會有不同的調整,目前主流的調整算法 CUBIC 算法會把擁塞窗口降為原先的 0.8 倍
# 查看當前系統支持的擁塞調整算法
sysctl net.ipv4.tcp_available_congestion_control
# 配置具體的擁塞調整算法
net.ipv4.tcp_congestion_control = cubic
這種算法在內網中效率高于 BBR,因為內網本來就時延低,但在高 RT 場景下表現不如 BBR,BBR 基于測量實現擁塞窗口的動態調整,在丟包率較高的網絡中應用效果尤其好
,例如此時網絡帶寬不變,但是請求時延增加,說明網絡已經開始阻塞了,CUBIC 直到丟包才開始控制,是有些滯后了的。
Linux 4.9 版本之后都支持 BBR 算法,同樣使用 tcp_congestion_control 進行配置。
在第二種情況下,已經到了慢啟動閾值,可能接下來就會出現擁塞,因此擁塞窗口不再指數級增長,而是線性增長
在第三種情況下,對方連續發送重復 ACK(例如3個) 說明網絡情況尚可,可能由于中間設備等原因造成某包丟失或失序,不應認為網絡出現嚴重擁塞,因此觸發快速重傳,略微降低發送速度,這里有個問題其實是,比如報文 6 7 8 一起發送,對方發了 3 個連續的 6 的 ACK,觸發報文 6 的重傳時,是否要發 7 和 8,如果發可能對方已經收到了只是 6 一直沒到,就浪費帶寬了,如果不發也可能網絡不好丟失了,待會還要重發,SACK (Selective Acknowledgment)選擇性確認機制解決了這個問題,接收方通過 TCP 頭部選項字段精準反饋已接收的非連續數據段信息,使發送方僅重傳真正丟失的報文段。