系统性能优化-8 TCP缓冲区与拥塞控制
每个 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 头部选项字段精准反馈已接收的非连续数据段信息,使发送方仅重传真正丢失的报文段。