一:摘要概述
客户端与服务端创建连接的过程称之为三次握手,三次握手过程中状态的变更以及数据信息的交互在系列文章TCP(二) -- 三次握手中已经详细阐述。但是是否思考过一个问题,服务端如何处理SYN、ACK后的连接
二:连接队列
syns queue
:半连接队列,存储SYN后的连接accept queue
:全连接队列,存储ACK后的连接
当客户端Client发送SYN包给服务端时,服务端会将连接信息存储到半连接队列,服务端回复SYN+ACK。客户端获取SYN+ACK数据包处理并回复ACK,服务端获取到客户端ACK后由半连接队列中取出连接信息处理存储到全连接队列。全连接队列中的连接就可以等待应用程序使用取出
三:连接控制
连接队列大小肯定不可能无限大,控制连接队列大小就是需要关注的一个核心问题,这关系着应用可承载压力。两个核心的数值如下:
backlog
:程序控制的参数,指定连接队列大小。Java默认50、Nginx/Redis等默认511somaxconn
:系统参数,CentOS默认数值128
backlog调用函数accept()时可以指定,但是系统不会直接选取该值作为连接队列大小。而是会根据系统参数somaxconn结合考虑。其选取标准为min(backlog,somaxconn)
,如下图所示,通过Tomcat发布的应用,其中Tomcat设置连接数量参数为800,但是最后查询显示也仅仅是128。128是系统参数somaxconn的默认值,修改路径为/pro/sys/net/core/somaxconn
四:连接策略
当连接队列充满时有新的连接进入,这时候服务端会如何进行处理?这个策略由参数/proc/sys/net/ipv4/tcp_abort_on_overflow
控制。其默认值为0,具体的策略如下描述:
0
:全连接队列溢出,服务端Server扔掉客户端Client发送的数据包信息1
:全连接队列溢出,服务端发送RST包到客户端Client强制断开连接
当参数设定为1的时候服务端会发送RST包到客户端,所以对于客户端来讲至少是知道连接失败的信息。但是当参数值设置为0的时候客户端并不知道连接失败,Client判断为连接成功处于ESTABLISHED
状态会向服务端发送数据,如下图所示:
/proc/sys/net/ipv4/tcp_retries2
控制。具体的超时重传策略将会在后续文章中进行解释。对于客户端的操作已经比较清晰的情况下可以思考服务端的操作,服务端此时认定连接并未成功,操作如下图所示:
服务端会重走三次握手的第二步,向客户端传输SYN+ACK
五:泄漏排查
进行压测的过程中如果遇到间断性连接获取不到,亦或是出现连接无响应的情况。但是查看机器的CPU等数据参数状况正常,压力还是上不去,注意了这时候你就需要排查连接队列是否存在溢出的情况
[root@bogon ~]# netstat -s | egrep "listen|LISTEN"
4972416 times the listen queue of a socket overflowed
4972416 SYNs to LISTEN sockets ignored
上述命令可以查看到服务器连接队列情况,如果该数据持续上升就可以印证存在队列溢出的情况,就需要调整相关参数
[root@bogon ~]# ss -s
Total: 4759 (kernel 4831)
TCP: 4533 (estab 4087, closed 204, orphaned 6, synrecv 0, timewait 32/0), ports 2460
Transport Total IP IPv6
* 4831 - -
RAW 0 0 0
UDP 8 5 3
TCP 4329 12 4317
INET 4337 17 4320
FRAG 0 0 0
使用ss -s命令可以查看到具体的服务器上连接信息,当然ss命令还可以查看很多内容。常用参数作用如下:
序列编码 | 参数名称 | 参数作用 |
---|---|---|
1 | s | 查询概要情况 |
2 | a | 显示所有套接字 |
3 | l | LISTEN 状态 |
4 | t | 查询TCP连接 |
5 | u | 查询UDP连接 |
6 | n | 不要翻译服务器名称 |
[root@bogon ~]# ss -alnt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 100 :::15345 :::*
LISTEN 0 1 ::ffff:127.0.0.1:15217 :::*
其中Send-Q
参数表示全连接队列的大小,Recv-Q
表示全连接队列使用的数量