徐州论坛:time_wait 详解和解决方案

admin 2个月前 (07-26) 科技 50 2
  • 1. 发生缘故原由
  • 2. 导致问题
  • 3. Nginx
    • 3.1 长毗邻
  • 4. 解决方案
  • 5 .参考

发生缘故原由

TCP 毗邻关闭时,会有 4 次通讯(四次挥手),来确认双方都住手收发数据了。如上图,自动关闭方,最后发送 ACK 时,会进入 TIME_WAIT 状态,要等 2MSL 时间后,这条毗邻才真正消逝。

为什么要进入 TIME_WAIT 状态?

TCP 的可靠传输机制要求,被动关闭方(简称 S)要确保最后发送的 FIN K 对方能收到。好比网络中的某个路由器泛起异常,自动关闭方(简称 C)回复的 ACK K+1 没有实时到达,S 就会重发 FIN K 给 C。若是此时 C 不进入 TIME_WAIT 状态,立马关闭毗邻,会有 2 种情形:

  1. C 机械上,有可能新起的毗邻会重用旧毗邻的端口,此时新毗邻就会收到 S 端重发的 FIN K 新闻,导致新毗邻传输泛起错误。
  2. C 机械上,并没有用旧毗邻端口,此时会回复给 S 端一个 RST 类型的新闻,应用程序报 connect reset by peer 异常。

为制止上面情形, TCP 会守候 2 MSL 时间,让 S 发的 FIN K 和 C 回复的 ACK K+1 在网络上消逝,才真正清除掉毗邻。

为什么守候 2 MSL 时间?

MSL是 Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,是 TCP 协议划定报文段在网络中最长生计时间,超出后报文段就会被抛弃。RFC793 界说 MSL 为 2 分钟,一样平常 Linux 会默认设置个更小的值 30 秒。

MSL 时间,是从 C 回复 ACK 后最先 TIME_WAIT 计时,若是这时代收到 S 重发的 FIN 在回复 ACK 后,重新最先计时。这块代码是 Linux tcp_timewait_state_process 函数处置的。

而 2 MSL 是为了确保 C 和 S 两头发送的数据都在网络中消逝,不会影响后续的新毗邻,该若何明白?

假设 C 回复 ACK ,S 经由 t 时间后收到,则有 0 < t <= MSL,由于 C 并不知道 S 多久收到,以是 C 至少要维持 MSL 时间的 TIME_WAIT 状态,才确保回复的 ACK 从网络中消逝。 若是 S 在 MSL 时间收到 ACK, 而收到前一瞬间, 由于超时又重传一个 FIN ,这个包又要 MSL 时间才会从网络中消逝。

回复需要 MSL 消逝 + 发送需要 MSL 消逝 = 2 MSL。

导致问题

早年面的剖析来看,泛起 TIME_WAIT 属于正常行为。但在现实生产环境中,大量的 TIME_WAIT 会导致系统异常。

假设前面的 C 是 Client,S 是 Server,若是 C 或 泛起大量的 TIME_WAIT,会导致新毗邻无端口可以用,泛起

Cannot assign requested address 错误。这是由于端口被占完了,Linux 一样平常默认端口局限是:32768-61000,可以通过 cat /proc/sys/net/ipv4/ip_local_port_range 来查看。凭据 TCP 毗邻四元组盘算,C 毗邻 S 最多有 28232 可以用,也就是说最多同时有 28232 个毗邻保持。

看着挺多,但若是用短毗邻的话很快就会泛起上面错误,由于每个毗邻关闭后,需要保持 2 MSL 时间,也就是 4分钟。这意味着 4 分钟内最多确立 28232 个毗邻,每秒钟 117 个,在高并发系统下一样平常不够用的。

Nginx

毗邻自动关闭方会进入 TIME_WAIT,若是 C 先关闭,C 会泛起上面错误。若是是客户端时真正的客户(浏览器),一样平常不会触发上面的错误。

若是 C 是应用程序或署理,好比 Nginx,此时链路是:浏览器 -> Nginx -> 应用。 由于 Nginx 是转发请求,自身也是客户端,以是若是 Nginx 到应用是短毗邻,每次转发完请求都自动关闭毗邻,那很快会触发到端口不够用的错误。

Nginx 默认设置毗邻到后端是 HTTP/1.0 不支持 HTTP keep-alive,以是每次后端应用都市自动关闭毗邻,这样后端泛起 TIME_WAIT,而 Nginx 不会泛起。

后端泛起大量的 TIME_WAIT 一样平常问题不明显,但需要注重的点是:

查看服务器上/var/log/messages 有没有 TCP: time wait bucket table overflow 的日志,有的话是超出最大 TIME_WAIT 的数目了,超出后系统会把多余的 TIME_WAIT 删除掉,会导致前面章节先容的 2 种情形。

这个错误可以调大内核参数 /etc/sysctl.conftcp_max_tw_buckets 来解决。

长毗邻

另外个解决方案是 Nginx 与后端挪用,启用 HTTP/1.1 开启 keep-alive ,保持长毗邻。设置如下:

http{
    upstream www{
        keepalive 500;  # 与后端最多保持的长毗邻数目
    }
    proxy_set_header X-Real-IP $remote_addr; ## 不会生效
    server {
        location / {
        proxy_http_version 1.1;  # 启用 HTTP/1.1
        proxy_set_header Connection "";   
        }
    }
}

proxy_set_header Connection ""; 这个设置是设置 Nginx 请求后端的 Connection header 的值为空。目的是防止客户端传值 close 给 Nginx,Nginx 又转发给后端,导致无法保持长毗邻。

在 Nginx 设置中有个注重的点是:当前设置 location 中若是界说了 proxy_set_header ,则不会从上级继续proxy_set_header 了,如上面设置的 proxy_set_header X-Real-IP $remote_addr 则不会生效。

没有显示界说的 header,Nginx 默认只带下面 2 个 header:

proxy_set_header Host $proxy_host;
proxy_set_header Connection close; 

解决方案

除保持长毗邻外,调整系统参数也可以解决大量 TIME_WAIT 的问题。

加速接纳

tcp_tw_timeout = 30:示意毗邻在 TIME_WAIT 状态下的过时时间。这里设置 30 秒后接纳,如前面盘算调整后 28232 / 30 = 936, 每秒钟可确立毗邻 936 个。

增添端口数目

ip_local_port_range = 1024 65535: 调整后最大端口数目 64511,64511 / 30 = 2150,每秒钟可确立毗邻 2150 个。

复用 TIME_WAIT 毗邻

tcp_tw_reuse = 1: 1 示意开启复用 TIME_WAIT 状态的毗邻,这个参数在 Linux tcp_twsk_unique 函数中读取的。

	int reuse = sock_net(sk)->ipv4.sysctl_tcp_tw_reuse;
	// tcptw->tw_ts_recent_stamp 为 1 示意旧的 TIME_WAIT 毗邻是携带时间戳的,需要开启 tcp_timestamps (已默认开启)。
    // tcp_tw_reuse  reuse 开启复用
   // time_after32 示意旧的 TIME_WAIT 毗邻,最后收到数据已跨越 1 秒。
	if (tcptw->tw_ts_recent_stamp &&
	    (!twp || (reuse && time_after32(ktime_get_seconds(),
					    tcptw->tw_ts_recent_stamp)))) {
		if (likely(!tp->repair)) {
			u32 seq = tcptw->tw_snd_nxt + 65535 + 2;

			if (!seq)
				seq = 1;
			WRITE_ONCE(tp->write_seq, seq);
			tp->rx_opt.ts_recent	   = tcptw->tw_ts_recent;
			tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
		}
		sock_hold(sktw);
		return 1;
	}

其他

tcp_tw_recycle 也有效果,但不建议调整,Linux 4.12 后已经移除这个参数了,这里不做先容了。

调整下令:

// 暂且生效
sysctl -w net.ipv4.tcp_tw_reuse = 1
sysctl -p

// 恒久生效
vi /etc/sysctl.conf

参考

http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header

https://github.com/torvalds/linux

https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt

,

进入sunbet官网手机版登陆

欢迎进入sunbet官网手机版登陆!Sunbet 申博提供申博开户(sunbet开户)、SunbetAPP下载、Sunbet客户端下载、Sunbet代理合作等业务。

Allbet声明:该文看法仅代表作者自己,与本平台无关。转载请注明:徐州论坛:time_wait 详解和解决方案

文章归档

站点信息

  • 文章总数:348
  • 页面总数:0
  • 分类总数:8
  • 标签总数:711
  • 评论总数:20
  • 浏览总数:3418