nginx代理node,TIME_WAIT 测试

本文通过实测nginx upstream模块的keepalive效果,详细解析如何优化连接复用,减少TIME_WAIT状态,提升服务器效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面: 阅读本文 需要了解以下知识:

       1. TIME_WAIT 是TCP连接的一种状态,对于TCP连接的双方,谁先主动断开连接,谁就要保持一段时间的TIME_WAIT状态

       2. 因为linux分配给一个用户的文件句柄是有限的(可以参考:http://blog.youkuaiyun.com/shootyou/article/details/6579139,nginx里设置了wroker_rlimit_nofile的话,会忽略ulimit -a 显示的值),而TIME_WAIT和CLOSE_WAIT两种状态如果一直被保持,那么意味着对应数目的通道就一直被占着,而且是“占着茅坑不使劲”,一旦达到句柄数上限,新的请求就无法被处理了
       注意:虽然 对nginx来说 有个参数可以突破ulimit -a 显示的数量 ,但nginx代理的上游服务器不会因nginx的设置而突破ulimit的限制。另外:如果进程启动时的ulimit是一个值,那么如果不重启这个进程,即使改了ulimit也无法对其生效。
       3. 对于nginx代理node,且同在一台机器上运行的话, time_wait 不是落在 nginx上 就是 落在node上, 要想减少整个服务器的time_wait总量,最好的个办法是重用连接,这样才能减少time_wait 的总量。

测试目标

      验证nginx 在 upstream 模块开启的 keepalive效果

测试环境

      Ubuntu16.04    nginx 1.14.2   node 8.14.0

测试代码

    1.  node  app.js

const http = require('http');
const hostname = '127.0.0.1';
const port =3000;
const server = http.createServer((req,res)=>{
        res.statusCode=200;
        res.setHeader('content-type','text/plain');
        setTimeout(function(){
                res.end('Hello World\n');
        },3000);
});
server.listen(port,hostname,()=>{
        console.log(`server running at http://${hostname}:${port}/`);
});

     node 运行在3000端口

   2. nginx.conf

events{}
http{

        upstream local {
                server 127.0.0.1:3000;
                keepalive 1; #这里为了测试方便,最大保持 1 个空闲活动连接
                #keepalive_timeout 60; #这个在nginx 1.15.3才支持
        }
        server{
                listen 1000;
                location / {
                        proxy_pass http://local;
                        proxy_set_header Connection ""; #清空这个头信息
                        proxy_http_version 1.1;#指定nginx 请求代理时使用http1.1
                }
        }
}

  这里nginx 在1000端口上 代理 3000端口的node。开启keepalive的要点:

  1) 在upstream里加入 keepalive 指令

   2)在server 里指定使用http1.1, nginx默认使用1.0。http1.1 可以重用连接,在一个连接上发送多次请求

        # 如果客户端请求nginx使用的是http1.0,头信息可能会有 connection:closed,我们需要清空这个头信息 
       proxy_set_header Connection  "";
       proxy_http_version 1.1    #指定nginx 请求node时使用http1.1

测试步骤

     1.  不开启 keepalive  (注释upstream里的 keepalive)

      通过命令循环发出4次请求,可以看到3秒一次请求

root@localhost:/www/nginx_noinlcude# for n in 1 2 3 4 ;do curl localhost:1000;date; done 
Hello World
Fri Mar  8 17:04:34 CST 2019
Hello World
Fri Mar  8 17:04:37 CST 2019
Hello World
Fri Mar  8 17:04:40 CST 2019
Hello World
Fri Mar  8 17:04:43 CST 2019

      在执行的同时,监测网络状态:

root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:3000          127.0.0.1:38684         ESTABLISHED 1324/node       
tcp        0      0 127.0.0.1:38684         127.0.0.1:3000          ESTABLISHED 3737/nginx: worker 
tcp        0      0 127.0.0.1:38678         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38672         127.0.0.1:3000          TIME_WAIT   -               
Fri Mar  8 17:06:00 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:3000          127.0.0.1:38684         ESTABLISHED 1324/node       
tcp        0      0 127.0.0.1:38684         127.0.0.1:3000          ESTABLISHED 3737/nginx: worker 
tcp        0      0 127.0.0.1:38678         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38672         127.0.0.1:3000          TIME_WAIT   -               
Fri Mar  8 17:06:02 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:38684         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38690         127.0.0.1:3000          ESTABLISHED 3737/nginx: worker 
tcp        0      0 127.0.0.1:38678         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38672         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38690         ESTABLISHED 1324/node       
Fri Mar  8 17:06:05 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:38684         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38690         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38678         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38672         127.0.0.1:3000          TIME_WAIT   -               
Fri Mar  8 17:06:08 CST 2019

   可以看到,最后有4条 time_wait ,都是由 nginx 随机端口 到 node 3000端口的连接,这说明 nginx 请求成功之后就主动断开了连接,导致time_wait状态

  2. 开启keepalive测试

  这次先在一个窗口 执行: for n in 1 2 3 4 ;do curl localhost:1000;date; done 

   同时在另一个窗口,连续执行 (curl localhost:1000 &) 

(curl localhost:1000 &)
root@localhost:/proc# (curl localhost:1000 &)
root@localhost:/proc# (curl localhost:1000 &)
root@localhost:/proc# Hello World
Hello World
Hello World

   注意:这3次执行,并没有停顿。 也就是说这3个请求是异步并发的

   再看看这次的监视结果 
 

root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:38744         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38738         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker 
tcp        0      0 127.0.0.1:38754         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38760         ESTABLISHED 1324/node       
tcp        0      0 127.0.0.1:3000          127.0.0.1:38738         ESTABLISHED 1324/node       
tcp        0      0 127.0.0.1:38760         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker 
Fri Mar  8 17:14:34 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:38744         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38738         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker 
tcp        0      0 127.0.0.1:38754         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38738         ESTABLISHED 1324/node       
tcp        0      0 127.0.0.1:38760         127.0.0.1:3000          TIME_WAIT   -               
Fri Mar  8 17:14:37 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:38744         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38738         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker 
tcp        0      0 127.0.0.1:38754         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38738         ESTABLISHED 1324/node       
tcp        0      0 127.0.0.1:38760         127.0.0.1:3000          TIME_WAIT   -               
Fri Mar  8 17:14:40 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:38744         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38738         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker 
tcp        0      0 127.0.0.1:38754         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38738         ESTABLISHED 1324/node       
tcp        0      0 127.0.0.1:38760         127.0.0.1:3000          TIME_WAIT   -               
Fri Mar  8 17:14:43 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:38744         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:38754         127.0.0.1:3000          TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38738         TIME_WAIT   -               
tcp        0      0 127.0.0.1:38760         127.0.0.1:3000          TIME_WAIT   -               
Fri Mar  8 17:14:46 CST 2019

  分析监测结果 :
  1) 由于3个窗口切换,操作不是很及时,所以第一次监测结果时已经有了time_wait
  2) 最后有4 个time_wait, 其中3个是 随机端口到 node 3000端口的(这是异步请求对应的连接)      
  3) 另一个是反过来的 3000---38738(这是循环请求使用的连接),可以看到这两个端口之间的连接在中间过程一直是 ESTABLISHED 状态。
    tcp        0      0 127.0.0.1:38738         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker     
    tcp        0      0 127.0.0.1:3000          127.0.0.1:38738         ESTABLISHED 1324/node

 3. 开启keepalive 但发起请求的间隔时间长

   循环请求,每次睡眠5s:
   

root@localhost:/www/nginx_noinlcude# for n in 1 2 3 4 ;do curl localhost:1000;date;sleep 5s; done
Hello World
Fri Mar  8 17:33:48 CST 2019
Hello World
Fri Mar  8 17:33:56 CST 2019
Hello World
Fri Mar  8 17:34:04 CST 2019
Hello World
Fri Mar  8 17:34:12 CST 2019

    监测结果:

root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:38774         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker 
tcp        0      0 127.0.0.1:3000          127.0.0.1:38774         ESTABLISHED 1324/node       
Fri Mar  8 17:33:50 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:38780         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker 
tcp        0      0 127.0.0.1:3000          127.0.0.1:38780         ESTABLISHED 1324/node       
tcp        0      0 127.0.0.1:3000          127.0.0.1:38774         TIME_WAIT   -               
Fri Mar  8 17:33:54 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:3000          127.0.0.1:38780         TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38774         TIME_WAIT   -               
tcp        0      0 127.0.0.1:38786         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker 
tcp        0      0 127.0.0.1:3000          127.0.0.1:38786         ESTABLISHED 1324/node       
Fri Mar  8 17:34:07 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:3000          127.0.0.1:38780         TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38792         ESTABLISHED 1324/node       
tcp        0      0 127.0.0.1:3000          127.0.0.1:38774         TIME_WAIT   -               
tcp        0      0 127.0.0.1:38792         127.0.0.1:3000          ESTABLISHED 3873/nginx: worker 
tcp        0      0 127.0.0.1:3000          127.0.0.1:38786         TIME_WAIT   -               
Fri Mar  8 17:34:13 CST 2019
root@localhost:~# netstat -anp | grep 3000  && date
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1324/node       
tcp        0      0 127.0.0.1:3000          127.0.0.1:38780         TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38792         TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38774         TIME_WAIT   -               
tcp        0      0 127.0.0.1:3000          127.0.0.1:38786         TIME_WAIT   -               
Fri Mar  8 17:34:25 CST 2019

   结果分析:
     1) 由于每次睡眠了5s, 所以虽然设置了 keepalive,但node已经等不及了,就主动关闭了连接(是node主动关闭)   
     2) 由于无空闲连接可以使用,每次请求新开辟了一个连接,最终全由node主动关闭,造成4个time_wait

 结论

  1. keepalive 不设置的话nginx不会重用连接

  2. keepalive 的值 表示 nginx 缓存的最大空闲连接,但这个连接的保持时间是有限的。
      至于这个时间是多长,在我这里发现就是5s。
      另外:nginx1.15.3之后可以在upstream里设置keepalive_timeout,但这里显示的是node主动关闭,是否能生效还需要测试
  3. keepalive 缓存的连接只有在空闲的时候才能被新的请求重用,如果请求过多,空闲连接不足,nginx仍然会发起新的连接,从而在nginx端保留很多time_wait。 所以keepalive的值需要根据实际情况调整。
  4. 理论上说 keepalive 的值应该这么算:

      假设平均请求量为 1000/s
      后端程序平均响应时长为:1s
      nginx woker进程数 : 16
      那么 keepalive的值 = 1000/16 ==62.5   大概64就好。

     这只是理论值,具体情况还需要根据可以根据服务器运行情况调整。

 

<think>好的,我需要解决用户关于Linux服务器上出现大量CLOSE_WAIT状态的问题。首先,我得回顾一下TCP连接的状态转换过程,尤其是CLOSE_WAITTIME_WAIT的区别。根据引用[5],CLOSE_WAIT出现在被动关闭连接的一方,也就是服务器在接收到客户端的FIN报文后进入这个状态,等待应用程序关闭连接。而TIME_WAIT则是主动关闭连接的一方在发送最后一个ACK后进入的状态,用来处理可能延迟的报文。 用户的问题是服务器上有大量CLOSE_WAIT,这说明服务器端的应用程序没有正确关闭连接。引用[4]提到,检测到对端关闭后,node.js会自动关闭本端socket,但如果应用程序没有执行close操作,就会导致CLOSE_WAIT堆积。因此,根本原因可能在于代码中没有正确释放连接资源。 接下来,我需要整理解决方案的步骤。首先,应该检查应用程序代码,确保所有socket连接在使用后正确关闭,特别是数据库连接、文件句柄等资源。引用[3]指出服务端使用短连接时,主动关闭连接会导致TIME_WAIT,但这里的问题不同,是CLOSE_WAIT,所以重点在代码层面的资源释放。 然后,查看系统参数,虽然引用[2]提到TIME_WAIT可以通过优化服务器参数解决,但CLOSE_WAIT更多是应用程序的问题,系统参数调整可能有限,但可以检查文件描述符限制,比如ulimit -n的设置,避免资源耗尽。 接下来,网络排查。使用netstat或ss命令查看具体连接情况,确认是哪些进程导致的CLOSE_WAIT,结合lsof或/proc文件系统定位到具体进程和代码位置。例如,使用netstat -ae | grep CLOSE_WAIT,类似引用[1]中的命令,但替换为CLOSE_WAIT状态。 另外,引用[3]提到服务端主动关闭连接导致TIME_WAIT,但这里的情况是服务端被动关闭,所以需要确保应用程序在收到FIN后及时close。如果是HTTP服务,检查是否使用了Keep-Alive,适当调整超时时间,避免连接长时间未关闭。 可能还需要使用压力测试工具模拟高并发场景,观察CLOSE_WAIT的增长情况,验证修复措施是否有效。同时,监控工具如Prometheus或Grafana可以帮助实时监控连接状态变化。 最后,总结解决方案步骤,从代码检查、资源释放、系统参数、网络排查到监控优化,逐步排除问题。需要提醒用户,CLOSE_WAIT问题通常由应用程序未正确关闭连接引起,与TIME_WAIT不同,后者更多是系统参数调整的问题,如引用[2]和[3]所述。 需要确保回答结构清晰,分步骤说明,并引用相关参考资料。同时,生成的相关问题要围绕TCP状态、区别、其他状态的解决方案等。</think>### 处理Linux服务器中大量CLOSE_WAIT连接的解决方案 #### 1. **理解CLOSE_WAIT状态的本质** CLOSE_WAIT是TCP连接关闭过程中的一个中间状态,表示**被动关闭方(通常是服务器)已收到主动关闭方(客户端)的FIN报文**,但尚未关闭本端连接[^5]。大量CLOSE_WAIT连接的堆积表明**应用程序未正确关闭socket连接**,导致资源无法释放[^4]。 --- #### 2. **排查与解决步骤** ##### **步骤1:检查应用程序代码** - **核心问题**:未显式调用`close()`方法释放连接。 - **常见场景**: - 数据库连接未关闭 - HTTP请求未关闭响应流 - 文件句柄泄漏 - **代码修复示例**(以Python为例): ```python # 错误示例:未关闭socket def handle_request(client_socket): data = client_socket.recv(1024) # 处理请求但未关闭连接 # 正确示例:显式关闭连接 def handle_request(client_socket): try: data = client_socket.recv(1024) # 处理请求 finally: client_socket.close() # 确保关闭 ``` ##### **步骤2:检查系统资源限制** - 使用命令查看当前进程打开的文件描述符数量: ```bash lsof -p <进程PID> | wc -l ``` - 调整系统文件描述符限制(临时生效): ```bash ulimit -n 65535 # 修改为更高限制 ``` ##### **步骤3:网络连接分析** - 使用`netstat`或`ss`命令定位问题: ```bash netstat -antop | grep CLOSE_WAIT # 查看所有CLOSE_WAIT连接 ss -s | grep CLOSE_WAIT # 更高效的统计方式 ``` - 结合`lsof`追踪具体进程: ```bash lsof -i :<端口号> # 查看占用端口的进程 ``` ##### **步骤4:优化连接管理策略** - **HTTP服务场景**: - 禁用不必要的Keep-Alive:减少长连接占用时间。 - 设置合理的超时时间(如Nginx配置): ```nginx keepalive_timeout 5; # 5秒后关闭空闲连接 ``` --- #### 3. **对比CLOSE_WAITTIME_WAIT的差异** | 状态 | 触发方 | 原因 | 解决方案 | |------------|----------|-------------------------------|------------------------------| | CLOSE_WAIT | 被动关闭 | 应用程序未关闭连接 | 修复代码逻辑 | | TIME_WAIT | 主动关闭 | 系统等待延迟报文 | 调整`tcp_tw_reuse`等内核参数[^3] | --- #### 4. **监控与验证** - **实时监控命令**: ```bash watch -n 1 "netstat -ant | grep CLOSE_WAIT | wc -l" # 每秒统计CLOSE_WAIT数量 ``` - **验证修复效果**: - 重启应用后观察CLOSE_WAIT是否减少。 - 使用压力测试工具(如`ab`、`wrk`)模拟高并发场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值