TCP队列满了导致web无法访问

参考:性能分析之TCP全连接队列占满问题分析及优化过程(转载) - 寒冰宇若 - 博客园

介绍:

从图中明显可以看出建立 TCP 连接的时候,有两个队列:syns queue(半连接队列)和accept queue(全连接队列),分别在第一次握手和第三次握手。
半连接队列: 保存 SYN_RECV 状态的连接。
控制参数:

  • 半连接队列的大小:min(backlog, 内核参数 net.core.somaxconn,内核参数tcp_max_syn_backlog).
  • net.ipv4.tcp_max_syn_backlog:能接受 SYN 同步包的最大客户端数量,即半连接上限;
  • tcp_syncookies:当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;

accept队列-全连接队列:保存 ESTABLISHED 状态的连接。
控制参数:

  • 全连接队列的大小:min(backlog, /proc/sys/net/core/somaxconn),意思是取backlog 与 somaxconn 两值的最小值,net.core.somaxconn 定义了系统级别的全连接队列最大长度,而 backlog 只是应用层传入的参数,所以 backlog 值尽量小于net.core.somaxconn;
  • net.core.somaxconn(内核态参数,系统中每一个端口最大的监听队列的长度);
  • net.core.netdev_max_backlog(每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目);
  • ServerSocket(int port, int backlog) 代码中的backlog参数;
  • 文件句柄;
  • net.ipv4.tcp_abort_on_overflow = 0,此值为 0 表示握手到第三步时全连接队列满时则扔掉 client 发过来的 ACK,此值为 1 则说明握手到第三步时全连接队列满时则返回 reset 给客户端。

解决:

TCP 连接队列的溢出数据统计情况,命令为:“netstat -s

1
2
3
4
5
# 查看TCP半连接队列溢出:
netstat -s | grep LISTEN

# 查看TCPaccept队列溢出:
netstat -s | grep overflow

通过反复敲命令,可以看出这个 overflow 的值一直在增加,那么这个现象说明 server 的TCP 全连接队列的确是满了。这时候应该想到的是,全连接队列已经溢出了,下一步就应该看一下,全连接队列的占用情况,命令为:

参数说明:

  • Recv-Q:全连接当前长度
  • Send-Q:如果连接不是在建立状态,则是当前全连接最大队列长度

从上图第三列的 Send-Q 可以看出,5000 端口服务的全连接队列最大为 50,而 Recv-Q 为当前使用了多少。在压测过程中,查看指定端口的 TCP 全连接队列使用情况,如下:

1
ss -lnt |grep 5000

上图可以看出,全连接队列几乎已经被占满,那么最终可以确定问题所在了。找到原因后,现在只要增大全连接队列的长度就可以了。
通过上面介绍的全连接队列中,我们知道全连接队列的大小为 backlog 和 somaxconn 的最小值,那么来看下 somaxconn 的取值。

1
2
sysctl -a |grep net.core.somaxconn
sysctl -a |grep net.ipv4.tcp_max_syn_backlog

如果太少则增加就行:vim /etc/sysctl.conf

可以看出 somaxconn 的值是很大的,那就只有通知开发,增加应用代码中的 backlog 的值来加大全连接队列的长度。

扩展:

一、查看当前系统下所有连接状态的数

1
2
3
netstat -n|awk '/^tcp/{++S[\$NF]}END{for (key in S) print key,S[key]}'
ESTABLISHED 38
TIME\_WAIT 1000

二、看下我系统上默认的SYN队列大小

1
2
3
4
[root@log]# cat /proc/sys/net/ipv4/tcp\_max\_syn\_backlog
262144
定义SYN队列大小:
echo 4096 > /proc/sys/net/ipv4/tcp\_max\_syn\_backlog --定义是php配置的两倍,大于php的就行

三、看下我系统上默认的TIME_WAIT队列大小

1
2
3
4
cat /proc/sys/net/ipv4/tcp\_max\_tw\_buckets
1000
定义TIME\_WAIT的大小:
echo 4096 > /proc/sys/net/ipv4/tcp\_max\_tw\_buckets

四、修改backlog参数
Kernel会为LISTEN状态的socket维护两个队列,一个是SYN RECEIVED状态,另一个是ESTABLISHED状态,而backlog就是这两个队列的大小之和。
当前Linux版本使用上面说法,有两个队列:具有由系统范围设置指定的大小的SYN队列 和 应用程序(也就是backlog参数)指定的accept队列。

五、查看系统默认数量:

1
2
3
cat /proc/sys/net/core/netdev\_max\_backlog
定义队列的数据包的最大数目
echo "4096" > /proc/sys/net/core/netdev\_max\_backlog #在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。

六、nginx 配置参数优化:
有代理就设置在代理上:没有代理就直接设置在web应用上(或者两个都设置)

1
2
3
4
5
6
7
8
9
10
upstream js\_sdk {
#ip\_hash;
server \*\*\*\*\*\*\* weight=1 max\_fails=3 fail\_timeout=10s;
server \*\*\*\*\*\*\* weight=1 max\_fails=3 fail\_timeout=10s;
keepalive 1000;
}

server {
listen 80 default backlog=1024;
listen 443 ssl default backlog=1024;

七、php优化:(-1 表示没有使用系统的 backlog )
vim /usr/local/php/etc/php-fpm.conf

listen.backlog = 2048 #每一个端口最大的监听队列的长度,需要配置nginx配置文件使用,如下(暂时只更改这里)
八、linux内核参进行优化:
vim /etc/sysctl.conf

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096 #对于还未获得对方确认的连接请求,可保存在队列中的最大数目。如果服务器经常出现过载,可以尝试增加这个数字。
net.core.somaxconn = 4096 #定义了系统中每一个端口最大的监听队列的长度,这是个全局的参数。backlog需要设置这个

使用命令使之生效:sysctl -p
转自:https://blog.csdn.net/u010917150/article/details/95621873