关于后端:TCP-三次握手给我长脸了噢

5次阅读

共计 4296 个字符,预计需要花费 11 分钟才能阅读完成。

大家好,我是小富~

集体资源分享网站:FIRE

本文收录在 Springboot-Notebook 面试锦集

前言

之前有个小伙伴在技术交换群里征询过一个问题,我过后还给提供了点排查思路,是个典型的八股文转实战剖析的案例,我感觉挺有意思,趁着中午劳动简略整理出来和大家分享下,有不谨严的中央欢送大家指出。

问题剖析

咱们先来看看他的问题,下边是他在群里对这个问题的形容,我大抵的总结了一下。

他们有很多的 IOT 设施与服务端建设连贯,当减少设施并发申请变多,TCP连接数在靠近 1024 个时,可用 TCP 连接数会降到 200 左右并且无奈建设新连贯,而且剖析应用服务的 GC 和内存状况均未发现异常。

从他的形容中我提取了几个要害值,1024200无奈建设新连贯

看到这几个数值,直觉通知我大概率是 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 线程状态 内存 看起来都比拟失常,偏偏连接数上不去。

定期对系统压测是能够暴露出更多问题的,不过话又说回来,就像我和小伙伴聊的一样,即使测试环境程序跑的在稳固,到了线上环境也总会呈现各种奇奇怪怪的问题。

我是小富,下期见~

技术交换,欢送关注公众号:程序员小富

正文完
 0