最近发现服务的逻辑完成时间很短,可是上游接收到的时间比较长,因此就怀疑是底层数据的序列化/反序列化、读写、传输有问题,而后怀疑是TCP的读写缓存是否是设置过小。如今就记录下TCP缓存的各配置项以及缓存大小的计算公式。linux
内核设置的套接字缓存缓存
/proc/sys/net/core/rmem_default,net.core.rmem_default,套接字接收缓存默认值 (bit)bash
/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
tcp缓存高并发
/proc/sys/net/ipv4/tcp_rmem:net.ipv4.tcp_rmem,接收缓存设置,依次表明最小值、默认值和最大值(bit)spa
4096 87380 4194304blog
/proc/sys/net/ipv4/tcp_wmem:net.ipv4.tcp_wmem,发送缓存设置,依次表明最小值、默认值和最大值(bit)ip
/proc/sys/net/ipv4/tcp_mem:
94500000 915000000 927000000
对应net.ipv4.tcp_mem,tcp总体缓存设置,对全部tcp内存使用情况的控制,单位是页,依次表明TCP总体内存的无压力值、压力模式开启阀值、最大使用值,用于控制新缓存的分配是否成功
tcp或者udp的设置会覆盖内核设置。其中只有tcp_mem是用于tcp总体内存的控制,其余都是针对单个链接的。
sysctl -w net.core.rmem_max=8388608 ... sysctl -w net.ipv4.tcp_mem='8388608 8388608 8388608'
以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
如第三节咱们真的能设置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)