赞
踩
半连接队列是指:用于存储处于 SYN_RECV 状态的连接的队列
当内核收到一个 SYN 报文,就将该连接放到半连接队列,并发送 ACK 给对方
全连接队列是指:用于存储已经建立好 TCP 连接,但还未被应用调用 accept 取走的连接的队列
当内核收到 SYN 报文对应的 ACK 后,就将该连接从半连接队列中取出,并放到全连接队列,等待应用程序调用 accept 取走
虽然都有「队列」二字,但二者 实际上都不是队列
“收到某个连接的 ack 的 ack 后,从半连接队列中取出该连接”的「取出」过程是一个 随机 的过程,因为这个 ack 的顺序并不固定
如果半连接队列设计成线性结构,那么取出对应连接就需要 O(n) 的时间
因此,半连接队列实际上是一个哈希表,取出对应连接的期望时间是 O(1)
而全连接队列实际上是一个链表,当然,也可以理解成队列(毕竟队列也有基于链表实现的)
可以使用 ss -lnt
查看某个全连接队列的大小:
root@SkyLee:~# ss -lnt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 4096 *:1145 *:*
当全连接队列满了以后,如果还有新的连接请求到来,内核会 默认直接丢弃 该连接,这个行为可以通过修改 /proc/sys/net/ipv4/tcp_abort_on_overflow
的值来改变:
root@SkyLee:~# cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0
为啥默认值为 0?直接丢弃,不会占用客户端的资源吗?
设置为 0,有利于提高请求的成功率
进入全连接队列,意味着客户端已经进入 ESTABLISHED 状态
当用户给服务器发请求时,只要服务器不 Response,客户端就会不断重试发送第三次握手的 ACK,只要在重试次数到达上限前,服务器调用了 accept 接收了该连接,就可以处理用户的请求了
因此,除非认为服务器需要很长时间才来得及 accept(超过了重传的上限时间),不要将 tcp_abort_on_overflow
修改成 1
经过上面的分析,可以发现,如果全连接队列的 capacity:
有些时候,在生产环境发现 QPS 上不去,硬件又没吃满,排查不出问题,就可以考虑是不是全连接队列的 capacity 太小了
那么,这个容量应该怎么修改呢?
两个配置:
/proc/sys/net/core/somaxconn
capacity 的值为二者的 最小值
可以使用 netstat -natp | grep SYN_RECV | grep 1145 | wc -l
来查看 1145 端口上的,处于 SYN_RECV 状态的连接数量,间接的查看半连接队列的大小
netstat -natp | grep SYN_RECV | grep 1145 | wc -l
0
目前没有一个命令可以直接查看半连接队列的容量,但是如果想查看的话:
当半连接队列满了以后,如果还有新的连接请求到来,并且 没有启用 syn_cookies,那么,新的连接请求 将会被抛弃
但是,可以通过启用 syn_cookies
,来避免抛弃新的连接请求
syncookies 参数主要有以下三个值:
应对 SYN 攻击,可以将 syn_cookies 设置为 1
root@SkyLee:~# echo 1 > /proc/sys/net/ipv4/tcp_syncookies
首先,是网上经常谈到的 /proc/sys/net/ipv4/tcp_max_syn_backlog
参数
root@SkyLee:~# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
1024
但是,半连接队列的容量在不同的 Linux 版本上,有所不同
下面以内核版本为 2.6.32 的 Linux 为例,说说如何确定半连接队列的容量
第一个因素就是上面提到的 tcp_max_syn_backlog
第二个因素是 全连接队列的容量
是的,全连接队列的容量也会影响半连接队列,具体算法如下:
max_qlen_log = min(somaxconn, listen_backlog) * 2
max_qlen_log = tcp_max_syn_backlog * 2
简单来说,max_qlen_log 的值为 min(全连接队列容量,tcp_max_syn_backlog) * 2
max_qlen_log 的值就是 理论 半连接队列的容量
但实际上,如果 size > tcp_max_syn_backlog - (tcp_max_syn_backlog >> 2)
,连接也会被抛弃
因此,2.6.32 版本的 Linux 实际的 capacity 的值的计算公式 为:
min(tcp_max_syn_backlog - (tcp_max_syn_backlog >> 2), min(tcp_max_syn_backlog, min(somaxconn, listen_backlog)) * 2)
在 2.2 以前的 Linux 中,理论 半连接队列的大小为 listen 中的 backlog
在 5.0 版本的 Linux,理论 半连接队列的大小就是全连接队列的大小
总结一波:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。