TIME_WAIT
服务端大量处于 TIME_WAIT 状态连接的原因
TCP 四次挥手的流程
下面这个图,是由「客户端」作为「主动关闭方」的 TCP 四次挥手的流程。
从上图我们可以知道,TIME_WAIT
状态是「主动关闭连接方
」才会出现的状态。而且 TIME_WAIT
状态会持续 2MSL
时间才会进入到 close
状态。在 Linux 上 2MSL
的默认时长是 60 秒
,也就是说停留在 TIME_WAIT 的时间为固定的 60 秒。
为什么需要 TIME_WAIT 状态?
- 保证「被动关闭连接」的一方,能被正确的关闭
- 防止历史连接中的数据,被后面相同四元组的连接错误的接收
总之记住,谁先关闭连接的,它就是主动关闭方,那么 TIME_WAIT
就会出现在主动关闭方。
什么场景下服务端会主动断开连接呢?
第一个场景:HTTP 没有使用长连接
在 HTTP/1.0
中默认是关闭的,如果浏览器要开启 Keep-Alive
,它必须在请求的 header
中添加
Connection: Keep-Alive
然后当服务器收到请求,作出回应的时候,它也被添加到响应中 header 里。
这样做,TCP 连接就不会中断,而是保持连接。当客户端发送另一个请求时,它会使用同一个 TCP 连接。这一直继续到客户端或服务器端提出断开连接。
从 HTTP/1.1
开始, 就默认是开启了 Keep-Alive
,现在大多数浏览器都默认是使用 HTTP/1.1
如果要关闭 HTTP Keep-Alive,需要在 HTTP 请求或者响应的 header 里添加 Connection:close
信息,也就是说,只要客户端和服务端任意一方的 HTTP header 中有 Connection:close
信息,那么就无法使用 HTTP 长连接的机制。
根据大多数 Web 服务的实现,不管哪一方禁用了 HTTP Keep-Alive,都是由服务端主动关闭连接(只需要服务器不用时直接关闭即可)。
总结:
因此,当服务端出现大量的 TIME_WAIT 状态连接的时候,可以排查下是否客户端和服务端都开启了 HTTP Keep-Alive
第二个场景:HTTP 长连接超时
配置长连接的超时参数,以nginx为例:
保持和client的长连接:
http {
keepalive_timeout 120s 120s;
keepalive_requests 10000;
}
#语法
keepalive_timeout timeout [header_timeout];
第一个参数:设置keep-alive客户端(浏览器)连接在服务器端(nginx端)保持开启的超时值(默认75s);值为0会禁用keep-alive客户端连接;
第二个参数:可选、在响应的header域中设置一个值“Keep-Alive: timeout=time”;通常可以不用设置;
keepalive_timeout 针对的是浏览器和nginx建立的一个tcp通道,没有数据传输时最长等待该时候后就关闭。
nginx和upstream中的keepalive_timeout则受到tomcat连接器的控制,tomcat中也有一个类似的keepalive_timeout参数
keepalive_requests 用于设置一个keep-alive连接上可以服务的请求的最大数量,当最大请求数量达到时,连接被关闭。默认是100
。这个参数就是下面第三个场景需要配置的参数。
第三个场景:HTTP 长连接的请求数量达到上限
Web 服务端通常会有个参数,来定义一条 HTTP 长连接上最大能处理的请求数量
,当超过最大限制时,就会主动关闭连接。
http {
upstream BACKEND {
server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s;
keepalive 300; // 这个很重要!最大空闲连接数,超过这个数值后就被关闭了,一般为最大连接数的10%--30%,或者直接设置最大连接数也行
}
server {
listen 8080 default_server;
server_name "";
location / {
proxy_pass http://BACKEND;
proxy_set_header Host $Host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header Cache-Control no-store;
add_header Pragma no-cache;
proxy_http_version 1.1; // 这两个最好也设置
proxy_set_header Connection "Keep-Alive";
}
}
}
总结:
导致 nginx端出现大量TIME_WAIT的情况有两种:
keepalive_requests
设置比较小,高并发下超过此值后nginx会强制关闭和客户端保持的keepalive长连接;(主动关闭连接后导致nginx
出现TIME_WAIT
)keepalive
设置的比较小(空闲数太小),导致高并发下nginx会频繁出现连接数震荡(空闲连接数超过该值就会被关闭
),不停的关闭、开启和后端server保持的keepalive长连接;
解决TIME_WAIT方法
1. 开启端口重用
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps=1(默认即为 1)
2. net.ipv4.tcp_max_tw_buckets
这个值默认为 18000,当系统中处于 TIME_WAIT 的连接一旦超过这个值时,系统就会将后面的 TIME_WAIT 连接状态重置,这个方法比较暴力。
net.ipv4.tcp_max_tw_buckets = 18000
3. 程序中使用 SO_LINGER
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));
如果l_onoff为非 0, 且l_linger值为 0,那么调用close后,会立该发送一个RST标志给对端,该 TCP 连接将跳过四次挥手,也就跳过了TIME_WAIT状态,直接关闭。
但这为跨越TIME_WAIT状态提供了一个可能,不过是一个非常危险的行为,不值得提倡。
参考来源:
https://mp.weixin.qq.com/s/sK2caRVxmkXInKcxtDsTVg
https://www.cnblogs.com/grimm/p/13038577.html
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)