记一次TIME_WAIT过多导致的服务异常

前言

前几天萌新遇到了一个问题,客户购买了我们的产品以后,发现使用过程中QPS一高,就有报错,虽然萌新也没遇到过类似的问题,但是客户反馈了问题萌新也只能一脸懵逼的去排查


排查问题

使用jmeter对接口进行压测的过程中,发现有很多报错
在这里插入图片描述

由于接口返回的报错内容很不明显,看不出问题,只能看服务的日志,然后发现日志里提示连不上mysql。

于是萌新登录mysql看一下连接数,发现居然是214,感觉有点小呀。
在这里插入图片描述

看一下配置文件中配置了多少,咦,里面配置了1000为啥mysql看起来只有214个连接呢,萌新想起来之前遇到过没有修改open files导致连接数只能为214个

比较新的MySQL 5.7的连接数好像不会被open files限制,不过具体萌新也没测试,反正修改open files是个好习惯,防止发生奇怪的问题,导致自己又得排查半天

在这里插入图片描述

ulimit -a看一下果然发现open files数没有修改
在这里插入图片描述

于是修改open files的数量

vim /etc/security/limits.conf

*                soft    nofile          65535
*                hard    nofile          65535

重新登录shell后确认已经修改成功
在这里插入图片描述

重启mysql后,发现max_connections确实已经是1000了
在这里插入图片描述

此时萌新以为已经解决问题了,然后再进行压测,发现服务的日志里还有有报错,依然是提示连不上数据库,这是咋回事呢

然后使用ss命令查看,发现有两万多个连接
在这里插入图片描述

统计一下都是什么连接看看,可以看到有2万多个TIME_WAT连接
在这里插入图片描述

为什么会有这么多TIME-WAIT呢?

我们先来回忆一下TCP四次挥手的过程

在这里插入图片描述

客户端: “(FIN标志)我希望断开连接,我的seq是x,你如果同意就回复我一下Ack = x + 1,自己进入FIN_wait_1状态”
服务器: “知道了,断开吧。Ack = x + 1,自己进入CLOSE_WAIT状态,客户端收到后进入FIN_WAIT_2状态”
服务器: “我这边也想断开连接。我的seq是y,Ack是x+1,你要同意就回复我一下ACK = y + 1,自己进入LAST_ACK状态,客户端收到后进入TIME_WAIT状态”
客户端: “知道了,断开吧。Ack = y + 1,此时需要在TIME_WAIT状态上等待2MSL后才能进入CLOSED状态,而服务器可以直接进入CLOSED状态”

可以看到,TIME_WAIT只会出现在主动断开的一方,被动断开的一方在经历4次挥手后可以直接进入CLOSED状态,而主动断开的一方则需要进入TIME_WAIT状态。

TIME_WAIT状态也称为2MSL等待状态,该状态中,TCP将会等待两倍于最大段生存期(Maximum Segment Lifetime, MSL)时间,有时也被称作加倍等待。[RFC0793]将最大生存期设置为2分钟,然而在常见的实现中,最大段生存期的数值可以为30秒、1分钟或者2分钟。

Centos 7中TIME_WAIT时间理论上是60s,不过萌新自己测试的TIME_WAIT时间大概是55-56秒。

那么究竟为什么主动断开的一方需要进入TIME_WAIT并等待2MSL的时间呢?

有两个原因

  • 阻止延迟数据段: 防止延迟的数据段被其他使用相同源地址、源端口、目的地址以及目的端口的TCP连接收到。
  • 保证连接被关闭: 保证被动关闭TCP连接的一方收到第4次挥手的ACK消息

下面我们分别来看一下上面两个原因

阻止延迟数据段

假如说没有TIME_WAIT,客户端和服务器经过3次握手后,在正常的传输数据,此时服务器给客户端发送了一个数据包,可是因为某些原因没有及时的到达客户端,客户端发现没有收到后让服务器重传了一份,当客户端与服务器交互完毕后,进行4次挥手,两边都直接进入CLOSED状态,关闭了连接。

此时客户端又向服务器发送了一次请求,恰好,客户端还是用的上次那个port与服务器建立的连接,三次握手过后,突然客户端收到了上一次连接中没有收到的数据包,这时候可能就会有问题。

而有了TIME_WAIT,只要保证TIME_WAIT的时间比数据包的生存时间长,那就没问题。


保证连接被关闭

假如没有TIME_WAIT,客户端与服务器交互完数据后,客户端主动进行4次挥手,恰好第四次挥手,服务器端没有收到这个ACK,但是客户端也不知道,于是就进行了CLOSE,可是这时候服务器还处于LAST ACK状态。
这时候客户端又想向服务器进行数据请求了,恰好启动的是上次的的port,客户端向服务器发送SYN包,进行第一次握手,可是服务器端还处于LAST ACK的状态,于是就认为不应该收到SYN包,就向客户端回了一个RST包,这时候客户端不正常了,无法与服务器建立连接。

看到这里我们就明白了,其实由于这个模块没有使用数据库连接池,每次连接数据库查询都是短连接,需要经过三次握手、四次挥手,挥手后会产生大量的TIME-WAIT将连接数占满,最后导致无法产生新的连接了。

面对这个问题,我们有以下几种解决方式

  1. 增大本地端口的范围net.ipv4.ip_local_port_range,这样可以支持更多的连接。
  2. 开启端口复用net.ipv4.tcp_tw_reuse,可以复用TIME_WAIT状态的连接(依然要经历正常的三次握手)
  3. 开启net.ipv4.tcp_tw_recycle快速回收
  4. 通过net.ipv4.tcp_max_tw_buckets参数限制TIME_WAIT的数量

开始解决问题

下面我们想一下应该如何修改配置,来解决我们的问题

首先先把net.ipv4.tcp_tw_recycle给排除掉,这个参数太可怕了,在NAT环境中可能造成造成大量丢包,Linux官方也不推荐使用该参数,并且已经在4.12内核中被移除了。

如果仅仅增大net.ipv4.ip_local_port_range,例如将这个值修改为10000-61000,那也才5万多个连接,TIME_WAIT时间60s,相当于1分钟内最多可以创建5万多个连接,每秒才800多个,这也不够,所以我们需要再配合net.ipv4.tcp_tw_reuse或者net.ipv4.tcp_max_tw_buckets参数

方案1

配置 net.ipv4.ip_local_port_range + net.ipv4.tcp_tw_reuse

要使用reuse必须开启net.ipv4.tcp_timestamps
关于这两个参数,可以看萌新之前的两篇文章
net.ipv4.ip_local_port_range
net.ipv4.tcp_tw_reuse

vim /etc/sysctl.conf

net.ipv4.ip_local_port_range = 10000  61000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1 

让参数立即生效

sysctl -p

然后继续使用jmeter压测
在这里插入图片描述

可以看到现在QPS大概1600多,并且已经没有报错了

再看一下TCP连接数,虽然有5w多的TIME_WAIT,但是已经不会报错了
在这里插入图片描述

方案2

配置 net.ipv4.ip_local_port_range + net.ipv4.tcp_max_tw_buckets

vi /etc/sysctl.conf

net.ipv4.ip_local_port_range = 10000  61000
# 关闭reuse
net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_max_tw_buckets = 50000
net.ipv4.tcp_timestamps = 1

让参数立即生效

sysctl -p

继续使用jmeter压测
在这里插入图片描述

发现这种方式也不报错,但是QPS是1300多,比上一种方式少了3-4千,萌新又测试了几次,发现还是这样

TIME-WAIT的数量也不多,看起来reuse 的性能比较好一点
在这里插入图片描述

总结

这种需要频繁访问某个端口的场景应该尽量使用长链接,不然当QPS比较高的时候,主动挥手的一方就会遇到大量TIME-WAIT,并且频繁的三次握手四次挥手的过程也会浪费一些性能。

如果确实只能使用短连接,需要解决TIME-WAIT过多的问题,建议使用net.ipv4.ip_local_port_range + net.ipv4.tcp_tw_reuse两个参数配合解决,比修改net.ipv4.tcp_max_tw_buckets性能会高一些

但是具体为什么reuse会比限制buckets性能高,萌新也没有太多分析网络问题的经验,所以通过抓包也没找出问题所在,只能看出修改buckets后的PPS相对于修改reuse波动更大一些。

萌新已经把抓到的包放到百度云盘里了,要是有感兴趣的大佬能帮萌新看一下,萌新感激不尽。由于服务模块比较多,要是有帮萌新看的大佬可以私信萌新要一下拓扑图,以及各个服务的端口号。
链接: https://pan.baidu.com/s/1wZeev-nDI0rKxuJO_D_aBA 提取码: 3u0q

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值