
”
我最近遇到了一个问题. 连接到服务器时,客户端始终会引发异常. 经过反复定位和分析,并查阅了各种材料以供理解,我发现没有一篇文章可以清楚地解释这两个队列以及如何观察其指标.

因此,我写了这篇文章,希望将其弄清楚. 欢迎大家一起讨论和讨论.
问题描述
方案: 使用Socket通信的Java客户端和服务器. 服务器使用NIO.
问题:
分析问题

建立普通TCP连接的三步握手过程分为以下三个步骤:
从问题的描述看,TCP建立连接时有点像完整的连接队列(接受队列,稍后详细介绍)已满.
特别是症状2和4为了证明这一点,请立即通过netstat -s |检查队列的溢出统计信息. egrep“听”:

多次阅读后,我发现这种溢出一直在增加,很明显,服务器上的完整连接队列肯定已经溢出.
下一步,检查操作系统如何处理溢出:


tcp_abort_on_overflow为0表示如果在三向握手的第三步中完整连接队列已满,服务器将丢弃客户端发送的Ack(服务器认为尚未建立连接). / p>
为了证明客户端应用程序代码的异常与完整的连接队列有关tcp三次握手超时,我首先将tcp_abort_on_overflow修改为1.
1表示在第三步中,如果完整的连接队列已满,服务器将向客户端发送一个Reset数据包,指示握手过程和连接被丢弃(服务器端尚未建立连接) ).
下一个测试,您可以看到很多由于客户端异常中的对等错误而导致的连接重置. 这证明了客户错误是由这个原因引起的(逻辑上很严格,问题的关键点很快得到了证明. )
因此,开发人员查看了Java源代码,发现Socket的默认积压(此值控制完整连接队列的大小,这将在后面进行详细介绍)为50.
因此,我重新运行并重新运行. 经过超过12小时的压力测试,一次没有发生此错误,并且观察到溢出不再增加.
此问题已解决. 简而言之,TCP三向握手之后有一个“接受”队列. 仅当您输入此队列时,才能从“侦听”更改为“接受”. 默认积压值为50,很容易填满.
充满后,服务器将忽略客户端在握手的第三步期间发送的Ack数据包(服务器在一段时间后将握手第二步的Syn + Ack数据包重新发送给客户端),如果此连接尚未排队异常.
但是我们不仅要解决问题,还必须回顾解决过程,哪些知识点缺失或难以理解.
除上述异常信息外,没有更清晰的指示可以检查和确认问题.
深入了解TCP握手过程中建立连接的过程和队列

如上所示,这里有两个队列: Syns队列(半连接队列);接受队列(全连接队列).
在三向握手中,在第一步服务器从客户端接收到Syn之后,服务器将该连接信息放入半连接队列中,并向客户端发送Syn + Ack(步骤2) :

在第三步中,服务器从客户端接收Ack. 如果此时完全连接队列未满,则从半连接队列中取出此连接的信息,并将其放入完全连接队列中. 否则,它将根据tcp_abort_on_overflow的指令执行.

这时,如果完全连接队列已满且tcp_abort_on_overflow为0,则服务器将在一段时间后再次将Syn + Ack发送给客户端(即,再次进行握手的第二步). 如果客户端超时并等待相对较短的时间,则客户端很容易变得异常.
在我们的操作系统中,“重试步骤2”的默认数量为2(默认为Centos为5):

如果TCP连接队列溢出,应该查看哪些指示器?
上面的解决过程有点曲折,听起来很尴尬,那么下次遇到类似问题时,更快又更清晰的方法来确认此问题是什么? (通过特定的情感事物来增强我们对知识的理解和吸收. )
netstat -s

例如,您在上面看到的667399次指示完整连接队列溢出的次数. 如果每隔几秒钟执行一次此操作,则完整的连接队列有时会变满.
ss命令

上面显示的第二列Send-Q值为50,这意味着第三列中侦听端口上的完全连接队列的最大数量为50,第一列Recv-Q为完全连接队列的数量当前正在使用.
完全连接的队列的大小取决于: min(积压,somaxconn). 创建套接字时,将传递积压. Somaxconn是操作系统级别的系统参数.
这时,您可以与我们的代码建立连接. 例如,Java将允许您在创建ServerSocket时传递积压的值:

半连接队列的大小取决于: 最大值(64,/ proc / sys / net / ipv4 / tcp_max_syn_backlog). 不同版本的操作系统会有一些差异.
在编写代码时,我们从来没有想过这个积压订单,或者在大多数情况下,我们没有给它一个值(当时的默认值为50),而是直接忽略了它.

首先,这是知识点的盲点. 其次,有一天您可能会在文章中看到此参数. 当时,我有点印象深刻,但过了一会儿我就忘记了. 这是因为知识之间没有联系,而不是系统之间没有联系. 变成了.
但是,如果像我一样,您首先经历了这个问题的痛苦,那么您就会发现自己为什么承受压力和痛苦.
同时,您可以理解为什么要从代码层推断到OS层. 这样您就可以更好地掌握这一知识点,它将成为您的知识系统随着TCP或性能的增长而增长的有力武器.
netstat命令
netstat可以像ss命令一样查看Send-Q和Recv-Q的状态信息,但是如果连接未处于“侦听”状态,则Recv-Q表示接收到的数据仍在高速缓存中并且尚未处理读取,该值是进程尚未读取的字节.
Send是远程主机尚未确认的发送队列中的字节数,如下所示:

netstat -tn看到的Recv-Q与完全连接和半连接无关. 在这里,我故意将其列出来,因为它很容易与ss -lnt的Recv-Q混淆. 顺便建立一个知识体系,巩固相关知识点.
例如,如下面的netstat -t所示,Recv-Q具有大量数据积累tcp三次握手超时,这通常是由于CPU处理不足所致:

以上内容是通过一些特定的工具和指标来了解完整的连接队列(一种工程效率的方式).
切实验证以上理解
将Java中的待办事项更改为10(值越小,越容易溢出),并继续承受压力. 此时,客户端开始报告异常,然后在服务器上观察ss命令:

根据先前的了解,这时我们可以看到端口3306上的最大完整服务队列为10.
但是现在队列中有11个人正在等待进入队列. 必须有一个不能进入队列溢出的连接. 同时,可以看出溢出值在不断增加.

在Tomcat和Nginx中接受队列参数
Tomcat默认短连接,积压(Tomcat中的接受计数)Ali-tomcat默认为200,Apache Tomcat默认为100.

Nginx默认为511,如下所示:

因为Nginx是多进程模式,所以我看到了多个8085,也就是说,多个进程在同一个端口上侦听,以避免上下文切换以提高性能.
摘要
完全连接队列和半连接队列溢出很容易忽略,但是它们非常重要,特别是对于某些短连接应用程序(例如Nginx,PHP,它们也支持长连接).
一旦发生溢出,CPU和线程状态看起来很正常,但是压力没有增加. 在客户看来,RT也很高(RT =网络+排队+实际服务时间),但是服务器日志中的真实记录服务时间非常短.
默认情况下,某些框架(例如JDK和Netty)的积压量相对较小,在某些情况下可能导致性能不兼容.
我希望本文可以帮助您了解TCP连接过程中半连接队列和全连接队列的概念,原理和功能. 更关键的是哪些指标可以清楚地看到这些问题(工程效率有助于加强理论理解).
此外,每个特定问题都是学习的最佳机会. 它绝对不够深入,无法通过阅读来理解. 请珍惜每个特定问题,并在遇到问题后能够理解上下文. 每个问题都是您的特定知识. 海关通关的好机会.
最后,我会问相关问题供您考虑:
问这些问题,希望以此知识为起点,让您的知识系统开始自我发展.
参考文章:
作者: 刺剑
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/tongxinshuyu/article-151085-1.html
Amber真像郑网红