优化方面
Listen状态下的半连接队列以及全连接队列
TCP挥手快速回收,心跳机制调优
异常状态下TCP处理逻辑,超时重传次数
阐述:
服务端收到客户端发起的SYN握手请求后,内核会把该连接存储到半连接队列,并响应客户端SYC+ACK(此时服务端状态SYN_RECV),接着客户端会返回ACK。
服务端收到ACK后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到全连接队列,等待进程调用accept函数时把连接取出来。
非Listen状态
Send-Q:全连接队列,send queue中bytes数值
Recv-Q:半连接队列,receive queue中bytes数值
Listen状态
Send-Q:全连接队列,min(backlog, somaxconn) somaxconn默认为128(笔者虚拟机),backlog由listen时传入
Recv-Q:半连接队列,完成连接的数量 max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog) tcp_max_syn_backlog默认128
- 统计分析
统计半连接连接个数
netstat -natp|grep -i 'syn_recv' |wc -l
统计半连接队列已满造成的失败次数,这是累计值
netstat -s |grep -i 'syns to listen'
统计TCP socket连接因为全连接队列、半连接队列满了而被丢弃
netstat -s |grep -i 'listen'
189000 times the listen queue of a socket overflowed 代表 189000 次全连接队列溢出
30150000 SYNs to LISTEN sockets dropped 代表 30150000 次半连接队列溢出
如果这两个值一段时间内相关数值一直上升,则表明半连接队列、全连接队列有溢出情况
ss -lnt
ss命令可快速查看网络连接状态
注意,调整参数笔者建议修改 /etc/sysctl.conf 文件
- 全连接、半连接队列相关参数优化
TCP 连接队列相关解释,如果半连接或者全连接队列超过长度大小限制,内核会丢弃或者返回RST报文。
1.超出队列大小后内核对于握手报文的处理
cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0(默认,建议)
参数值为 0: 全连接队列满了,server扔掉client发送的ack
客户端认为服务端并未收到ACK,然后重传,服务端继续扔掉ACK,然后重传SYN+ACK直到客户端加入全连接队列或者达到双方重传上限次数。
会造成客户端 connection time out错误,errno=110
参数值为 1:全连接队列满了,新的连接不能加入,server发送reset包给client
TCP全连接队列溢出,会造成客户端connection reset by peer的错误,errno=104
2. 调整全连接队列大小
全连接队列的最大值取值 min(somaxconn, bakclog)
cat /proc/sys/net/core/somaxconn
128(默认)
echo 1024 > cat /proc/sys/net/core/somaxconn
backlog是listen(int sockfd, int backlog)中backlog的大小,如果是Nginx
listen 8080 default backlog=1024 (如果已经调整了上面两个参数1024,nginx默认启动后全连接队列就是511)
不同的Web服务配置不同,看看源码是什么参数
将 tcp_abort_on_overflow 设置为 1 ,如果客户端收到很多 reset 报文,则全连接队列可能太小了。
调整完全连接队列后,tcp_abort_on_overflow 还是建议为 0 ,以应对突发情况。因为不会立即返回 reset,会给客户端重试机会。
3. 调整半连接队列大小
单纯增大半连接没有用处,得全连接队列也增大。
/proc/sys/net/ipv4/tcp_max_syn_backlog
128(默认)
4. 系统同时保持tw状态的连接数量
系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数量,TIME_WAIT套接字会被立即清楚并打印警告信息
cat /proc/sys/net/ipv4/tcp_max_tw_buckets
16384(默认)
5. client端syn重传次数
cat /proc/sys/net/ipv4/tcp_syn_retries
6(默认)
重传的时间周期:(1s后重传->2s后重传->4s->8s->16S->32s->64s)
建议调整
调整为1的示例:
echo 1 > /proc/sys/net/ipv4/tcp_syn_retries
6.server端syn+ack重传次数
cat /proc/sys/net/ipv4/tcp_synack_retries
5(默认)
重传的时间周期:(1s后重传->2s后重传->4s->8s->16S->32s)
7.开启syncookies
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
- 预防SYN攻击
预防SYN攻击===> 笔者认为内网应该很少遇见
1.增加半连接队列的大小
++ tcp_max_syn_backlog
++ somaxconn
++ backlog
2.开启tcp_syncookies
cat /proc/sys/net/ipv4/tcp_syncookies
1
默认是1 ,表示在syn backlog(tcp_max_syn_backlog)队列满员时会开启此功能
调整为2,表视无条件开启syncookies功能
3.减少SYN+ACK 重传次书
echo 1 > /proc/sys/net/ipv4/tcp_synack_retries
- TCP挥手优化
TCP挥手优化
1.开启syncookies
cat /proc/sys/net/ipv4/tcp_syncookies
1
建议调整为1
2.调整TCP回收时间, 新的连接可以立即使用TIME-WAIT状态下的连接
cat /proc/sys/net/ipv4/tcp_tw_reuse
1
建议调整为1
3.开启TCP连接中TIME-WAIT的快速回收
cat /proc/sys/net/ipv4/tcp_tw_recyle
1
建议调整为1
注意:2和3建议开启2网上推荐的。笔者自测发现开启3就能快速回收TIME_WAIT。
4.调整tcp fin wait 2状态的超时时间
cat /proc/sys/net/ipv4/tcp_fin_timeout
60
注意:这个参数在Linux中并非2MSL,而是fin-wait-2 状态超时时间。
要调整TIME_WAIT参数,linux中需要修改内核宏定义重新编译,windows可以在注册表中修改。
大家可以调整这个参数,然后再看Timer等待的时间。
无论改成多少,笔者测试还是从60秒开始计时。
netstat -ntpo 查看timer值
- TCP其他参数优化项
TCP 接收和发送缓冲区大小的默认值和最大值
net.core.wmen_default
net.core.wmen-max
net.core.rmem_default
net.core.rmem-max
TCP保活机制 (以下数值单位均为秒)
TCP保活机制中发送探测报文前的空闲时间
cat /proc/sys/net/ipv4/tcp_keepalive_time
7200(默认)
TCP保活机制中连续发送探测报文的间隔时间
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
30(默认)
TCP保活机制中发送探测报文的次数
cat /proc/sys/net/ipv4/tcp_keepalive_probes
9(默认)
net.ipv4.tcp_keepalive = 120
net.ipv4.tcp_keepalive_intval = 30
net.ipv4.tcp_keepalive_probes = 3
- 汇总优化项目
注意:以下内容仅为笔者总结,并未全部长期测试,建议自测后使用。
汇总所有优化项目 ==> 个人虚拟机食用
vi /etc/sysctl.conf
# 超时重传的次数 ==> 建议减少重试次数, 以太网环境下
net.ipv4.tcp_syn_retries = 3
net.ipv4.tcp_synack_retries = 3
# 队列满员情况下处理机制 ==> 建议保持默认
net.ipv4.tcp_abort_on_overflow = 0
# 半、全连接队列大小设置 ==> 建议适当增大
net.ipv4.tcp_max_syn_backlog = 1024
net.core.somaxconn = 1024
# 默认就开启的 ==>非外层设备也可以考虑关闭
net.ipv4.tcp_syncookies = 1
# 快速回收连接 ==> 建议减少超时时间
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
# tcp 心跳保持 ==> 纯TCP服务建议调整
net.ipv4.tcp_keepalive = 120
net.ipv4.tcp_keepalive_intval = 30
net.ipv4.tcp_keepalive_probes = 3
# time wait 保持个数 ==> 建议适当增大
net.ipv4.tcp_max_tw_buckets = 30000
sysctl -p