本文主要参考链接 :https://www.jianshu.com/p/6eaac07c93ed
其它链接 :https://www.oschina.net/translate/optimising-nginx-node-js-and-networking-for-heavy-workloads?print
背景
1. nginx 代理 nodejs, 但都在同一台服务器上
2. nodejs 的端口是3000
现象
1. 服务器端 出现大量TIME_WAIT
查询方式
1)ss查看比netstat要快:
ss -s #查看TW的数量
2)netstat方法:
shell> netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'
2. 造成的问题
服务器存在大量TIME_WAIT链接,导致无法为新的请求分配链接
3. 参考解决方案
参考 :Nginx做前端Proxy时TIME_WAIT过多的问题 http://www.cnblogs.com/QLeelulu/p/3601499.html
要点
- 修改系统预留端口,防止自己进程监听的大于1024的端口被占用
- 解决TW过多:修改nginx upstream配置中的keepalive时长并且在server段中使用http1.1(1.0不支持keepalive)
upstream http_backend {
server 127.0.0.1:8080;keepalive 16;
#此参数表示上每个worker进程保持空闲连接的最大数量,这个数量不能太大以致无法分配其它链接
#也不能太小,以致于没有足够的可重用的连接,导致开辟大量的短连接,最后仍是很多TIME_WAIT
#nginx总的keepalive连接池大小 = keepalive 取值 * nginx worker数
#可参考官方文档 :http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
}server {
...location /http/ {
proxy_pass http://http_backend;
proxy_http_version 1.1; #使用http1.1
proxy_set_header Connection ""; # 置空头信息中的connection,否则有可能会是 closed
...
}
}
linux相关内核参数优化 参考:
https://huoding.com/2013/12/31/316
要点:
- ip_conntrack:顾名思义就是跟踪连接。(引入问题过多不建议使用)
- tcp_tw_recycle:顾名思义就是回收TIME_WAIT连接。(NAT模式下有问题,产生时间戳混乱,不建议使用)
- tcp_tw_reuse:顾名思义就是复用TIME_WAIT连接。(作为连接的发起方(Client)有用(需网络稳定),作为被连接方基本没用)
- tcp_max_tw_buckets:顾名思义就是控制TIME_WAIT总数。(治标不治本,限制tw队列大小系统日志会报「TCP: time wait bucket table overflow」)
有时候,如果我们换个角度去看问题,往往能得到四两拨千斤的效果。前面提到的例子:客户端向服务端发起HTTP请求,服务端响应后主动关闭连接,于是TIME_WAIT便留在了服务端。这里的关键在于主动关闭连接的是服务端!在关闭TCP连接的时候,先出手的一方注定逃不开TIME_WAIT的宿命,套用一句歌词:把我的悲伤留给自己,你的美丽让你带走。如果客户端可控的话,那么在服务端打开KeepAlive,尽可能不让服务端主动关闭连接,而让客户端主动关闭连接,如此一来问题便迎刃而解了。
参数配置示例: (参考 :https://www.oschina.net/translate/optimising-nginx-node-js-and-networking-for-heavy-workloads?print
(为高负载网络优化 Nginx 和 Node.js ))
net.ipv4.ip_local_port_range='1024 65000'
net.ipv4.ip_local_reserved_ports = 你要保留的端口如(3000,27017-27018)
net.ipv4.tcp_tw_reuse='1'
net.ipv4.tcp_fin_timeout='15'
net.core.netdev_max_backlog='4096'
net.core.rmem_max='16777216'
net.core.somaxconn='4096'
net.core.wmem_max='16777216'
net.ipv4.tcp_max_syn_backlog='20480'
net.ipv4.tcp_max_tw_buckets='400000'
net.ipv4.tcp_no_metrics_save='1'
net.ipv4.tcp_rmem='4096 87380 16777216'
net.ipv4.tcp_syn_retries='2'
net.ipv4.tcp_synack_retries='2'
net.ipv4.tcp_wmem='4096 65536 16777216'
vm.min_free_kbytes='65536'
还有另一个示例:(来自https://blog.csdn.net/shootyou/article/details/6622226)
#对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间
net.ipv4.tcp_syn_retries=2
#net.ipv4.tcp_synack_retries=2
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒
net.ipv4.tcp_keepalive_time=1200
net.ipv4.tcp_orphan_retries=3
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout=30
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_syn_backlog = 4096
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1
##减少超时前的探测次数
net.ipv4.tcp_keepalive_probes=5
##优化网络设备接收队列
net.core.netdev_max_backlog=3000
重点说明其中几个重要的。
net.ipv4.ip_local_port_range
为了替上游的应用服务下游的客户端,NginX必须打开两条TCP连接,一条连接客户端,一条连接应用。在服务器收到很多连接时,系统的可用端口将很快被耗尽。通过修改net.ipv4.ip_local_port_range参数,可以将可用端口的范围改大。如果在/var/log/syslog中发现有这样的错误: “possible SYN flooding on port 80. Sending cookies”,即表明系统找不到可用端口。增大net.ipv4.ip_local_port_range参数可以减少这个错误。 但要注意保留正常的服务端口(net.ipv4.ip_local_reserved_ports)。
这个参数的默认设置可通过下面的命令查看(也可以在这里修改):
cat /proc/sys/net/ipv4/ip_local_port_range
32768 609995
net.ipv4.ip_local_reserved_ports
因为net.ipv4.ip_local_port_range扩大的范围,所以有可能造成正常服务在重启过程中,端口在服务器分配随机端口时被占用,导致不能正常重启。 所以可以通过这个参数来指定需要保留的端口。
参考:http://www.ttlsa.com/linux/reserved-port-to-avoid-occupying-ip_local_reserved_ports/
net.ipv4.tcp_tw_reuse
当服务器需要在大量TCP连接之间切换时,会产生大量处于TIME_WAIT状态的连接。TIME_WAIT意味着连接本身是关闭的,但资源还没有释放。将net_ipv4_tcp_tw_reuse设置为1是让内核在安全时尽量回收连接,这比重新建立新连接要便宜得多。
net.ipv4.tcp_fin_timeout
这是处于TIME_WAIT状态的连接在回收前必须等待的最小时间。改小它可以加快回收。
4. 需要注意的地方
因为我的情况是 nginx 和 node 在一台机器上,要么nginx保留很多TIME_WAIT,要么NODE保留很多TIME_WAIT ,所以让客户端主动关闭连接的方式对于这种情况不能走到什么效果,重点是要提高重用链接数量。
nginx总的keepalive连接池大小 = keepalive 取值 * nginx worker数
5. 其它案例分析
参考 :https://blog.csdn.net/liuxiao723846/article/details/50747366
案例一:
将nginx作为反向代理时,后连tomcat等服务器。测试中不同并发压力下多次、反复出现nginx服务器端口资源耗尽的问题。表现为nginx服务器出现大量time_wait状态连接,端口资源耗尽(nginx报错:cannot assign requested address )。首先检查nginx开启了长连接keepalive,但是系统仍然出现了大量的TIME_WAIT,这就和之前提到的当系统产生TIME_WAIT的速度大于其消耗速度时,就会累计TIME_WAIT。原因是:keepalive取配置太小,将其增大后问题得以解决。(PS:nginx总的keepalive连接池大小 = keepalive取值 * nginx worker数)
案例二:
某应用其中一层系统架构Nginx+Tomcat,tomcat服务器上出现了大量的TIME_WAIT。
Nginx作为反向代理,长连接配置主要有三项,upstream中的keepalive设置单个worker最大请求数,参数proxy_http_version 1.1强制转换为http1.1协议(默认支持长连接),proxy_set_header Connection将请求头部connection为空(http1.0请求默认connection头部为close),后两项要放到server域中,配置后问题即可解决。
但是我们不禁会尝试疑问TIME_WAIT出现在Tomcat而不是在Nginx上?从抓包可以看出Nginx发送给Tomcat包头部Connection为close,所以Tomcat在处理完head请求后就主动关闭,所以TIME_WAIT出现在Tomcat服务器。