连接|TCP半连接队列和全连接队列满了,怎么破


 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

作者|小林coding来源|小林coding
责编|王晓曼
前言
网上许多博客针对增大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函数时把连接取出来。

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

半连接队列与全连接队列不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,内核会直接丢弃,或返回RST包。
实战-TCP全连接队列溢出
1、如何知道应用程序的TCP全连接队列大小?
在服务端可以使用ss命令,来查看TCP全连接队列的情况:
但需要注意的是ss命令获取的Recv-Q/Send-Q在「LISTEN状态」和「非LISTEN状态」所表达的含义是不同的。从下面的内核代码可以看出区别:

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

在「LISTEN状态」时,Recv-Q/Send-Q表示的含义如下:Recv-Q:当前全连接队列的大小,也就是当前已完成三次握手并等待服务端accept()的TCP连接个数;
Send-Q:当前全连接最大队列长度,上面的输出结果说明监听8088端口的TCP服务进程,最大全连接长度为128;
在「非LISTEN状态」时,Recv-Q/Send-Q表示的含义如下:
Recv-Q:已收到但未被应用进程读取的字节数;
Send-Q:已发送但未收到确认的字节数;
2、如何模拟TCP全连接队列溢出的场景?

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

测试环境实验环境:
客户端和服务端都是CentOs6.5,Linux内核版本2.6.32
服务端IP192.168.3.200,客户端IP192.168.3.100
服务端是Nginx服务,端口为8088
这里先介绍下wrk工具,它是一款简单的HTTP压测工具,它能够在单机多核CPU的条件下,使用系统自带的高性能I/O机制,通过多线程和事件模式,对目标机器产生大量的负载。
本次模拟实验就使用wrk工具来压力测试服务端,发起大量的请求,一起看看服务端TCP全连接队列满了会发生什么?有什么观察指标?
客户端执行wrk命令对服务端发起压力测试,并发3万个连接:

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

在服务端可以使用ss命令,来查看当前TCP全连接队列的情况:
 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

其间共执行了两次ss命令,从上面的输出结果,可以发现当前TCP全连接队列上升到了129大小,超过了最大TCP全连接队列。当超过了TCP最大全连接队列,服务端则会丢掉后续进来的TCP连接,丢掉的TCP连接的个数会被统计起来,我们可以使用netstat-s命令来查看:

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

上面看到的41150times,表示全连接队列溢出的次数,注意这个是累计值。可以隔几秒钟执行下,如果这个数字一直在增加的话肯定全连接队列偶尔满了。从上面的模拟结果,可以得知,当服务端并发处理大量请求时,如果TCP全连接队列过小,就容易溢出。发生TCP全连接队溢出的时候,后续的请求就会被丢弃,这样就会出现服务端请求数量上不去的现象。

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

全连接队列溢出3、全连接队列满了,就只会丢弃连接吗?


推荐阅读