靓仔, 能跳出TIME-WAIT的坑吗

2,321 阅读3分钟

1. 开篇语

在TCP断开连接四次挥手时, 主动发起关闭方会产生 TIME_WAIT, TIME_WAIT 是 TCP 协议可靠性设计的重要一个环节, 虽说增强了可靠性, 但是对于高并发场景下, 会产生大量的 TIME_WAIT, 导致高峰时段无端口可以使用.

本文只做简单学习测试, 不保证内容的全面性及正确性, 不要轻易修改正式环境内核配置

今天主要对两个 Linux 内核的配置 tcp_tw_reuse 和 tcp_tw_recycle 进行测试讲解

2. 搭建实验环境

为了方面模拟网络情况, 我们设置可用端口区间仅为81

sysctl -w "net.ipv4.ip_local_port_range=81 81"

3. 默认配置测试

访问本地nginx服务

curl http://127.0.0.1

curl http://127.0.0.1
curl: (7) Failed to connect to 127.0.0.1: Cannot assign requested address

查看网络状态

netstat -napo |grep 127.0.0.1

我们可以看到, 第一次正常, 在 2MSL 时间内, 再次访问将会出现无法分配请求地址错误. 在 Linux 中TIME_WAIT时间为60s,并且还无法修改

TIME_WAIT过期时间宏定义

//include/net/tcp.h
/* how long to wait to destroy TIME-WAIT
 * state, about 60 seconds   */
#define TCP_TIMEWAIT_LEN (60*HZ) 

验证时间

可以看到标红线的地方为 time_wait 的倒计时,到时间后将会自动释放端口.

4. 关于 TIME_WAIT 情况的配置指令

开启 tcp_tw_reuse

echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse

进行测试

我们可以观测到, 在 TIME_WAIT 状态的端口也可以继续完成请求, 但不会改变 TIME_WAIT 本身的状态和计时.

此结论证明, 当本地端口将耗尽时, 可以尝试开启 tcp_tw_reuse 进行端口重用.

It's not SO_REUSEADDR socket option. SO_REUSEADD is used for binding socket to LISTEN state even if it is in TIME_WAIT state.

开启 tcp_tw_recycle

开启 tcp_tw_recycle

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

进行测试

结果很好, 同样完成了请求测试. 但是似乎跟 tcp_tw_reuse 哪里不太一样?

对比开启 tcp_tw_reuse 的netstat检测结果, 看看有什么差异.

我们发现这里简直就是暴力美学, 根本没有 TIME_WAIT 的状态呀

linux内核判定代码

当开启回收时,我们的 timeout 值为 rto, 这是一个非常短的一个时间, 否则为 TCP_TIMEWAIT_LEN , 还记得文章开头提到的宏定义的时间吗, 没错, 这里指的就是那个60s.

5. tcp_tw_reuse 与 tcp_tw_recycle 的区别

似乎这两个参数都能够很好的工作, 至少测试结果是很理想的.

参数 功能
tcp_tw_reuse 复用(reuse),不改变 TIMEWAIT 状态
tcp_tw_recycle 回收(recycle),最快时间回收

net.ipv4.tcp_timestamps 默认开启, 他是记录标记时间戳

tcp_tw_reuse 是怎么工作的

如果开启了 tcp_tw_reuse, 如果客户端发来的时间戳大于先前连接内核记录的最新时间戳, 则 Linux 将重新使用状态中的现有连接以 TIME-WAIT 用于新的对外请求连接, 状态中的传出连接 TIME-WAIT可在仅一秒之后重复使用.

tcp_tw_recycle 是怎么工作的

如果开启了 tcp_tw_recycle, 则内核会记住客户端上次发来数据包的时间戳, 如果发来的数据包时间戳小于内核记录的最后发来的数据包时间戳, 那么将会丢弃此数据包, 这种情况在 NAT 模式下多机器时间滞后或同时发送, 会有很大危险, 会造成难以排查的异常情况.

网上说 设置net.ipv4.tcp_timestamps=0, 可以不再检测时间戳, 未找到官方出处, 真实性无法保证.

6. 结论:

在服务器端,请勿启用net.ipv4.tcp_tw_recycle 除非你非常确定你的服务中永远不会有NAT设备。 启用net.ipv4.tcp_tw_reuse 对于发送请求 (outgoing connection) 的连接有效。

在客户端,启用 net.ipv4.tcp_tw_reuse 是另一个几乎安全的解决方案

而且,在设计协议时,不要先让客户关闭。客户端不必处理将TIME-WAIT状态推向更适合处理此问题的服务器的状态。

所以最终建议可以开启 tcp_tw_reuse, 禁用 tcp_tw_recycle.

7. 附TCP状态图

参考文档:

vincent.bernat.ch/en/blog/201…

linuxsyseng.blogspot.com/2017/03/the…

更多精彩技术, 请关注公众号 (呆呆熊的技术路):