关于服务器:终于搞懂了服务器为啥产生大量的TIMEWAIT

43次阅读

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

写在结尾,大略 4 年前,听到运维同学提到 TIME\_WAIT 状态的 TCP 连贯过多的问题,然而过后没有去细推敲;最近又听人说起,是一个老手进行压测过程中,遇到的问题,因而,花点工夫,细深究一下。

从这几个方面着手:

  • 问题形容:什么景象?什么影响?
  • 问题剖析
  • 解决方案
  • 底层原理

问题形容

模仿高并发的场景,会呈现批量的 TIME_WAIT 的 TCP 连贯:

短时间后,所有的 TIME_WAIT 全都隐没,被回收,端口包含服务,均失常。

即,在高并发的场景下,TIME_WAIT 连贯存在,属于失常景象。

线上场景中,继续的高并发场景

  • 一部分 TIME_WAIT 连贯被回收,但新的 TIME_WAIT 连贯产生;
  • 一些极其状况下,会呈现大量的 TIME_WAIT 连贯。

Think:

上述大量的 TIME_WAIT 状态 TCP 连贯,有什么业务上的影响吗?

Nginx 作为反向代理时,大量的短链接,可能导致 Nginx 上的 TCP 连贯处于 time_wait 状态:

  • 每一个 time_wait 状态,都会占用一个「本地端口」,下限为 65535(16 bit,2 Byte);
  • 当大量的连贯处于 time_wait 时,新建设 TCP 连贯会出错,address already in use : connect 异样

统计 TCP 连贯的状态:

// 统计:各种连贯的数量
$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 1154
TIME_WAIT 1645

Tips:TCP 本地端口数量,下限为 65535(6.5w),这是因为 TCP 头部应用 16 bit,存储「端口号」,因而束缚下限为 65535。

问题剖析

大量的 TIME_WAIT 状态 TCP 连贯存在,其本质起因是什么?

  • 大量的短连贯存在
  • 特地是 HTTP 申请中,如果 connection 头部取值被设置为 close 时,根本都由「服务端」发动被动敞开连贯
  • 而,TCP 四次挥手敞开连贯机制中,为了保障 ACK 重发和抛弃提早数据,设置 time_wait 为 2 倍的 MSL(报文最大存活工夫)

TIME_WAIT 状态:

  • TCP 连贯中,被动敞开连贯的一方呈现的状态;(收到 FIN 命令,进入 TIME_WAIT 状态,并返回 ACK 命令)
  • 放弃 2 个 MSL 工夫,即,4 分钟;(MSL 为 2 分钟)

解决办法

解决上述 time_wait 状态大量存在,导致新连贯创立失败的问题,个别解决办法:

  • 客户端,HTTP 申请的头部,connection 设置为 keep-alive,放弃存活一段时间:当初的浏览器,个别都这么进行了
  • 服务器端
  • 容许 time_wait 状态的 socket 被重用
  • 缩减 time_wait 工夫,设置为 1 MSL(即,2 mins)
论断:几个外围要点
  • time_wait 状态的影响:
  • TCP 连贯中,「被动发动敞开连贯」的一端,会进入 time_wait 状态
  • time_wait 状态,默认会继续 2 MSL(报文的最大生存工夫),个别是 2×2 mins
  • time_wait 状态下,TCP 连贯占用的端口,无奈被再次应用
  • TCP 端口数量,下限是 6.5w(65535,16 bit)
  • 大量 time_wait 状态存在,会导致新建 TCP 连贯会出错,address already in use : connect 异样

事实场景

  • 服务器端,个别设置:不容许「被动敞开连贯」
  • 但 HTTP 申请中,http 头部 connection 参数,可能设置为 close,则,服务端解决完申请会被动敞开 TCP 连贯
  • 当初浏览器中,HTTP 申请 connection 参数,个别都设置为 keep-alive
  • Nginx 反向代理场景中,可能呈现大量短链接,服务器端,可能存在
解决办法
  • 服务器端,
  • 容许 time_wait 状态的 socket 被重用
  • 缩减 time_wait 工夫,设置为 1 MSL(即,2 mins)

附录

几个方面:

  • TCP 连贯状态的查问
  • MSL 工夫
  • TCP 三次握手和四次握手
附录 A:查问 TCP 连贯状态

Mac 下,查问 TCP 连贯状态的具体命令:

// Mac 下,查问 TCP 连贯状态
$ netstat -nat |grep TIME_WAIT

// Mac 下,查问 TCP 连贯状态,其中 -E 示意 grep 或的匹配逻辑
$ netstat -nat | grep -E "TIME_WAIT|Local Address"Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp4       0      0  127.0.0.1.1080         127.0.0.1.59061        TIME_WAIT

// 统计:各种连贯的数量

$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'ESTABLISHED 1154TIME_WAIT 1645
附录 B:MSL 工夫

MSL,Maximum Segment Lifetime,“报文最大生存工夫”,

  • 任何报文在网络上存在的最长工夫,超过这个工夫报文将被抛弃。(IP 报文)
  • TCP 报文(segment)是 ip 数据报(datagram)的数据局部。

Tips:

RFC 793 中规定 MSL 为 2 分钟,理论利用中罕用的是 30 秒,1 分钟和 2 分钟等。

2MSL,TCP 的 TIME_WAIT 状态,也称为 2MSL 期待状态:

  • 当 TCP 的一端发动被动敞开(收到 FIN 申请),在收回最初一个 ACK 响应后,即第 3 次握 手实现后,发送了第四次握手的 ACK 包后,就进入了 TIME_WAIT 状态。
  • 必须在此状态上停留两倍的 MSL 工夫,期待 2MSL 工夫次要目标是怕最初一个 ACK 包对方没收到,那么对方在超时后将重发第三次握手的 FIN 包,被动敞开端接到重发的 FIN 包后,能够再发一个 ACK 应答包。
  • 在 TIME_WAIT 状态时,两端的端口不能应用,要等到 2MSL 工夫完结,才可持续应用。(IP 层)
  • 当连贯处于 2MSL 期待阶段时,任何早退的报文段都将被抛弃。

不过在理论利用中,能够通过设置「SO_REUSEADDR 选项」,达到不用期待 2MSL 工夫完结,即可应用被占用的端口。

附录 C:TCP 三次握手和四次握手,具体细节,参考:TCP 的三次握手与四次挥手

具体示意图:

  • 三次握手,建设连贯过程
  • 四次挥手,开释连贯过程

几个外围疑难:

time_wait 是「服务器端」的状态?or「客户端」的状态?
  • RE:time_wait 是「被动敞开 TCP 连贯」一方的状态,可能是「客服端」的,也可能是「服务器端」的
  • 个别状况下,都是「客户端」所处的状态;「服务器端」个别设置「不被动敞开连贯」
服务器在对外服务时,是「客户端」发动的断开连接?还是「服务器」发动的断开连接?
  • 失常状况下,都是「客户端」发动的断开连接
  • 「服务器」个别设置为「不被动敞开连贯」,服务器通常执行「被动敞开」
  • 但 HTTP 申请中,http 头部 connection 参数,可能设置为 close,则,服务端解决完申请会被动敞开 TCP 连贯
对于 HTTP 申请中,设置的被动敞开 TCP 连贯的机制:TIME_WAIT 的是被动断开刚才会呈现的,所以被动断开方是服务端?
  • 答案是是的。在 HTTP1.1 协定中,有个 Connection 头,Connection 有两个值,close 和 keep-alive,这个头就相当于客户端通知服务端,服务端你执行实现申请之后,是敞开连贯还是放弃连贯,放弃连贯就意味着在放弃连贯期间,只能由客户端被动断开连接。还有一个 keep-alive 的头,设置的值就代表了服务端放弃连贯放弃多久。
  • HTTP 默认的 Connection 值为 close,那么就意味着敞开申请的一方简直都会是由服务端这边发动的。那么这个服务端产生 TIME_WAIT 过多的状况就很失常了。
  • 尽管 HTTP 默认 Connection 值为 close,然而,当初的浏览器发送申请的时候个别都会设置 Connection 为 keep-alive 了。所以,也有人说,当初没有必要通过调整参数来使 TIME_WAIT 升高了。
对于 time_wait:
  • TCP 连贯建设后,「被动敞开连贯」的一端,收到对方的 FIN 申请后,发送 ACK 响应,会处于 time_wait 状态;
  • time_wait 状态,存在的必要性:
  • 牢靠的实现 TCP 全双工连贯的终止:四次挥手敞开 TCP 连贯过程中,最初的 ACK 是由「被动敞开连贯」的一端收回的,如果这个 ACK 失落,则,对方会重发 FIN 申请,因而,在「被动敞开连贯」的一段,须要保护一个 time_wait 状态,解决对方重发的 FIN 申请;
  • 解决提早达到的报文:因为路由器可能抖动,TCP 报文会提早达到,为了防止「提早达到的 TCP 报文」被误认为是「新 TCP 连贯」的数据,则,须要在容许新创建 TCP 连贯之前,放弃一个不可用的状态,期待所有提早报文的隐没,个别设置为 2 倍的 MSL(报文的最大生存工夫),解决「提早达到的 TCP 报文」问题;

起源:ningg.top/computer-basic-theory-tcp-time-wait/

正文完
 0