Resin服务器出现大量的ESTABLISHED和TIME_WAIT连接造成响应缓慢

故障描述

Resin服务的端口为8080,执行 lsof -i :8080 命令出现大量的ESTABLISHED连接:

然后执行netstat -n | awk '/^tcp/ {++y[$NF]} END {for(w in y) print w, y[w]}' 命令发现存在大量状态为TIME_WAIT的连接:

简单来说,ESTABLISHED表示正在进行网络连接的数量,TIME_WAIT表示表示等待系统主动关闭网络连接的数量,CLOSE_WAIT表示被动等待程序关闭的网络连接数量,因此TIME_WAIT跟服务器的配置有关,而CLOSE_WAIT跟程序进行网络连接有关了,通常是程序没有主动关闭网络连接所致。

逻辑代码中没有释放网络资源

首先检查确保,在逻辑代码中是否没有及时释放网络资源。通常如果出现大量的CLOSE_WAIT说明程序在进行网络连接的时候没有主动关闭连接,比如使用HttpClient调用网络连接的时候,当出现错误的时候没有调用abort()方法,或者网络连接结束之后没有调用close方法等等,具体在源代码中进行排查即可。

Nginx设置

Nginx 1.1以上版本的upstream开始支持keep-alive参数了,可以开启proxy的keep-alive来减少TPC的连接。
其实现原理摘抄如下:

Nginx目前的upstream连接建立和获取的机制如下图。Nginx会在一开始创建connection pool(进程间不共享,可以避免锁),提供给所有向前/后的连接。

如果要实现upstream长连接,则每个进程需要另外一个connection pool,里面都是长连接。一旦与后端服务器建立连接,则在当前请求连接结束之后不会立即关闭连接,而是把用完的连接保存在一个keepalive connection pool里面,以后每次需要建立向后连接的时候,只需要从这个连接池里面找,如果找到合适的连接的话,就可以直接来用这个连接,不需要重新创建socket或者发起connect()。这样既省下建立连接时在握手的时间消耗,又可以避免TCP连接的slow start。如果在keepalive连接池找不到合适的连接,那就按照原来的步骤重新建立连接。假设连接查找时间可以忽略不计,那么这种方法肯定是有益而无害的(当然,需要少量额外的内存)。

可以使用如下配置:

upstream http_backend {
 server 127.0.0.1:8080;

keepalive 16;
}

server {
 ...

location /http/ {
 proxy_pass http://http_backend;
 proxy_http_version 1.1;
 proxy_set_header Connection "";
 ...
 }
}

需要注意的是,keepalive参数并非限定upstream server总的连接数,而是指定了持续连接的数量,这个参数不能设置太大,否则会造成upstream无法建立新的连接的问题。在实际的应用中不断地去调整该参数,保持一种平衡。

如果Nginx没有使用HTTP长连接请求后端,后端处理完后就主动关闭连接,因此TIME_WAIT发生在后端;如果Nginx使用了HTTP长连接,建立连接后,后端认为是「长连接」而不会主动关闭连接(一般有个空闲超时),关闭连接由 Nginx 来做了,所以 Nginx 会出现大量的 TIME_WAIT。

Linux参数调整

如果没有使用Nginx的话,也可以对Linux的系统参数进行调整。对 /etc/sysctl.conf 文件做如下设置(包含每个配置项的说明):

#对于一个新建连接,内核要发送多少个 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

修改保存之后执行 /sbin/sysctl -p让参数生效即可。这里需要说明几点:
(1) net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源,net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间,net.ipv4.tcpkeepalive*一系列参数,是用来设置服务器检测连接存活的相关配置。
(2) 如果客户端访问是NAT网络(即:局域网内多台电脑使用一个公网IP上网),不要开启net.ipv4.tcp_tw_recycle或者在服务端关闭time stamp (echo 0 /proc/sys/net/ipv4/tcp_timestamps),参考这篇文章:不要在linux上启用net.ipv4.tcp_tw_recycle参数【经验总结】tcp_tw_recycle参数引发的故障
(3) Windows服务器的解决方法请参考这篇文字:在 Windows 上遇到非常多 TIME_WAIT 連線時應如何處理

参考资料:

HttpClient连接池抛出大量ConnectionPoolTimeoutException: Timeout waiting for connection异常排查
httpClient connection not closing
Nginx做前端Proxy时TIME_WAIT过多的问题
再叙TIME_WAIT
Nginx upstream 长连接
关于 Nginx upstream keepalive 的说明
优化nginx upstream连接
记一次压测引起的nginx负载均衡性能调优
再谈应用环境下的TIME_WAIT和CLOSE_WAIT
【经验总结】tcp_tw_recycle参数引发的故障
不要在linux上启用net.ipv4.tcp_tw_recycle参数
Tcp 连接出现大量ESTABLISHED连接
resin 调优

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注