测试驿栈-由浅入深学性能 TCP 三次握手性能优化
概述
客户端优化
序列号同步
第一次握手的时候,客户端会向服务端发送连接请求的报文,叫SYN,SYN 全称是Synchronize Sequence Numbers,翻译过来是同步序列号。
建立连接的第一件事就是同步序列号。只有同步了序列号才有保证稳定可靠的传输。一个序列号占四个字节的内存
SYN 重试
在第二次握手之前,客户端会等待服务端返回ACK报文。
一般情况下,ACK 报文会在几毫秒内返回。but,如果客户端迟迟没有收到 ACK,客户端会一直重发 SYN 报文,默认是 6 次
net.ipv4.tcp_syn_retries = 2
第 1 次重试在 1 秒钟后,接着会以翻倍的方式在第 2、4、8、16、32 秒共做 6 次重试,最后一次重试会等待 64 秒。如果依然没有收到 ACK,会终止三次握手。所以,总耗时是 1+2+4+8+16+32+64=127 秒。
我们可以根据网络的稳定性和服务器的繁忙程度修改 retries,调整客户端的三次握手时间上限。可以适当调低 retries,尽早的把错误暴露给应用程序。
服务端优化
半连接队列
服务器收到 SYN 报文后会回复 SYN+ACK 报文。既确认了客户端的序列号,也把自己的序列号发给客户端。此时服务端状态是 SYN_RECV。这个状态下,服务端必须建立一个 SYN 半连接队列来维护未完成的握手信息,当这个队列溢出后,服务器将无法再建立新连接。
但是,连接建立失败的原因有很多。如何甄别出队列溢出引发的连接失败?可以通过 netstat 命令
netstat -s | grep "SYNs to LISTEN"
这里是队列溢出导致 SYN 被丢弃的个数累加值。如果数值持续增加,应该调大 SYN 半连接队列。
设置 tcp_max_syn_backlog
syncookies
如果 SYN 半连接队列已满,是不是只能丢弃连接?事实上如果开启 syncookies 功能就可以在不使用 SYN 队列的情况下也能建立连接。
net.ipv4.tcp_syncookies = 1。0 表示关闭此功能,1 表示当半连接队列已满的情况下开启此功能
SYN+ACK 重试
客户端接收到服务端发来的 SYN+ACK 报文之后回复 ACK 去通知服务端,同时客户端连接状态从 SYN_SENT 转换为 ESTABLISHED,表示连接建立成功。
服务器端一直等到接收到 ACK 之后,状态才变为 ESTABLISHED
but,如果服务端迟迟没有收到 ACK,就会一直重发 SYN+ACK 报文,tcp_synack_retries 的默认次数是 5 次。第 1 次重试在 1 秒钟后,接着会以翻倍的方式在第 2、4、8、16 秒共做 5 次重试,若仍然没有收到 ACK,才会关闭连接,总耗时是 63s。
修改重发次数的方法是调整 tcp_synack_retries 参数
net.ipv4.tcp_synack_retries = 6
RST 复位
服务器收到 ACK 后表示连接建立成功,此时内核会把连接从 SYN 半连接队列中移出,再移入 accept 队列,等待进程调用 accept 函数时把连接取出来。如果进程不能及时地调用 accept 函数,就会造成 accept 队列溢出,导致建立好的 TCP 连接被丢弃。此时我们可以选择向客户端发送 RST 复位报文,告诉客户端连接已经建立失败。
需要将 tcp_abort_on_overflow设置为 1
通常情况下,tcp_abort_on_overflow 应该设置为 0。因为可以提高连接建立的成功率。只有当 accept 队列频繁溢出的时候,才能设置为 1
accept 队列长度
backlog 参数就可以设置 accept 队列的大小
backlog 参数受限于 Linux 队列长度,这个上限阈值可以通过 somaxconn 参数修改
通过 netstat 命令可以看到有多少连接因为 accept 队列溢出而被丢弃
netstat -s | grep "listen queue"
常用 tcp 参数配置
net.ipv4.ip_local_port_range = 1024 65500
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_window_scaling = 0
net.ipv4.tcp_sack = 0
net.tcp_abort_on_overflow=0
net.core.netdev_max_backlog = 30000
net.ipv4.tcp_no_metrics_save=1
net.core.somaxconn = 262144
net.ipv4.tcp_syncookies = 0
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2