TCP/IP详解 第十二章 (4)TCP 半连接队列和全连接队列满了会发生什么?又该如何应对?
前言
网上许多博客针对增大 TCP 半连接队列和全连接队列的方式如下:
-
增大 TCP 半连接队列方式是增大 tcp_max_syn_backlog;
-
增大 TCP 全连接队列方式是增大 listen() 函数中的 backlog;
这里先跟大家说下,上面的方式都是不准确的。
“你怎么知道不准确?”
很简单呀,因为我做了实验和看了 TCP 协议栈的内核源码,发现要增大这两个队列长度,不是简简单单增大某一个参数就可以的。
接下来,就会以实战 + 源码分析,带大家解密 TCP 半连接队列和全连接队列。
“源码分析,那不是劝退吗?我们搞 Java 的看不懂呀”
放心,本文的源码分析不会涉及很深的知识,因为都被我删减了,你只需要会条件判断语句 if、左移右移操作符、加减法等基本语法,就可以看懂。
另外,不仅有源码分析,还会介绍 Linux 排查半连接队列和全连接队列的命令。
“哦?似乎很有看头,那我姑且看一下吧!”
行,没有被劝退的小伙伴,值得鼓励,下面这图是本文的提纲:
正文
什么是 TCP 半连接队列和全连接队列?
在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:
-
半连接队列,也称 SYN 队列;
-
全连接队列,也称 accepet 队列;
服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。
不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,内核会直接丢弃,或返回 RST 包。
如何增大 TCP 全连接队列呢?
是的,当发现 TCP 全连接队列发生溢出的时候,我们就需要增大该队列的大小,以便可以应对客户端大量的请求。
TCP 全连接队列足最大值取决于 somaxconn 和 backlog 之间的最小值,也就是 min(somaxconn, backlog)。从下面的 Linux 内核代码可以得知:
-
somaxconn
是 Linux 内核的参数,默认值是 128,可以通过/proc/sys/net/core/somaxconn
来设置其值; -
backlog
是listen(int sockfd, int backlog)
函数中的 backlog 大小,Nginx 默认值是 511,可以通过修改配置文件设置其长度;
前面模拟测试中,我的测试环境:
-
somaxconn 是默认值 128;
-
Nginx 的 backlog 是默认值 511
所以测试环境的 TCP 全连接队列最大值为 min(128, 511),也就是 128
,可以执行ss
命令查看:
如何增大 TCP 半连接队列呢?
增大半连接队列,我们得知不能只单纯增大 tcp_max_syn_backlog 的值,还需一同增大 somaxconn 和 backlog,也就是增大全连接队列。否则,只单纯增大 tcp_max_syn_backlog 是无效的。
增大 tcp_max_syn_backlog 和 somaxconn 的方法是修改 Linux 内核参数:
增大 backlog 的方式,每个 Web 服务都不同,比如 Nginx 增大 backlog 的方法如下:
最后,改变了如上这些参数后,要重启 Nginx 服务,因为半连接队列和全连接队列都是在 listen() 初始化的。
巨人的肩膀
[1] 系统性能调优必知必会.陶辉.极客时间.
[2] https://blog.cloudflare.com/syn-packet-handling-in-the-wild
【3】https://mp.weixin.qq.com/s/2qN0ulyBtO2I67NB_RnJbg