百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

生产Nginx现大量TIME-WAIT,连接耗尽,该如何处理?

off999 2025-02-06 16:04 22 浏览 0 评论

背景说明:

在读者30+交流群中,是不是有小伙伴问:

生产环境 Nginx 后端服务大量 TIME-WAIT , 该怎么办?

除了Nginx进程之外,还有其他的后端服务如:

生产环境 Netty、SpringCloud Gateway 后端服务大量 TIME-WAIT , 该怎么办?

遇到这样的生产环境难题,小伙伴们非常头疼。

更为头疼的是,这个也是一道场景的面试题。之前有小伙伴反应过,他面试科大讯飞的时候,遇到了这道题目:

生产环境 Nginx 后端服务大量 TIME-WAIT 的解决步骤

这里给大家做一下系统化、体系化的梳理,使得大家可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”

也一并把这个题目以及参考答案,收入咱们的《Java面试宝典》,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

注:本文以 PDF 持续更新,最新Java 架构笔记、面试题 的PDF文件,请后台私信【笔记】即可获取!


基础知识:

TIME_WAIT是什么?

在构建TCP客户端服务器系统时,很容易犯简单的错误,这些错误会严重限制可伸缩性。

其中一个错误是没有考虑到TIME_WAIT状态。

为什么TIME_WAIT存在,它可能导致的问题,如何解决它,以及什么时候不应该。

TIME_WAIT是 TCP 状态转换图中经常被误解的状态。

这是某些套接字可以进入并保持相对较长时间的状态,如果您有足够的套接字,那么您创建新套接字连接的能力可能会受到影响,这可能会影响客户端服务器系统的可伸缩性。

关于套接字如何以及为什么首先进入TIME_WAIT,经常存在一些误解,不应该有,这并不神奇。从下面的TCP状态转换图中可以看出TIME_WAIT,是TCP客户端通常最终处于的最终状态。

虽然状态转换图显示TIME_WAIT为客户端的最终状态,但它不一定是客户端TIME_WAIT。事实上,最终状态是启动“主动关闭”的对等端最终进入,这可以是客户端或服务器。那么,发布“主动关闭”是什么意思?

如果TCP对等方是第一个呼叫Close()连接的对等方,则TCP对等方会启动“主动关闭” 。

在许多协议和客户端/服务器设计中,这是客户端。

在HTTP和FTP服务器中,这通常是服务器。

导致同伴结束的事件的实际顺序TIME_WAIT如下。

现在我们知道套接字是如何结束TIME_WAIT的,理解为什么这个状态存在以及它为什么会成为一个潜在的问题是有用的。

TIME_WAIT通常也被称为2MSL等待状态。

这是因为转换到的套接字在此期间的持续时间TIME_WAIT为2 x最大段寿命

MSL是任何段的最大时间量,对于构成TCP协议一部分的数据报的所有意图和目的而言,在丢弃之前,网络可以在网络上保持有效。这个时间限制最终以用于传输TCP段的IP数据报中的TTL字段为界。

不同的实现为MSL选择不同的值,常用值为30秒,1分钟或2分钟。

RFC 793指定MSL为2分钟,Windows系统默认为此值,但可以使用TcpTimedWaitDelay注册表设置进行调整。

TIME_WAIT可能影响系统可伸缩性 的原因是,TCP连接中一个完全关闭的套接字将保持TIME_WAIT约4分钟的状态。

如果许多连接正在快速打开和关闭,那么套接字TIME_WAIT可能会开始累积在系统上; 您可以TIME_WAIT使用netstat查看套接字。一次可以建立有限数量的套接字连接,限制此数量的其中一个因素是可用本地端口的数量。

如果TIME_WAIT插入太多的套接字,您会发现很难建立新的出站连接,因为缺少可用于新连接的本地端口。

但为什么TIME_WAIT存在呢?有两个原因需要TIME_WAIT。

首先是防止一个连接的延迟段被误解为后续连接的一部分。丢弃在连接处于2MSL等待状态时到达的任何段。

在上图中,我们有两个从终点1到终点2的连接。每个终点的地址和端口在每个连接中都是相同的。第一个连接终止于由端点2启动的主动关闭。如果端点2没有保持TIME_WAIT足够长的时间以确保来自先前连接的所有分段已经失效,则延迟的分段(具有适当的序列号)可以是误认为是第二个连接的一部分...

请注意,延迟片段可能会导致这样的问题。首先,每个端点的地址和端口需要相同; 这通常不太可能,因为操作系统通常从短暂端口范围为您选择客户端端口,从而在连接之间进行更改。其次,延迟段的序列号需要在新连接中有效,这也是不太可能的。但是,如果出现这两种情况,TIME_WAIT则会阻止新连接的数据被破坏。

需要TIME_WAIT第二个原因是可靠地实施TCP的全双工连接终端。

如果ACK从终点2开始的终点被丢弃,则终点1将重新发送终点FIN。如果连接已经过渡到CLOSED终点2,那么唯一可能的应答是发送一个,RST因为重发FIN是意外的。即使所有数据传输正确,这也会导致终点1接收到错误。

不幸的是,一些操作系统实现的方式TIME_WAIT似乎有点幼稚。

只有TIME_WAIT通过阻塞才能提供保护的连接与需要的socket完全匹配TIME_WAIT。这意味着由客户端地址,客户端端口,服务器地址和服务器端口标识的连接。但是,某些操作系统会施加更严格的限制,并且阻止本地端口号被重新使用,而该端口号包含在连接中TIME_WAIT。如果有足够的套接字结束,TIME_WAIT则不能建立新的出站连接,因为没有剩余本地端口分配给新连接。

Windows不会执行此操作,只会阻止与其中的连接完全匹配的出站连接TIME_WAIT。

入站连接受影响较小TIME_WAIT。

虽然由服务器主动关闭的TIME_WAIT连接与客户端连接完全一样,但是服务器正在侦听的本地端口不会阻止其成为新的入站连接的一部分。在Windows上,服务器正在监听的众所周知的端口可以构成随后接受的连接的一部分,并且如果从远程地址和端口建立了新的连接,该连接当前构成TIME_WAIT该本地地址和端口的连接的一部分,则只要新序列号大于当前连接的最终序列号,就允许连接TIME_WAIT。然而,TIME_WAIT在服务器上累积可能会影响性能和资源使用,因为TIME_WAIT最终需要超时的连接需要进行一些工作,直到TIME_WAIT状态结束,连接仍占用(少量)服务器资源。

鉴于TIME_WAIT由于本地端口号耗尽而影响出站连接的建立,并且这些连接通常使用由临时端口范围由操作系统自动分配的本地端口,因此您可以做的第一件事情是确保你正在使用一个体面的短暂端口范围。在Windows上,您可以通过调整MaxUserPort注册表设置来完成此操作。请注意,默认情况下,许多Windows系统的临时端口范围大约为4000,这对于许多客户端服务器系统来说可能太低。

虽然有可能缩短套接字在TIME_WAIT这方面的花费时间通常不会有帮助。鉴于TIME_WAIT当许多连接建立并且主动关闭时,这只是一个问题,调整2MSL等待周期通常会导致在给定时间内建立和关闭更多连接的情况,因此必须不断调整2MSL直到由于延迟片段似乎是后来连接的一部分,因此它可能会开始出现问题; 如果您连接到相同的远程地址和端口,并且非常快速地使用所有本地端口范围,或者如果连接到相同的远程地址和端口并将本地端口绑定到固定值,这种情况才会变得可能。

更改2MSL延迟通常是机器范围内的配置更改。您可以尝试TIME_WAIT使用SO_REUSEADDR套接字选项解决套接字级别问题。这允许在具有相同地址和端口的现有套接字已经存在的同时创建套接字。新的套接字基本上劫持了旧的套接字。您可以使用它SO_REUSEADDR来允许创建套接字,同时具有相同端口的套接字已经在其中,TIME_WAIT但这也会导致诸如拒绝服务攻击或数据窃取等问题。在Windows平台上另一套接字选项,SO_EXCLUSIVEADDRUSE可以帮助防止某些缺点的SO_REUSEADDR,但在我看来,最好避免这些尝试在各地工作TIME_WAIT,而是设计系统,使TIME_WAIT 不是问题。

上面的TCP状态转换图都显示有序的连接终止。还有另一种方法来终止TCP连接,这是通过中止连接并发送一个RST而不是一个FIN。这通常通过将SO_LINGER套接字选项设置为0 来实现。这会导致挂起的数据被丢弃,并且连接将被中止,RST而不是等待传输的数据,并且使用a清除干净的连接FIN。重要的是要认识到,当连接被中止时,可能在对等体之间流动的任何数据将被丢弃,RST直接送达; 通常作为表示“连接已被同级重置”的事实的错误。远程节点知道连接被中止,并且两个节点都没有进入TIME_WAIT。

当然,已经中止使用的连接的新化身RST可能成为TIME_WAIT阻碍的延迟段问题的受害者,但是成为问题所需的条件是不太可能的,无论如何,参见上面的更多细节。为了防止已经中止的连接导致延迟段问题,两个对等端都必须转换到TIME_WAIT连接关闭可能由诸如路由器之类的中介引起。但是,这不会发生,连接的两端都会关闭。

有几件事你可以做,以避免TIME_WAIT成为你的问题。其中一些假定您有能力更改您的客户端和服务器之间所说的协议,但是对于自定义服务器设计,您通常会这样做。

对于永远不会建立自己的出站连接的服务器,除了维护连接的资源和性能意义之外TIME_WAIT,您不必过分担心。

对于确实建立出站连接并接受入站连接的服务器,黄金法则是始终确保如果TIME_WAIT需要发生这种情况,则它会在另一个对等而不是服务器上结束。做到这一点的最佳方式是永远不要从服务器发起主动关闭,无论原因是什么。如果您的对等方超时,则以中止连接RST而不是关闭连接。如果你的对方发送无效数据,中止连接等等。

这个想法是,如果你的服务器永远不会启动一个主动关闭,它永远不会累积TIME_WAIT套接字,因此永远不会受到它们导致的可伸缩性问题的困扰。虽然在出现错误情况时很容易看到如何中止连接,但正常连接终止的情况如何?理想情况下,您应该为协议设计一种方式,让服务器告诉客户端它应该断开连接,而不是简单地让服务器发起主动关闭。因此,如果服务器需要终止连接,服务器会发送一个应用程序级别“我们已经完成”的消息,客户将此消息作为关闭连接的原因。如果客户端未能在合理的时间内关闭连接,则服务器会中止连接。

在客户端上,事情稍微复杂一些,毕竟,有人必须发起一个主动关闭来干净地终止TCP连接,并且如果它是客户端,那么这就是TIME_WAIT最终会结束的地方。然而,TIME_WAIT最终在客户端有几个优点。

首先,如果由于某种原因,客户端由于套接字在TIME_WAIT一个客户端中的积累而最终导致连接问题。其他客户不会受到影响。

其次,快速打开和关闭TCP连接到同一台服务器是没有效率的,因此超出了问题的意义TIME_WAIT尝试维持更长时间的连接而不是更短的时间。不要设计一个协议,客户端每分钟连接一次,并打开一个新的连接。而是使用持久连接设计,并且只有在连接失败时才重新连接,如果中间路由器拒绝在没有数据流的情况下保持连接处于打开状态,则可以实现应用程序级别ping,使用TCP保持活动状态或仅接受路由器重置连接; 好处是你没有积累TIME_WAIT插座。如果你在连接上做的工作自然是短暂的,那么考虑某种形式的“连接池”设计,从而保持连接的开放性和重用性。

最后,如果您绝对必须快速地从客户端打开和关闭连接到同一台服务器,那么也许您可以设计一个可以使用的应用程序级别关闭顺序,然后按照此顺序关闭。您的客户可能会发送“我完成了”消息,您的服务器可能会发送“再见”消息,然后客户端可能会中止连接。

TIME_WAIT存在原因并通过缩短2MSL周期或允许地址重用来解决这个问题SO_REUSEADDR并不总是一个好主意。

如果你能够设计你的协议TIME_WAIT避免在脑海中,那么你通常可以完全避免这个问题。

TIME_WAIT的4种查询方式

1、netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

2、ss -s

3、netstat -nat |awk '{print $6}'|sort|uniq -c|sort -rn

4、 统计TIME_WAIT 连接的本地地址

netstat -an | grep TIME_WAIT | awk '{print $4}' | sort | uniq -c | sort -n -k1

尝试抓取 tcp 包

tcpdump tcp -i any -nn port 8080 | grep "172.11.11.11"

系统参数优化

处理方法:需要修改内核参数开启重启:

net.ipv4.tcp_tw_reuse = 1,开启快速回收
net.ipv4.tcp_tw_recycle = 1 (在NAT网络中不建议开启,要设置为0),并且开启net.ipv4.tcp_timestamps = 1以上两个参数才生产。

具体操作方法如下:

echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.conf; 
echo "net.ipv4.tcp_tw_recycle = 1" >> /etc/sysctl.conf;
echo "net.ipv4.tcp_timestamps = 1" >> /etc/sysctl.conf;

sysctl -p;

nginx 配置优化

当使用nginx作为反向代理时,为了支持长连接,需要做到两点:

  1. 从client到nginx的连接是长连接
  2. 从nginx到server的连接是长连接

保持和client的长连接:

http {
    keepalive_timeout 120s 120s;
    keepalive_requests 1000;
}

keepalive_timeout设置

语法

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理解设置

keepalive_requests指令用于设置一个keep-alive连接上可以服务的请求的最大数量,当最大请求数量达到时,连接被关闭。默认是100。

这个参数的真实含义,是指一个keep alive建立之后,nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则nginx会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。

保持和server的长连接

为了让nginx和后端server(nginx称为upstream)之间保持长连接,典型设置如下:(默认nginx访问后端都是用的短连接(HTTP1.0),一个请求来了,Nginx 新开一个端口和后端建立连接,后端执行完毕后主动关闭该链接)

location / {
    proxy_http_version 1.1;
    proxy_set_header Connection keep-alive;
    proxy_pass http://httpurl;
}

HTTP协议中对长连接的支持是从1.1版本之后才有的,因此最好通过proxy_http_version指令设置为”1.1”;

从client过来的http header,因为即使是client和nginx之间是短连接,nginx和upstream之间也是可以开启长连接的。

keepalive理解设置

此处keepalive的含义不是开启、关闭长连接的开关;也不是用来设置超时的timeout;更不是设置长连接池最大连接数。官方解释:

  1. The connections parameter sets the maximum number of idle keepalive connections to upstream servers connections(设置到upstream服务器的空闲keepalive连接的最大数量)
  2. When this number is exceeded, the least recently used connections are closed. (当这个数量被突破时,最近使用最少的连接将被关闭)
  3. It should be particularly noted that the keepalive directive does not limit the total number of connections to upstream servers that an nginx worker process can open.(特别提醒:keepalive指令不会限制一个nginx worker进程到upstream服务器连接的总数量)

总结:

keepalive 这个参数一定要小心设置,尤其对于QPS比较高的场景,推荐先做一下估算,根据QPS和平均响应时间大体能计算出需要的长连接的数量。

比如前面10000 QPS和100毫秒响应时间就可以推算出需要的长连接数量大概是1000. 然后将keepalive设置为这个长连接数量的10%到30%。比较懒的同学,可以直接设置为keepalive=1000之类的,一般都OK的了。

综上,出现大量TIME_WAIT的情况

1)导致 nginx端出现大量TIME_WAIT的情况有两种:

keepalive_requests设置比较小,高并发下超过此值后nginx会强制关闭和客户端保持的keepalive长连接;(主动关闭连接后导致nginx出现TIME_WAIT)

keepalive设置的比较小(空闲数太小),导致高并发下nginx会频繁出现连接数震荡(超过该值会关闭连接),不停的关闭、开启和后端server保持的keepalive长连接;

2)导致后端server端出现大量TIME_WAIT的情况:

nginx没有打开和后端的长连接,即:没有设置proxy_http_version 1.1;和proxy_set_header Connection “”;从而导致后端server每次关闭连接,高并发下就会出现server端出现大量TIME_WAIT

相关推荐

在NAS实现直链访问_如何访问nas存储数据

平常在使用IPTV或者TVBOX时,经常自己会自定义一些源。如何直链的方式引用这些自定义的源呢?本人基于armbian和CasaOS来创作。使用标准的Web服务器(如Nginx或Apache...

PHP开发者必备的Linux权限核心指南

本文旨在帮助PHP开发者彻底理解并解决在Linux服务器上部署应用时遇到的权限问题(如Permissiondenied)。核心在于理解“哪个用户(进程)在访问哪个文件(目录)”。一、核心...

【Linux高手必修课】吃透sed命令!文本手术刀让你秒变运维大神!

为什么说sed是Linux运维的"核武器"?想象你有10万个配置文件需要批量修改?传统方式要写10万行脚本?sed一个命令就能搞定!这正是运维工程师的"暴力美学"时...

「实战」docker-compose 编排 多个docker 组成一个集群并做负载

本文目标docker-compose,对springboot应用进行一个集群(2个docker,多个类似,只要在docker-compose.yml再加boot应用的服务即可)发布的过程架构...

企业安全访问网关:ZeroNews反向代理

“我们需要让外包团队访问测试环境,但不想让他们看到我们的财务系统。”“审计要求我们必须记录所有第三方对内部系统的访问,现在的VPN日志一团糟。”“每次有新员工入职或合作伙伴接入,IT部门都要花半天时间...

反向代理以及其使用场景_反向代理实现过程

一、反向代理概念反向代理(ReverseProxy)是一种服务器配置,它将客户端的请求转发给内部的另一台或多台服务器处理,然后将响应返回给客户端。与正向代理(ForwardProxy)不同,正向代...

Nginx反向代理有多牛?一篇文章带你彻底搞懂!

你以为Nginx只是个简单的Web服务器?那可就大错特错了!这个看似普通的开源软件,实际上隐藏着惊人的能力。今天我们就来揭开它最强大的功能之一——反向代理的神秘面纱。反向代理到底是什么鬼?想象一下你...

Nginx反向代理最全详解(原理+应用+案例)

Nginx反向代理在大型网站有非常广泛的使用,下面我就重点来详解Nginx反向代理@mikechen文章来源:mikechen.cc正向代理要理解清楚反向代理,首先:你需要搞懂什么是正向代理。正向代理...

centos 生产环境安装 nginx,包含各种模块http3

企业级生产环境Nginx全模块构建的大部分功能,包括HTTP/2、HTTP/3、流媒体、SSL、缓存清理、负载均衡、DAV扩展、替换过滤、静态压缩等。下面我给出一个完整的生产环境安装流程(C...

Nginx的负载均衡方式有哪些?_nginx负载均衡机制

1.轮询(默认)2.加权轮询3.ip_hash4.least_conn5.fair(最小响应时间)--第三方6.url_hash--第三方...

Nginx百万并发优化:如何提升100倍性能!

关注△mikechen△,十余年BAT架构经验倾囊相授!大家好,我是mikechen。Nginx是大型架构的核心,下面我重点详解Nginx百万并发优化@mikechen文章来源:mikechen....

在 Red Hat Linux 上搭建高可用 Nginx + Keepalived 负载均衡集群

一、前言在现代生产环境中,负载均衡是确保系统高可用性和可扩展性的核心技术。Nginx作为轻量级高性能Web服务器,与Keepalived结合,可轻松实现高可用负载均衡集群(HA+LB...

云原生(十五) | Kubernetes 篇之深入了解 Pod

深入了解Pod一、什么是PodPod是一组(一个或多个)容器(docker容器)的集合(就像在豌豆荚中);这些容器共享存储、网络、以及怎样运行这些容器的声明。我们一般不直接创建Pod,而是...

云原生(十七) | Kubernetes 篇之深入了解 Deployment

深入了解Deployment一、什么是Deployment一个Deployment为Pods和ReplicaSets提供声明式的更新能力。你负责描述Deployment中的目标状...

深入理解令牌桶算法:实现分布式系统高效限流的秘籍

在高并发系统中,“限流”是保障服务稳定的核心手段——当请求量超过系统承载能力时,合理的限流策略能避免服务过载崩溃。令牌桶算法(TokenBucket)作为最经典的限流算法之一,既能控制请求的平...

取消回复欢迎 发表评论: