TIME_WAIT和优化

故障现象 #

让我们先从一例线上故障说起。在一次升级线上应用服务之后,我们发现该服务的可用性变 得时好时坏,一段时间可以对外提供服务,一段时间突然又不可以, 大家都百思不得其解。 运维同学登录到服务所在的主机上,使用 netstat 命令查看后才发现,主机上有成千上万处 于 TIME_WAIT 状态的连接。

经过层层剖析后,我们发现罪魁祸首就是 TIME_WAIT。为什么呢?我们这个应用服务需要 通过发起 TCP 连接对外提供服务。每个连接会占用一个本地端口,当在高并发的情况下, TIME_WAIT 状态的连接过多,多到把本机可用的端口耗尽,应用服务对外表现的症状,就 是不能正常工作了。当过了一段时间之后,处于 TIME_WAIT 的连接被系统回收并关闭 后,释放出本地端口可供使用,应用服务对外表现为,可以正常工作。这样周而复始,便会 出现了一会儿不可以,过一两分钟又可以正常工作的现象。

TIME_WAIT概念和作用 #

概念 #

  • 只有发起连接终止的一方会进入 TIME_WAIT 状态
  • TIME_WAIT停留持续时间是固定的,是最长分节生命期 MSL(maximumsegment lifetime)的两倍(2MSL)。Linux系统里有一个硬编码的字段,名称为TCP_IMEWAIT_LEN,其值为 60 秒。也就是说,Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒

作用 #

  • 这样做是为了确保最后的 ACK 能让被动关闭方接收(可能丢失ACK后重传ACK),从而帮助其正常关闭
  • 第二个理由和连接“化身”和报文迷走有关系,为了让旧连接的重复分节在网络中自然消失。

TIME_WAIT的副作用和优化 #

TCP和TIME_WAIT #

当连接的一方主动关闭连接,在接收到对端的 FIN 报文之后,主动关闭连接的一方会在 TIME_WAIT 这个状态里停留一段时间,这个时间大约为 2MSL。

主动关闭连接的一方可以是客户端,也可以是服务端; 副作用1 主动关闭连接的一方 是客户端; 副作用2 主动关闭连接的一方 是服务端;

副作用和优化 #

  • 副作用1 对端口资源的占用,一个 TCP 连接至少消耗一个本地端口

  • 副作用1 优化 net.ipv4.tcp_tw_reuse(推荐) 从协议角度理解如果是安全可控的,可以复用处于 TIME_WAIT 的套接字为新的连接所用.

  1. 只适用于连接发起方(C/S 模型中的客户端);
  2. 对应的 TIME_WAIT 状态的连接创建时间超过 1 秒才可以被复用。

net.ipv4.tcp_max_tw_buckets(不推荐) 这个值默认为 18000,当系统中处于TIME_WAIT 的连接一旦超过这个值时,系统就会将所有的 TIME_WAIT 连接状态重置. 这个方法过于暴力,而且治标不治本,带来的问题远比解决的问题多,不推荐使用。

SO_LINGER(不推荐)
不要试图使用SO_LINGER设置套接字选项,跳过 TIME_WAIT;

  • 副作用2 服务器端程序重启时,地址被占用 -> Address already in use 异常 通过服务器端发起的关闭连接操作,引起了一个已有的 TCP 连接处于 TME_WAIT 状态,正是这个 TIME_WAIT 的连接,使得服务器重启时,继续绑定在127.0.0.1地址和xxxx端口上的操作,返回了Address already in use的错误。

  • 副作用2 优化 服务器端程序,都应该设置 SO_REUSEADDR 套接字选项,以便服务端程序可以在极短时间内复用同一个端口启动

大量TIME_WAIT的问题[gpt] #

问题 #

RPC客户端A调用服务端C有很多time-wait,客户端B调用服务端C会有什么问题

解决方案 #

在RPC客户端A调用服务端C时出现多个TIME_WAIT状态的情况下,如果RPC客户端B也调用相同的服务端C,可能会面临以下问题
1. 端口耗尽:每个TCP连接会占用一个本地端口号,如果RPC客户端A和B同时发起大量的请求,由于TIME_WAIT状态的存在,可能会导致本地端口耗尽,无法建立新的连接。
2. 连接超时:由于服务端C处于TIME_WAIT状态,即使RPC客户端B发起请求,也需要等待TIME_WAIT状态消失后才能建立连接,这会导致连接的延迟增加,可能会触发连接超时,导致B无法正常与服务端C建立连接。
3. 并发性能下降:当RPC客户端A和B同时发起大量请求时,如果每个请求都需要等待TIME_WAIT状态消失后才能建立连接,会导致连接的建立速度变慢,从而降低系统的并发性能。

为了避免这些问题,可以采取以下措施: 1. 避免端口耗尽:可以通过增加可用端口数、增加端口重用的时间等方式来缓解端口耗尽的问题。在操作系统级别上,可以通过调整端口范围或增加可用端口的数量来解决。
2. 优化连接复用:可以使用连接池来管理TCP连接,以便复用连接,避免频繁地创建和关闭连接。这样可以减少TIME_WAIT状态的产生,并提高连接的复用率和性能。
3. 调整TIME_WAIT时间:可以在服务端C上调整TIME_WAIT状态的时间,在合理的范围内降低TIME_WAIT状态的持续时间,从而缩短等待时间,提高连接的可用性和性能。

总结: 最佳的解决方案取决于具体的应用场景和系统配置,需要综合考虑性能、资源消耗和可维护性等因素来做出合理的决策。

参考 #

  1. 网络编程实战 - 10 | TIME_WAIT:隐藏在细节下的魔鬼 盛延敏
  2. 网络编程实战 - 15 | 怎么老是出现“地址已经被使用”? 盛延敏