一、TIME_WAIT的产生
TIME_WAIT是主动关闭的一方,在使用FIN|ACK|FIN|ACK四分组正常关闭TCP连接时产生的。TIME_WAIT状态本身和应用层的客户端或者服务器是没有关系的。服务器在处理客户端请求的时候,如果你的程序设计为服务器主动关闭,那么你才有可能需要关注这个TIME_WAIT状态过多的问题。如果你的服务器设计为被动关闭,那么你首先要关注的是CLOSE_WAIT。
二、问题分析及影响
在高并发短连接的TCP服务器上,当服务器处理完请求后立刻按照主动正常关闭连接这个场景下,会出现大量socket处于TIME_WAIT状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。
Nginx 作为反向代理时,大量的短链接,可能导致 Nginx 上的 TCP 连接处于 time_wait 状态:
• 每一个 time_wait 状态,都会占用一个「本地端口」,上限为 65535(16 bit,2 Byte);
• TCP 连接中,「主动发起关闭连接」的一端,会进入 time_wait 状态
• 当大量的连接处于 time_wait 时,新建立 TCP 连接会出错,address already in use : connect 异常
• 特别是 HTTP 请求中,如果 connection 头部取值被设置为 close 时,基本都由「服务端」发起主动关闭连接
• TCP 四次挥手关闭连接机制中,为了保证 ACK 重发和丢弃延迟数据,设置 time_wait 为 2 倍的 MSL(报文最大存活时间)即4分钟
三、解决方案
1、客户端,HTTP 请求的头部,connection 设置为 keep-alive,保持存活一段时间
http {
upstream BACKEND{
server 192.168.0.1:8080 weight=1 max_false=2 fail_timeout=30s;
server 192.168.0.2:8080 weight=1 max_false=2 fail_timeout=30s;
keepalive 300; # 连接池里面最大的空闲连接数量
}
server{
listen 8080 default_server;
server_name:"";
location /{
proxy_pass http://BACKEND
proxy_set_header Host $Host;
proxy_set_header x-forwarded-for $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
add_header Cache-Control no-store;
add_header Pragma no-cache;
proxy_http_version 1.1; # 启用HTTP/1.1版本与被代理服务器建立连接,1.0版本不支持长连接,很重要!
proxy_set_header Connection ""; # 设置http头默认为长连接,不关闭
}
}
}
2、服务器端,允许 time_wait 状态的 socket 被重用,缩减 time_wait 时间,设置为 1 MSL(即,2 mins)
# vim /etc/sysctl.conf
net.ipv4.tcp_fin_timeout = 30 # 当服务器主动闭连接时,决定了套接字保持在FIN-WAIT-2状态的时间。默认值是60秒
sysctl net.ipv4.tcp_tw_reuse=1 # 表示允许将TIME_WAIT状态的socket重新用于新的TCP连接,避免总有大量TIME_WAIT状态的连接存在
sysctl net.ipv4.tcp_tw_recycle=1 # 这个参数用于设置启用timewait快速回收
sysctl net.ipv4.tcp_timestamps=1 # 开启时间戳,配合tcp复用,注:阿里的slb会清理掉tcp_timestamps