- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
大家好,我是小富~ 。
个人资源分享网站: FIRE 。
本文收录在 Springboot-Notebook 面试锦集 。
之前有个小伙伴在技术交流群里咨询过一个问题,我当时还给提供了点排查思路,是个典型的八股文转实战分析的案例,我觉得挺有意思,趁着中午休息简单整理出来和大家分享下,有不严谨的地方欢迎大家指出.
我们先来看看他的问题,下边是他在群里对这个问题的描述,我大致的总结了一下.
他们有很多的 IOT 设备与服务端建立连接,当增加设备并发请求变多, TCP 连接数在接近1024个时,可用 TCP 连接数会降到200左右并且无法建立新连接,而且分析应用服务的GC和内存情况均未发现异常.
从他的描述中我提取了几个关键值, 1024 、 200 、 无法建立新连接 .
看到这几个数值,直觉告诉我大概率是TCP请求溢出了,我给的建议是先直接调大 全连接队列 和 半连接队列 的阀值试一下效果.
那为什么我会给出这个建议?
半连接队列和全连接队列又是个啥玩意?
弄明白这些回顾下TCP的三次握手流程,一切就迎刃而解了~ 。
TCP三次握手,熟悉吧,面试八股里经常全文背诵的题目.
话不多说先上一张图,看明白TCP连接的整个过程.
第一步:客户端发起 SYN_SEND 连接请求,服务端收到客户端发起的 SYN 请求后,会先将连接请求放入半连接队列; 。
第二步:服务端向客户端响应 SYN+ACK ; 。
第三步:客户端会返回 ACK 确认,服务端收到第三次握手的 ACK 后标识连接成功。如果这时全连接队列没满,内核会把连接从半连接队列移除,创建新的连接并将其添加到全连接队列,等待客户端调用 accept() 方法将连接取出来使用; 。
TCP协议三次握手的过程, Linux 内核维护了两个队列, SYN 半连接队列和 accepet 全连接队列。即然叫队列,那就存在队列被压满的时候,这种情况我们称之为 队列溢出 .
当半连接队列或全连接队列满了时,服务器都无法接收新的连接请求,从而导致客户端无法建立连接.
全连接队列溢出时,首先要查看全连接队列的状态,服务端通常使用 ss 命令即可查看, ss 命令获取的数据又分为 LISTEN 状态 和 非LISTEN 两种状态下,通常只看 LISTEN 状态数据就可以.
LISTEN 状态 。
Recv-Q:当前全连接队列的大小,表示上图中已完成三次握手等待可用的 TCP 连接个数; 。
Send-Q:全连接最大队列长度,如上监听8888端口的TCP连接最大全连接长度为128; 。
# -l 显示正在Listener 的socket
# -n 不解析服务名称
# -t 只显示tcp
[root@VM-4-14-centos ~]# ss -lnt | grep 8888
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 100 :::8888 :::*
非LISTEN 状态下Recv-Q、Send-Q字段含义有所不同 。
Recv-Q:已收到但未被应用进程读取的字节数; 。
Send-Q:已发送但未收到确认的字节数; 。
# -n 不解析服务名称
# -t 只显示tcp
[root@VM-4-14-centos ~]# ss -nt | grep 8888
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 100 :::8888 :::*
一般在请求量过大,全连接队列设置过小会发生全连接队列溢出,也就是 LISTEN 状态下 Send-Q < Recv-Q 的情况。 接收到的请求数大于TCP全连接队列的最大长度,后续的请求将被服务端丢弃,客户端无法创建新连接 .
# -l 显示正在Listener 的socket
# -n 不解析服务名称
# -t 只显示tcp
[root@VM-4-14-centos ~]# ss -lnt | grep 8888
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 200 100 :::8888 :::*
如果发生了全连接队列溢出,我们可以通过 netstat -s 命令查询溢出的累计次数,若这个 times 持续的增长,那就说明正在发生溢出.
[root@VM-4-14-centos ~]# netstat -s | grep overflowed
7102 times the listen queue of a socket overflowed #全连接队列溢出的次数
在全连接队列已满的情况,Linux提供了不同的策略去处理后续的请求,默认是直接丢弃,也可以通过 tcp_abort_on_overflow 配置来更改策略,其值 0 和 1 表示不同的策略,默认配置 0.
# 查看策略
[root@VM-4-14-centos ~]# cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0
tcp_abort_on_overflow = 0 :全连接队列已满时,服务端直接丢弃客户端发送的 ACK ,此时服务端仍然是 SYN_RCVD 状态,在该状态下服务端会重试几次向客户端推送 SYN + ACK .
重试次数取决于 tcp_synack_retries 配置,重试次数超过此配置后后,服务端不在重传,此时客户端发送数据,服务端直接向客户端回复 RST 复位报文,告知客户端本次建立连接已失败.
RST : 连接 reset 重置消息,用于连接的异常关闭。常用场景例如:服务端接收不存在端口的连接请求;客户端或者服务端异常,无法继续正常的连接处理,发送 RST 终止连接操作;长期未收到对方确认报文,经过一定时间或者重传尝试后,发送 RST 终止连接.
[root@VM-4-14-centos ~]# cat /proc/sys/net/ipv4/tcp_synack_retries
0
tcp_abort_on_overflow = 1 :全连接队列已满时,服务端直接丢弃客户端发送的 ACK ,直接向客户端回复 RST 复位报文,告知客户端本次连接终止,客户端会报错提示 connection reset by peer .
解决全连接队列溢出我们可以通过调整TCP参数来控制全连接队列的大小,全连接队列的大小取决于 backlog 和 somaxconn 两个参数.
这里需要注意一下,两个参数要同时调整,因为取的两者中最小值 min(backlog,somaxconn) ,经常发生只挑调大其中一个另一个值很小导致不生效的情况.
backlog 是在 socket 创建的时候 Listen() 函数传入的参数 ,例如我们也可以在 Nginx 配置中指定 backlog 的大小.
server {
listen 8888 default backlog = 200
server_name fire100.top
.....
}
somaxconn 是个 OS 级别的参数,默认值是 128,可以通过修改 net.core.somaxconn 配置.
[root@localhost core]# sysctl -a | grep net.core.somaxconn
net.core.somaxconn = 128
[root@localhost core]# sysctl -w net.core.somaxconn=1024
net.core.somaxconn = 1024
[root@localhost core]# sysctl -a | grep net.core.somaxconn
net.core.somaxconn = 1024
如果服务端处理请求的速度跟不上连接请求的到达速度,队列可能会被快速填满,导致连接超时或丢失。应该及时增加队列大小,以避免连接请求被拒绝或超时.
增大该参数的值虽然可以增加队列的容量,但是也会占用更多的内存资源。一般来说, 建议将全连接队列的大小设置为服务器处理能力的两倍左右 .
上边TCP三次握手过程中,我们知道服务端 SYN_RECV 状态的TCP连接存放在半连接队列,所以直接执行如下命令查看半连接队列长度.
[root@VM-4-14-centos ~] netstat -natp | grep SYN_RECV | wc -l
1111
半连接队列溢出最常见的场景就是,客户端没有及时向服务端回 ACK ,使得服务端有大量处于 SYN_RECV 状态的连接,导致半连接队列被占满,得不到 ACK 响应半连接队列中的 TCP 连接无法移动全连接队列,以至于后续的 SYN 请求无法创建。 这也是一种常见的DDos攻击方式.
查看TCP半连接队列溢出情况,可以执行 netstat -s 命令, SYNs to LISTEN 前的数值表示溢出的次数,如果反复查询几次数值持续增加,那就说明半连接队列正在溢出.
[root@VM-4-14-centos ~]# netstat -s | egrep “listen|LISTEN”
1606 times the listen queue of a socket overflowed
1606 SYNs to LISTEN sockets ignored
可以修改 Linux 内核配置 /proc/sys/net/ipv4/tcp_max_syn_backlog 来调大半连接队列长度.
[root@VM-4-14-centos ~]# echo 2048 > /proc/sys/net/ipv4/tcp_max_syn_backlog
看完上边对两个队列的粗略介绍,相信大家也能大致明白,为啥我会直接建议他去调大队列了.
因为从他的描述中提到了两个关键值,TCP连接数增加至1024个时,可用连接数会降至200以内,一般 centos 系统全连接队列长度一般默认 128,半连接队列默认长度 1024。所以队列溢出可以作为第一嫌疑对象.
全连接队列默认大小 128 。
[root@localhost core]# sysctl -a | grep net.core.somaxconn
net.core.somaxconn = 128
半连接队列默认大小 1024 。
[root@iZ2ze3ifc44ezdiif8jhf7Z ~]# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
1024
简单分享了一点TCP全连接队列、半连接队列的相关内容,讲的比较浅显,如果有不严谨的地方欢迎留言指正,毕竟还是个老菜鸟.
全连接队列、半连接队列溢出是比较常见,但又容易被忽视的问题,往往上线会遗忘这两个配置,一旦发生溢出,从 CPU 、 线程状态 、 内存 看起来都比较正常,偏偏连接数上不去.
定期对系统压测是可以暴露出更多问题的,不过话又说回来,就像我和小伙伴聊的一样,即便测试环境程序跑的在稳定,到了线上环境也总会出现各种奇奇怪怪的问题.
我是小富,下期见~ 。
技术交流,欢迎关注公众号:程序员小富 。
最后此篇关于TCP三次握手,给我长脸了噢的文章就讲到这里了,如果你想了解更多关于TCP三次握手,给我长脸了噢的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
当我测试我的网站性能时,我注意到 SSL 握手是连接设置的一部分。我了解(页面的)第一个请求需要完整的 SSL 握手。 但是,如果您从 pingdom 测试中注意到,只有某些其他资源在进行 SSL 握
在 SSL 握手期间,浏览器会根据需要使用提供的 URL 从主机 Web 服务器下载任何中间证书。我相信浏览器附带来自公共(public) CA 的预安装证书,这些证书只有根证书的公钥。 1) 当使用
在配置了客户端身份验证的 TLS 握手中,有一个步骤,服务器接收客户端的证书并选择是否信任它(例如,在 Java 中,它是通过 TrustManager 完成的)。 我想知道来自服务器的最终“信任失败
我正在尝试了解 Android 与服务器的 TLS 连接。有人可以纠正我吗? 有两种启动 TLS 连接的方式。首先,只有服务器有证书,客户端决定是否信任它。其次,客户端和服务器都获得了证书。我说得对吗
我正在创建一个社交网站,我希望用户在其中聊天并接收实时通知,例如 Facebook,我尝试搜索可能的解决方案并找到了 ejabberd 的 pubsub 模块(我正在使用 ejabberd 进行聊天)
我正在编写一个应用程序来(非正式地)替换在 adobe air 中制作的客户端,他们使用 RTMP 作为连接协议(protocol),我必须创建自己的类来实现它:< 据我所知,RTMP 属于 TCP
我正在做一个关于 TLS 握手的学术项目,我已经捕获了由多个客户端(谷歌浏览器、Firefox ......)生成的一些 TLS 流量,我想看看对于给定的浏览器,客户端 hello 消息是否总是相同的
我使用 openssl 实现了一个 DTLS 服务器。 (我有一个 udp 套接字,我正在使用内存 bio 与 openssl 通信。)但是,如果丢包,DTLS 握手可能需要 1-2 秒,这在我的情况
我编写了一个 PHP 程序来执行包含 openssl 命令的批处理文件: openssl s_client -showcerts -connect google.com:443 >test.cert
我编写了一个 PHP 程序来执行包含 openssl 命令的批处理文件: openssl s_client -showcerts -connect google.com:443 >test.cert
客户: var socket = new WebSocket('ws://localhost:8183/websession'); socket.onopen =
当有这么多证书时,浏览器如何知道在 ssl 握手中的客户端身份验证步骤中将哪个证书发送到服务器。我的意思是它如何识别哪个证书适用于哪个服务器 最佳答案 现在是 CyberMonk 的问题 如果您在
我正在尝试使用 python 连接到 XMPP 服务器。我有要连接的 XML,只是不确定如何进行连接的 TLS 部分?我可以找到很多 HTTPS TLS 示例和 XMPP 示例,只是不知道如何将两者放
我需要在我的 Python 服务器中实现 Websocket 握手。我的 python 服务器正在使用 Twisted 进行事件处理。我找到了 this webpage这解释了这个过程,但是当涉及到这
是否可以在当前 SSL 连接保持事件状态时重新协商 SSL 握手。当新的握手成功时,服务器应响应新握手的确认。 我搜索过 SSL 重新协商,但找不到任何具体内容。有谁知道这样的事情是否可能? 最佳答案
我编写了一个客户端-服务器应用程序,旨在通过局域网交换文件(以及其他内容)。在服务器模式下,应用程序监听具有特定标识 header 的 TCP 连接。在客户端模式下,它会尝试与用户提供的 IP 地址建
我有两台服务器通过 SSL 进行通信。 server1 通过 SSL 启动到 server2 的 SSL 连接。服务器 1 有一个 key 大小为 1k 的 keystore ,而服务器 2 有一个
架构是中间层 Liberty 服务器,它接收 http 请求和代理到各种后端,一些是 REST,一些只是 JSON。当我为 SSL 配置时(仅通过非常酷的 envVars)......似乎我得到了每个
我需要一些关于 TLS 的解释:每次客户端想要将自己连接到服务器时是否都执行 TLS 握手?每次都重新创建 session key ? premasterkey 和 masterkey 也是吗?Cli
我正在尝试将 TimeoutMixin 合并到基于 SSL 的协议(protocol)中。但是,当超时发生并且它调用 transport.loseConnection() 时,什么也没有发生。我认为这
我是一名优秀的程序员,十分优秀!