本帖最后由 易逝的信仰 于 2020-9-7 12:56 编辑
三、TIME_WAIT 会带来哪些问题呢?TIME_WAIT 带来的问题注意是源于:一个连接进入 TIME_WAIT 状态后需要等待 2*MSL(一般是 1 到 4 分钟)那么长的时间才能断开连接释放连接占用的资源,会造成以下问题: 作为服务器,短时间内关闭了大量的 Client 连接,就会造成服务器上出现大量的 TIME_WAIT 连接,占据大量的 tuple,严重消耗着服务器的资源。 作为客户端,短时间内大量的短连接,会大量消耗的 Client 机器的端口,毕竟端口只有 65535 个,端口被耗尽了,后续就无法在发起新的连接了。
由于上面两个问题,作为客户端需要连本机的一个服务的时候,首选 UNIX 域套接字而不是 TCP)。TIME_WAIT 很令人头疼,很多问题是由 TIME_WAIT 造成的,但是 TIME_WAIT 又不是多余的不能简单将 TIME_WAIT 去掉,那么怎么来解决或缓解 TIME_WAIT 问题呢?可以进行 TIME_WAIT 的快速回收和重用来缓解 TIME_WAIT 的问题。有没一些清掉 TIME_WAIT 的技巧呢? 四、TIME_WAIT 的快速回收和重用【1】TIME_WAIT 快速回收linux 下开启 TIME_WAIT 快速回收需要同时打开 tcp_tw_recycle 和 tcp_timestamps(默认打开)两选项。Linux 下快速回收的时间为 3.5* RTO(Retransmission Timeout),而一个 RTO 时间为 200ms 至 120s。开启快速回收 TIME_WAIT,可能会带来(问题一、)中说的三点危险,为了避免这些危险,要求同时满足以下三种情况的新连接要被拒绝掉: 来自同一个对端 Peer 的 TCP 包携带了时间戳; 之前同一台 peer 机器(仅仅识别 IP 地址,因为连接被快速释放了,没了端口信息)的某个 TCP 数据在 MSL 秒之内到过本 Server; Peer 机器新连接的时间戳小于 peer 机器上次 TCP 到来时的时间戳,且差值大于重放窗口戳(TCP_PAWS_WINDOW)。
初看起来正常的数据包同时满足下面 3 条几乎不可能,因为机器的时间戳不可能倒流的,出现上述的 3 点均满足时,一定是老的重复数据包又回来了,丢弃老的 SYN 包是正常的。到此,似乎启用快速回收就能很大程度缓解 TIME_WAIT 带来的问题。但是,这里忽略了一个东西就是 NAT。 在一个 NAT 后面的所有 Peer 机器在 Server 看来都是一个机器,NAT 后面的那么多 Peer 机器的系统时间戳很可能不一致,有些快,有些慢。这样,在 Server 关闭了与系统时间戳快的 Client 的连接后,在这个连接进入快速回收的时候,同一 NAT 后面的系统时间戳慢的 Client 向 Server 发起连接,这就很有可能同时满足上面的三种情况,造成该连接被 Server 拒绝掉。所以,在是否开启 tcp_tw_recycle 需要慎重考虑了 【2】TIME_WAIT 重用 linux 上比较完美的实现了 TIME_WAIT 重用问题。只要满足下面两点中的一点,一个 TW 状态的四元组(即一个 socket 连接)可以重新被新到来的 SYN 连接使用。 [1]. 新连接 SYN 告知的初始序列号比 TIME_WAIT 老连接的末序列号大;[2]. 如果开启了 tcp_timestamps,并且新到来的连接的时间戳比老连接的时间戳大。 要同时开启 tcp_tw_reuse 选项和 tcp_timestamps 选项才可以开启 TIME_WAIT 重用,还有一个条件是:重用 TIME_WAIT 的条件是收到最后一个包后超过 1s。细心的同学可能发现 TIME_WAIT 重用对 Server 端来说并没解决大量 TIME_WAIT 造成的资源消耗的问题,因为不管 TIME_WAIT 连接是否被重用,它依旧占用着系统资源。即便如此,TIME_WAIT 重用还是有些用处的,它解决了整机范围拒绝接入的问题,虽然一般一个单独的 Client 是不可能在 MSL 内用同一个端口连接同一个服务的,但是如果 Client 做了 bind 端口那就是同个端口了。时间戳重用 TIME_WAIT 连接的机制的前提是 IP 地址唯一性,得出新请求发起自同一台机器,但是如果是 NAT 环境下就不能这样保证了,于是在 NAT 环境下,TIME_WAIT 重用还是有风险的。 有些同学可能会混淆 tcp_tw_reuse 和 SO_REUSEADDR 选项,认为是相关的一个东西,其实他们是两个完全不同的东西,可以说两个半毛钱关系都没。tcp_tw_reuse 是内核选项,而 SO_REUSEADDR 用户态的选项,使用 SO_REUSEADDR 是告诉内核,如果端口忙,但 TCP 状态位于 TIME_WAIT,可以重用端口。如果端口忙,而 TCP 状态位于其他状态,重用端口时依旧得到一个错误信息,指明 Address already in use”。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时 SO_REUSEADDR 选项非常有用。但是,使用这个选项就会有(问题二、)中说的三点危险,虽然发生的概率不大。 |