您到底有多了解? -阿里巴巴技术团队的最佳做法
任希军是阿里巴巴中间件技术团队的成员。 最近,他遇到了一个客户端通信服务器的问题,该问题一直在引发异常。 但令他沮丧的是,尽管搜寻互联网上的信息并反复尝试查找原因,但他找不到任何有助于解释这两个队列或如何观察其指标的东西。
他无所畏惧,自负不下。 他写了这篇文章来记录他如何识别和解决该问题。
一个烦人的问题
在Java中,客户端和服务器使用套接字进行通信。 在这种情况下,将使用NIO服务器。 发生以下状态:
·间歇执行三向握手以在客户端和服务器之间建立连接,但是侦听套接字没有响应。
·然后,该问题同时在许多其他连接中发生。
·NIO选择器未销毁并重新创建。 使用的始终是第一个。
·启动程序时出现问题,此后间歇出现。
概述:TCP三向握手如何工作?
我做的第一件事是提醒自己建立TCP连接时三向握手的标准过程。 标准过程如下:
1.客户端将SYN数据包发送到服务器以发起握手。
2.收到此消息后,服务器将SYN-ACK数据包发送到客户端。
3.最后,客户端将ACK数据包发送到服务器,以表明它已收到服务器的SYN-ACK数据包。 (到此为止,已经通过客户端的端口56911建立了到服务器的连接。)
快速修复
从对问题的描述来看,这听起来与在建立TCP连接期间TCP完整连接队列(或接受队列,将在后面讨论)充满时类似。 为了确认这一点,我通过netstat -s |检查了队列的溢出统计信息。 egrep“听”。
667399 times the listen queue of a socket overflowed
经过三遍检查,我发现该值在不断增加。 很明显,服务器上的接受队列已溢出。
然后可以看到操作系统如何处理溢出。
# cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0
如果tcp_abort_on_overflow为0,则在三向握手的第三步中,如果接受队列已满,则服务器将丢弃客户端发送的ACK数据包,因为它假定尚未在服务器端建立连接。
为了证明该异常与完整的连接队列有关,我首先将tcp_abort_on_overflow更改为1。如果在第三步中完整的连接队列已满,则服务器将向客户端发送一个重置数据包,指示它应同时结束两个握手过程和连接。 (实际上未在服务器端建立连接。)
然后,我进行了测试,发现客户端中存在许多“对等连接重置”异常。 我们得出的结论是,完整的连接队列的溢出又导致了客户端错误,这有助于我们快速确定问题的关键部分。
开发团队查看了Java源代码,发现套接字的待办事项的默认值为50(此值控制整个连接队列的大小,稍后将进行详细介绍)。 我增加了该值,然后再次运行它,经过12个小时的压力测试后,我注意到该错误不再显示了,并且溢出也没有增加。
因此,就这么简单。 TCP三向握手发生后,连接队列完全溢出,只有进入此队列后,服务器才能从侦听更改为接受。 待办事项的默认值为50,这很容易溢出。 如果溢出,则在握手的第三步,服务器将忽略客户端发送的ACK数据包。 服务器将定期重复第二步(将SYN-ACK数据包发送到客户端)。 如果连接未排队,则会导致异常。
但是,尽管我们解决了问题,但我仍然不满意。 我想将整个遭遇作为学习经验,所以我进一步研究了这个问题。
深入研究:TCP握手过程和队列
如上所示,有两个队列:SYN队列(或不完整的连接队列)和接受队列(或完整的连接队列)。
在三向握手中,服务器从客户端接收到SYN数据包后,将连接信息放入SYN队列中,并将SYN-ACK数据包发送回客户端。
然后,服务器从客户端接收ACK数据包。 如果接受队列未满,则应该从SYN队列中删除信息并将其放入接受队列,或者按照tcp_abort_on_overflow指示执行。
此时,如果接受队列已满且tcp_abort_on_overflow为0,则服务器将在一定时间后再次向客户端发送SYN-ACK数据包(换句话说,它重复握手的第二步)。 如果客户端经历了短暂的超时,则很容易遇到客户端异常。
在我们的操作系统中,第二步默认情况下重试两次(CentOS为5次)。
net.ipv4.tcp_synack_retries = 2
一种新方法
上面详述的解决方案有点令人困惑,您可能想知道是否有更快或更简单的方法来解决这些问题。 让我们先来看一些有用的命令。
指令
netstat –s
[root@server ~]# netstat -s | egrep "listen|LISTEN"
667399 times the listen queue of a socket overflowed
667399 SYNs to LISTEN sockets ignored
在这里,例如667399表示接受队列溢出的次数。 每隔几秒钟执行一次此命令,如果数量增加,则接受队列必须已满。
ss命令
[root@server ~]# ss -lnt
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 50 *:3306 *:*
此处,第二列中的Send-Q值为50,表示侦听端口(第三列)上的接受队列最多为50。 第一列Recv-Q指示当前正在使用的接受队列的数量。
接受队列的大小取决于min(backlog,somaxconn)。 创建套接字时将传递积压,因此somaxconn是操作系统级别的系统参数。
此时,我们可以与我们的代码建立联系。 例如,当Java创建ServerSocket时,它将允许您传递积压的值。
SYN队列的大小取决于max(64,/ proc / sys / net / ipv4 / tcp_max_syn_backlog),不同版本的OS可能不同。
netstat命令
就像ss命令一样,也可以通过netstat命令显示Send-Q和Recv-Q。 但是,如果连接未处于“侦听”状态,则Recv-Q表示接收到的数据仍在高速缓存中,并且尚未被进程读取。 该值表示该进程尚未读取的字节。 发送是发送队列中尚未被远程主机确认的字节数。
$netstat -tn
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp0 0 100.81.180.187:8182 10.183.199.10:15260 SYN_RECV
tcp0 0 100.81.180.187:43511 10.137.67.18:19796 TIME_WAIT
tcp0 0 100.81.180.187:2376 100.81.183.84:42459 ESTABLISHED
重要的是要注意,netstat -tn显示的Recv-Q数据与接受队列或SYN队列无关。 这里必须强调这一点,以免将其与ss -lnt所示的Recv-Q数据混淆。
例如,以下netstat -t可以看到Recv-Q积累了很多数据,这通常是CPU处理失败引起的。
验证程序
要验证上述详细信息,请将Java中的积压值更改为10(值越小,越容易溢出),然后继续运行压力测试。 然后,客户端开始报告异常,然后可以通过服务器上的ss命令观察以下情况。
Fri May 5 13:50:23 CST 2017
Recv-Q Send-QLocal Address:Port Peer Address:Port
11 10 *:3306 *:*
在这里我们可以看到端口3306上的服务接受队列最多为10个,但是队列中现在有11个连接。 必须有一个不能排队并且将溢出的队列。 同时,溢出的值确实不断增加。
在Tomcat和Nginx中接受队列大小
Tomcat默认为临时连接。 在Ali-Tomcat中,待办事项的默认值(在Tomcat中为“接受计数”)为200。在Apache Tomcat中,默认值为100。
#ss -lnt
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 100 *:8080 *:*
在Nginx中,待办事项的默认值为511。
$sudo ss -lnt
State Recv-Q Send-Q Local Address:PortPeer Address:Port
LISTEN 0 511 *:8085 *:*
LISTEN 0 511 *:8085 *:*
Nginx在多进程模式下运行,因此有多个8085,这意味着多个进程都在侦听同一端口,以避免上下文切换并提高性能。
摘要
一旦发生溢出,CPU和线程状态看起来很正常,但是压力并没有增加。 从客户端的角度来看,响应时间(网络+队列+服务时间)很高,但是考虑到服务器日志中的真实服务时间,它实际上很短。 在某些框架(例如JDK和Netty)中,积压的默认值很小,这在某些情况下可能会导致性能问题。
我希望本文能帮助您了解建立TCP连接时SYN队列和accept队列的概念,原理和功能。 接受队列和SYN队列的溢出问题很容易被忽略,但这是至关重要的,尤其是在使用临时连接(例如Nginx和PHP,尽管它们也支持持久连接)的情况下。
(任喜军任喜军的原创文章)
阿里巴巴科技
关于阿里巴巴最新技术的第一手和深入信息→Facebook: “阿里巴巴技术” 。 Twitter: “阿里巴巴技术” 。
参考
http://veithen.github.io/2014/01/01/how-tcp-backlog-works-in-linux.html
http://www.cnblogs.com/zengkefu/p/5606696.html
http://www.cnxct.com/something-about-phpfpm-s-backlog/
http://jaseywang.me/2014/07/20/tcp-queue-%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/
http://jin-yang.github.io/blog/network-synack-queue.html#
http://blog.chinaunix.net/uid-20662820-id-4154399.html
From: https://hackernoon.com/tcp-three-way-handshake-4161eb8aba32