0. 手把手教你做中间件、高性能服务器、分布式存储技术交流群
手把手教你做中间件、高性能服务器、分布式存储等 (redis、memcache、nginx、大容量 redis pika、rocksdb、mongodb、wiredtiger 存储引擎、高性能代理中间件),git 地址如下:
git 地址:https://github.com/y123456yz/middleware_development_learning
1. time_wait 状态产生条件
只有在正常四次挥手关闭连接的情况下,在主动关闭连接的一方会出现一段时间的 time_wait。如果启用了快速回收功能,回收时间和网络延迟状况有关,正常情况下小于 1s,如果没有开启 time_wait 快速回收功能,则 time_wait 回收时间默认 60s。
三次挥手过程(FIN+ACK, FIN+ACK,ACK)的情况,例如杀掉一段进程,第一个发送 FIN+ACK 的一端也会产生 time_wait。
2. Time_wait 状态相关参数说明
TCP 中有和 time_wait 状态相关的参数有以下四个:
tcp_tw_recycle |
表示开启 TCP 连接中 time_wait 的快速回收功能,默认为 0,表示关闭;生效前提是必须启用本端和对端 tcp_timestamps 配置。 |
tcp_timestamps |
时间戳选项,只有在该选项置 1 的时候 tcp_tw_recycle 才会生效。 |
tcp_max_tw_buckets |
表示系统同时保持 time_wait 的最大数量,如果超过这个量,time_wait 将打印警告信息。超限的时候后面产生的 time_wait 直接不处理,释放资源。注意:是新的连接直接释放资源,老的连接还是处于 time_wait 状态。 |
Tcp_tw_reuse |
客户端大量 time_wait 状态存在时,端口被占用,当有新的连接,如果没有可用端口,则会连接失败。启用该功能后,可以复用 time_wait 状态的连接。客户端 tcp_tw_reuse 生效前提是启用本端和对端 tcp_timestamp。
Tcp_tw_reuse 端口重用功能一般只针对客户端,因为服务端一般都是监听固定端口,端口数是固定的,端口不会用完。而客户端每次连接端口一般都是由协议栈自动分配。 |
3. Time_wait 快速回收
3.1 快速回收功能失效前提
Time_wait 快速回收功能生效前提:启用 tcp_tw_recycle,并启动本端和对端 tcp_timestamps 配置。启用 timestamps 功能时,报文中会携带时间戳选项信息,抓包如下:
3.2 启用 time_wait 快速回收功能副作用
如果启用了 tcp_tw_recycle 和 tcp_timestamps,如果接收报文四层选项字段带有时间戳信息,则会对时间戳进行检查,对不满足条件的包会直接丢弃,可能会造成客户端连接建立不成功。例如网络路由信息反复变化,移动 cmwap 网络发来的包的时间戳乱跳,同一局域网通过路由器做 NAT 访问服务器 (因为做 NAT 后,源 IP 就变为路由器的 IP 了,如果局域网内各个电脑系统时间不一致,则会出现) 等情况有可能会出现部分连接异常。原因是 tcp_tw_recycle/tcp_timestamps 以及对端 tcp_timestamps 都开启的条件下,60s 内同一源 ip 主机的 socket connect 请求中的 timestamp 必须是递增的。不同主机经过路由器做 NAT 后,报文的源 IP 地址就变为路由器的 IP 地址了。
3.3 内核协议栈相关主要源码
Time_wait 状态生成及快速回收相关代码:
开启 timestamps 引起的丢包相关源码如下:
4. 客户端端口重用
4.1 客户端大量 time_wait,端口重用前提
启用 tcp_tw_reuse,并启动本端和对端 tcp_timestamps 配置。
4.2 内核协议栈相关主要源码
5. 大量 timewait 对客户端、服务端影响
5.1 客户端大量 time_wait 影响
- 大量 time_wait 会造成连接资源不释放,内存无法回收。
- 由于客户端端口一般采用协议栈随机分配的方式,协议栈会给每个客户端连接分配一个未使用的端口,因此如果客户端同一 IP 对应的 time_wait 数量超过 ip_local_port_range 设置的最大值(也就是 65000),端口将被用完,连接会无法建立。
5.2 服务端大量 time_wait 影响
由于服务端只占用监听端口,因此不存在端口用完的现象。服务端大量 time_wait 唯一影响是:资源不释放,内存无法回收。
6. 测试验证
本次测试结果采用 sysbench.short 来压测 cobar 来验证,客户端物理设备和服务端物理设备的 ip_local_port_range (1024~65000) 和 tcp_max_tw_buckets (81920) 参数都是默认值,测试结果如下:
- 当 cobar 服务端 time_wait 数达到 81920 的时候,任然可以继续接收客户端连接,能够正常提供连接服务。
- 当客户端测试工具 sysbench.short 服务器上的 time_wait 数达到 60000 多的时候,客户端连接失败,无法连接,因为端口用完。打印:Cannot assign requested address; Cobar 服务器 time_wait 超限时打印:
Cobar 服务器 time_wait 超限的情况下,客户端 sysbench 压测结果基本不受影响,如下:
从上面测试可以看出,服务端 time_wait 不会影响客户端建链,只是占用内存。如果是客户端出现大量 time_wait 状态,此时端口用完,则无法建立连接。以上测试结论符合理论、代码分析。
7. 三种解决 time_wait 方法总结
|
Time_wait 快速回收 |
端口重用 |
限制 Tcp_max_tw_buckets |
配置方法 |
在需要进行 time_wait 快速回收的一端进行一下配置: tcp_tw_recycle:1 本端 tcp_timestamps:1 对端 tcp_timestamps:1 |
在需要进行 time_wait 快速回收的一端进行一下配置: tcp_tw_reuse:1 本端 tcp_timestamps:1 对端 tcp_timestamps:1 |
配置 Tcp_max_tw_buckets 的值在 60000 以下。例如配置为 30000 |
副作用 |
在某些情况下可能引起用户建连接失败(例如需要直接返回给用户信息的服务器) 比较暴力,不符合 TCP 协议规范 |
在某些情况下可能引起用户建连接失败(例如需要直接返回给用户信息的服务器) 部署复杂,需要同时改服务端,而服务端比较多。 服务器时间戳会带出 IDC,经过中间各种网络设备,尤其是运营商的无线设备等,如果某个设备对时戳有校验,则会产生丢包问题。 |
比较暴力,不符合 TCP 协议规范 应急的处理,立竿见影。 建议这种。 |