TIME_WAIT 排查

01 Sep 2016

最近在ss -s看服务器状态时, 发现TIME_WAIT状态连接数过高, 于是着手解决.

我们的系统:

(client <-> ) ELB <-> nginx (on multiple host) <-> upstream service

整个链路下来, 每一环都应该是HTTP长连接加连接池解决问题. 不应有频繁的TCP连接建立/销毁.

TCP TIME_WAIT

TCP连接, 主动关闭方会处于较长的TIME_WAIT状态, 以确保数据传输完毕, 时间可长达2 * MSL, 即就是一个数据包在网络中往返一次的最长时间.

其目的是避免连接串用导致无法区分新旧连接.

过多的TIME_WAIT意味着TCP连接频繁销毁. 另外每个处于TIME_WAIT状态连接仍然占用着端口, 有耗尽系统可用端口的风险.

在系统设计时, 一般不应该由服务器主动关闭连接, 而应由客户端主动关闭, 以避免TIME_WAIT堆积.

sysctl 相关参数

net.ipv4.tcp_fin_timeout MSL时间, 默认60s 这个其实是处于FIN_WAIT2状态的超时时间. Linux系统是写死在Kernel里的(=2*MSL):

`#define TCP_TIMEWAIT_LEN (60*HZ)`

net.ipv4.ip_local_port_range 本地端口范围, 调大一些可以避免端口耗尽的风险.

复用处于TIME_WAIT状态的socket:

这两个参数的机制和使用注意, 这里不再展开. 启用了这两个参数, 可以极大减少了TIME_WAIT数目, 但是没有从更本上解决连接创建/销毁过于频繁问题.

nginx <-> upstream service 环节

因为nginx连接nhttp upstream默认是HTTP/1.0, 需要加上如下配置, 强制使用HTTP/1.1以使HTTP长连接特性生效.

upstream service {
    ...
    keepalive N; # 当然这个参数也是要开启的, 以确保nginx和upstream间的连接复用
}
...

location ... {
    proxy_pass http://service;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

ELB <-> nginx 环节

nginx的keepalive_requests参数限制了一个HTTP长连接内最多可以完成请求数, 默认是100. 当每连接在完成了keepalive_requests个请求后, 就被nginx主动断开, 从而导致了TIME_WAIT堆积.

这不合理, 理想状态下应该无论处理了多少个请求, 一直复用连接不断开. 由于 keepalive_requests参数不支持 unlimited 的配置, 故只能设成一个较大的值以减少主动断开频率.

Reference

nginx配置

火丁TCP系列

HOME