最近發現服務的邏輯完成時間很短,但是上游接收到的時間比較長,所以就懷疑是底層數據的序列化/反序列化、讀寫、傳輸有問題,然后懷疑是TCP的讀寫緩存是不是設置太小。現在就記錄下TCP緩存的各配置項以及緩存大小的計算公式。
1.有關發送、接收緩存的配置
內核設置的套接字緩存
/proc/sys/net/core/rmem_default,net.core.rmem_default,套接字接收緩存默認值 (bit)
/proc/sys/net/core/wmem_default,net.core.wmem_default,套接字發送緩存默認值 (bit)
/proc/sys/net/core/rmem_max,net.core.rmem_max,套接字接收緩存最大值 (bit)
/proc/sys/net/core/wmem_max,net.core.wmem_max,發送緩存最大值 (bit)
tcp緩存
/proc/sys/net/ipv4/tcp_rmem:net.ipv4.tcp_rmem,接收緩存設置,依次代表最小值、默認值和最大值(bit)
4096 87380 4194304
/proc/sys/net/ipv4/tcp_wmem:net.ipv4.tcp_wmem,發送緩存設置,依次代表最小值、默認值和最大值(bit)
/proc/sys/net/ipv4/tcp_mem:
94500000 915000000 927000000
對應net.ipv4.tcp_mem,tcp整體緩存設置,對所有tcp內存使用狀況的控制,單位是頁,依次代表TCP整體內存的無壓力值、壓力模式開啟閥值、最大使用值,用於控制新緩存的分配是否成功
tcp或者udp的設置會覆蓋內核設置。其中只有tcp_mem是用於tcp整體內存的控制,其他都是針對單個連接的。
2.修改配置
sysctl -w net.core.rmem_max=8388608 ... sysctl -w net.ipv4.tcp_mem='8388608 8388608 8388608'
3.估算TCP緩存大小
以tcp接收緩存為例(實際上發送窗口=對方的接收窗口),tcp接收緩存有2部分組成:接收窗口及應用緩存,應用緩存用於應用的延時讀及一些調度信息。linux使用net.ipv4.tcp_adv_win_scale(對應文件/proc/sys/net/ipv4/tcp_adv_win_scale)指出應用緩存的比例。
if tcp_adv_win_scale > 0: 應用緩存 = buffer / (2^tcp_adv_win_scale),tcp_adv_win_scale默認值為2,表示緩存的四分之一用於應用緩存,可用接收窗口占四分之三。
if tcp_adv_win_scale <= 0: 應用緩存 = buffer - buffer/2^(-tcp_adv_win_scale),即接收窗口=buffer/2^(-tcp_adv_win_scale),如果tcp_adv_win_scale=-2,接收窗口占接收緩存的四分之一。
那如果能估算出接收窗口就能算出套接字緩存的大小。如何算接收窗口呢?
BDP(bandwidth-delay product,帶寬時延積) = bandwith(bits/sec) * delay(sec),代表網絡傳輸能力,為了充分利用網絡,最大接收窗口應該等於BDP。delay = RTT/2。
receive_win = bandwith * RTT / 2 buffer = rec_win/(3/4) (上面知道tcp_adv_win_scale=2時表示接收窗口占buffer的3/4
以我們的機房為例,同機房的帶寬為30Gbit/s,兩台機器ping可獲得RTT大概為0.1ms,那BDP=(30Gb/1000) * 0.1 / 2 = 1.5Mb,buffer = 1.5Mb * 4 / 3 = 2Mb
4.TCP緩存的綜合考慮
如第三節我們真的能設置tcp最大緩存為2Mb嗎?通常一台機器會部署多個服務,一個服務內部也往往會建立多個tcp連接。但系統內存是有限的,如果有4000個連接,滿負荷工作,達到最大窗口。那么tcp整體消耗內存=4000 * 2Mb = 1GB。
並發連接越多,默認套接字緩存越大,則tcp占用內存越大。當套接字緩存和系統內存一定時,會影響並發連接數。對於高並發連接場景,系統資源不足,縮小緩存限制;並發連接少時,可以適當放大緩存限制。
linux自身引入了自動調整接收緩存的功能,來使吞吐量最大,(緩存最大值還是受限於tcp_rmem[2])。配置項如下。
net.ipv4.tcp_moderate_rcvbuf = 1 (/proc/sys/net/ipv4/tcp_moderate_rcvbuf)