资讯专栏INFORMATION COLUMN

出现大量TIME_WAIT连接的排查与解决

myshell / 4348人阅读

摘要:年月日项目生产环境出现大量数千个需要一一排查先上总结未开启导致大量主动断开的连接与的连接默认是短连接此时必然出现状态确认统计连接的本地地址前面很少的略过分析端口是对外端口端口是的端口对外端口经过确认的配置文件中存在一行不启用

Last-Modified: 2019年7月10日21:58:43

项目生产环境出现大量TIME_WAIT(数千个), 需要一一排查

先上总结:

nginx 未开启 keep-alive 导致大量主动断开的tcp连接

nginx 与 fastcgi(php-fpm) 的连接默认是短连接, 此时必然出现 TIME_WAIT


状态确认

统计TIME_WAIT 连接的本地地址

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

#    ... 前面很少的略过
#    2 127.0.0.1:56420
#    442 192.168.1.213:8080
#    453 127.0.0.1:9000

分析:

8080端口是nginx对外端口

9000端口是php-fpm的端口

8080 对外web端口

经过确认, nginx 的配置文件中存在一行

# 不启用 keep-alive
keepalive_timeout 0;

尝试抓取 tcp 包

tcpdump tcp -i any -nn port 8080 | grep "我的ip"

# 其中某一次连接的输出如下
# 20:52:54.647907 IP 客户端.6470 > 服务端.8080: Flags [S], seq 2369523978, win 64240, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0
# 20:52:54.647912 IP 服务端.8080 > 客户端.6470: Flags [S.], seq 1109598671, ack 2369523979, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
# 20:52:54.670302 IP 客户端.6470 > 服务端.8080: Flags [.], ack 1, win 256, length 0
# 20:52:54.680784 IP 客户端.6470 > 服务端.8080: Flags [P.], seq 1:301, ack 1, win 256, length 300
# 20:52:54.680789 IP 服务端.8080 > 客户端.6470: Flags [.], ack 301, win 123, length 0
# 20:52:54.702935 IP 服务端.8080 > 客户端.6470: Flags [P.], seq 1:544, ack 301, win 123, length 543
# 20:52:54.702941 IP 服务端.8080 > 客户端.6470: Flags [F.], seq 544, ack 301, win 123, length 0
# 20:52:54.726494 IP 客户端.6470 > 服务端.8080: Flags [.], ack 545, win 254, length 0
# 20:52:54.726499 IP 客户端.6470 > 服务端.8080: Flags [F.], seq 301, ack 545, win 254, length 0
# 20:52:54.726501 IP 服务端.8080 > 客户端.6470: Flags [.], ack 302, win 123, length 0
上述具体的ip已经被我批量替换了, 不方便暴露服务器ip

分析:

可以看到4次挥手的开始是由服务端主动发起的(记住TIME_WAIT只会出现在主动断开连接的一方)

个人理解是, nginx 在配置"不启用keep-alive"时, 会在http请求结束时主动断开连接.

尝试开启http的keep-alive

修改 nginx 配置

keepalive_timeout 65;

reload nginx

nginx -s reload

再次抓包

tcpdump tcp -i any -nn port 8080 | grep "我的ip"

# 21:09:10.044918 IP 客户端.8217 > 服务端.8080: Flags [S], seq 1499308169, win 64240, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0
# 21:09:10.044927 IP 服务端.8080 > 客户端.8217: Flags [S.], seq 2960381462, ack 1499308170, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
# 21:09:10.070694 IP 客户端.8217 > 服务端.8080: Flags [.], ack 1, win 256, length 0
# 21:09:10.077437 IP 客户端.8217 > 服务端.8080: Flags [P.], seq 1:302, ack 1, win 256, length 301
# 21:09:10.077443 IP 服务端.8080 > 客户端.8217: Flags [.], ack 302, win 123, length 0
# 21:09:10.198117 IP 服务端.8080 > 客户端.8217: Flags [P.], seq 1:671, ack 302, win 123, length 670
# 21:09:10.222957 IP 客户端.8217 > 服务端.8080: Flags [F.], seq 302, ack 671, win 254, length 0
# 21:09:10.222980 IP 服务端.8080 > 客户端.8217: Flags [F.], seq 671, ack 303, win 123, length 0
# 21:09:10.247678 IP 客户端.8217 > 服务端.8080: Flags [.], ack 672, win 254, length 0

注意看上面很有意思的地方:

tcp 的挥手只有3次, 而非正常的4次. 个人理解是, 服务端在收到 FIN 时, 已经确认自己不会再发送数据, 因此就将 FIN 与 ACK 一同合并发送

此时是客户端主动断开tcp连接, 因此服务端不会出现 TIME_WAIT

再次查看连接状态

netstat -an | grep TIME_WAIT | awk "{print $4}" | sort | uniq -c | sort -n -k1
#      ...忽略上面
#      1 127.0.0.1:60602
#      1 127.0.0.1:60604
#    344 127.0.0.1:9000

此时发现已经没有处于 TIME_WAIT 的连接了.

9000 fast-cgi 端口

经过网上查找资料, 整理:

nginx 与 fast-cgi 的默认连接是短连接, 每次连接都需要经过一次完整的tcp连接与断开

当前 nginx 配置

upstream phpserver{
    server 127.0.0.1:9000 weight=1;
}

修改nginx配置使其与fastcgi的连接使用长连接

upstream phpserver{
    server 127.0.0.1:9000 weight=1;
    keepalive 100
}

fastcgi_keep_conn on;

说明:

upstream 中的 keepalive 指定nginx每个worker与fastcgi的最大长连接数, 当长连接不够用时, 此时新建立的连接会在请求结束后断开(由于此时指定了 HTTP1.1, fastcgi不会主动断开连接, 因此nginx这边会出现大量 TIME_WAIT, 需谨慎(未验证)

由于php-fpm设置了最大进程数为100, 因此此处的 keepalive 数量指定 100 (未测试)

此处题外话, 如果 nginx 是作为反向代理, 则需增加如下配置:

# 将http版本由1.0修改为1.1
proxy_http_version 1.1;
# 清除"Connection"头部
proxy_set_header Connection "";    

配置 proxy_pass 将请求转发给后端

这里, 理解一下 proxy_passfastcgi_pass 区别

客户端 --http-->  前端负载均衡Nginx --proxy_pass--> 业务服务器Nginx --fastcgi_pass--> 业务服务器 php-fpm

再次确认 tcp 连接情况

netstat -antp  | grep :9000 | awk "{print $(NF-1)}" | sort | uniq -c
#      6 ESTABLISHED
#      1 LISTEN

ok, 问题解决.

另一种解决方法:

若 nginx 与 fast-cgi 在同一台服务器上, 则使用 unix域 会更为高效, 同时避免了 TIME_WAIT 的问题.

题外

经过上面优化后, TIME_WAIT数量从上千个大幅下降到几十个, 此时发现TIME_WAIT中的存在大量的 127.0.0.1:6379, 6379是redis服务的默认端口....

赶紧改业务代码去, 将 $redis->connect(...) 改成 $redis->pconnect(...)

说明:

pconnect 表示 php-fpm 与 redis 建立 tcp 连接后, 在本次http请求结束后仍维持该连接, 下次新的请求进来时可以复用该连接, 从而复用了tcp连接.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/40525.html

相关文章

  • 服务器TIME_WAIT和CLOSE_WAIT分析和解决办法

    摘要:服务器出现异常最长出现的状况是服务器保持了大量的状态。此时主动关闭一方必须保持一个有效的状态下维持状态信息,以便可以重发。这就意味着,一个成功建立的连接,必须使得之前网络中残余的数据报都丢失了。,维持这些状态给服务器端带来巨大的负担。 showImg(https://segmentfault.com/img/bV9DQk?w=732&h=563); showImg(https://se...

    LeanCloud 评论0 收藏0
  • 服务器TIME_WAIT和CLOSE_WAIT分析和解决办法

    摘要:服务器出现异常最长出现的状况是服务器保持了大量的状态。此时主动关闭一方必须保持一个有效的状态下维持状态信息,以便可以重发。这就意味着,一个成功建立的连接,必须使得之前网络中残余的数据报都丢失了。,维持这些状态给服务器端带来巨大的负担。 showImg(https://segmentfault.com/img/bV9DQk?w=732&h=563); showImg(https://se...

    helloworldcoding 评论0 收藏0
  • centos系统大量time wait占用解决

    摘要:发现存在大量状态的连接通过调整内核参数解决编辑文件,加入以下内容然后执行让参数生效。当出现等待队列溢出时,启用来处理,可防范少量攻击,默认为,表示关闭表示开启重用。 统计在一台前端机上高峰时间TCP连接的情况,统计命令:netstat -n | awk /^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]} 除了ESTABLISHED,可以看...

    betacat 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<