TCP 三次握手,给我长脸了噢
大家好,我是小富~
个人资源分享网站:FIRE
本文收录在 Springboot-Notebook 面试锦集
前言
之前有个小伙伴在技术交流群里咨询过一个问题,我当时还给提供了点排查思路,是个典型的八股文转实战分析的案例,我觉得挺有意思,趁着中午休息简单整理出来和大家分享下,有不严谨的地方欢迎大家指出。
问题分析
我们先来看看他的问题,下边是他在群里对这个问题的描述,我大致的总结了一下。
他们有很多的 IOT 设备与服务端建立连接,当增加设备并发请求变多,TCP
连接数在接近1024个时,可用TCP
连接数会降到200左右并且无法建立新连接,而且分析应用服务的GC和内存情况均未发现异常。
从他的描述中我提取了几个关键值,1024
、200
、无法建立新连接
。
看到这几个数值,直觉告诉我大概率是TCP请求溢出了,我给的建议是先直接调大全连接队列
和半连接队列
的阀值试一下效果。
那为什么我会给出这个建议?
半连接队列和全连接队列又是个啥玩意?
弄明白这些回顾下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三次握手/四次挥手详解
一. TCP/IP协议族 TCP/IP是一个协议族,通常分不同层次进行开发,每个层次负责不同的通信功能.包含以下四个层次: 1. 链路层,也称作数据链路层或者网络接口层,通常包括操作系统中的设备驱动程 ...
- wireshark抓包工具简介以及tcp三次握手的一些含义
wireshark是非常流行的网络封包分析软件,功能十分强大.可以截取各种网络封包,显示网络封包的详细信息.使用wireshark的人必须了解网络协议,否则就看不懂wireshark了.为了安全考虑, ...
- TCP三次握手四次挥手
看到一篇总结很好的TCP三次握手,学习一下,原文链接. 建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 先来看看如何建立连接的. 首先Client端发送连接请求报文,S ...
- TCP ,UDP概念和TCP三次握手连接 的知识点总结
OSI 计算机网络7层模型 TCP/IP四层网络模型 传输层提供应用间的逻辑通信(端到端),网络层提供的是主机到主机的通信,传输层提供的是可靠服务. TCP 中常说的握手指的是:连接的定义和连接的建立 ...
- Wireshark基本介绍和学习TCP三次握手
wireshark介绍 wireshark的官方下载网站: http://www.wireshark.org/ wireshark是非常流行的网络封包分析软件,功能十分强大.可以截取各种网络封包,显示 ...
- TCP 三次握手四次挥手, ack 报文的大小.tcp和udp的不同之处、tcp如何保证可靠的、tcp滑动窗口解释
一.TCP三次握手和四次挥手,ACK报文的大小 首先连接需要三次握手,释放连接需要四次挥手 然后看一下连接的具体请求: [注意]中断连接端可以是Client端,也可以是Server端. [注意] 在T ...
- iOS 开发:TCP三次握手连接
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认: 第二次握 ...
- 用tcpdump分析tcp三次握手,四次挥手
1.tcpdump 简介 tcpdump是一个对网络上的数据包进行截获的包分析工具,一般linux系统以命令的形式使用 2.tcp三次握手 建立一个tcp连接会发生下面三个过程: 1.服务器必须准备好 ...
- TCP三次握手,数据传输,四次挥手
TCP包结构 一个TCP包结构如下: 一个TCP包主要由TCP包头和数据部分组成,包头固定部分为20字节,选项和数据部分根据实际情况设置为4N(N可以为0)字节. 1.16bit源端口和目的端口号,它 ...
- TCP三次握手原理详解
TCP/IP协议不是TCP和IP这两个协议的合称,而是指因特网整个TCP/IP协议族. 从协议分层模型方面来讲,TCP/IP由四个层次组成:网络接口层.网络层.传输层.应用层. TCP协议:即传输控制 ...
随机推荐
- CentOS 7.9 环境下部署 Docker 服务
sudo setenforce Permissive sudo vi /etc/selinux/config SELINUX=permissive sudo systemctl stop firewa ...
- plsql链接oracle
安装两个oracle文件夹在一个database中,安装plsql 要先配置两个都要修改,不然会找不到服务器 管理员运行 监听服务/监听位置和数据库服务都要修改 ass文件---监听程序配置 和本场 ...
- IDEA-mybatis逆向工程使用
首先我们需要安装mybatis逆向工程插件mybatis Generator: 然后在pom.xml文件中添加逆向工程插件: <!--mybatis逆向工程插件--> <plugin ...
- 项目实训DAY7
今天与昨天一样,查论文,并美化了一下功能界面的样式.
- C# 数据结构和算法-数组队列
队列: 队列是一个有序列表,遵循先入先出原则,可以用数组或链表实现 使用场景 用于排队,按顺序执行 public static void Main(string[] args) { ArrayQueu ...
- selenium webdriver 无法选中元素,修改元素属性可见
<ul data-v-6529428e="" class="el-dropdown-menu el-popper filter-dropdown el-dropdo ...
- CC2020 分享信息
CC2020是鄙人第一次参与的国际计算教育报告.CC2020报告的特色是希望面向未来的教育能走近每一个教育的利益相关者,包括家长.学生.行业雇主.政府决策制定者和学术界人士.敬请各位从自己的身份角度对 ...
- 无显示器无键盘的树莓派搭建NAS(samba)
使用软件Rufus烧录系统2020-02-13-raspbian-buster.img到TF卡后,在TF卡的文件夹内创建空文件ssh,再创建一个名为wpa_supplicant.conf的文件,内容为 ...
- .Net Core 前后端分离 DevOps 系列
前言: 目录: Linux 安装脚本之乌班图版本 Linux Nginx部署 DevOps安装步骤
- MySQL使用bin-log将数据恢复到某个时间点
binlog的三种模式 statement:记录每一条修改数据的sql row:保存哪条记录被修改 mixed:兼顾前两者的优点. # 查看binlog有没有开启 SHOW VARIABLES LIK ...