记录一次网关服务器time_wait数高的排查方法

396 阅读4分钟

认识time_wait

time_wait状态是tcp四次挥手中的一个正常状态。关于作用:

TIME_WAIT状态存在的理由:
1)可靠地实现TCP全双工连接的终止
   在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,
因此客户端必须维护状态信息允许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节,服务器将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。
因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭的客户端必须维持状态信息进入TIME_WAIT状态。
 
2)允许老的重复分节在网络中消逝 
TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。
在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身(incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。
为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。

排查过程(gw01)

  • 查看各状态连接数量:netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
  • 发现TIME_WAIT数为十几万,而连接中的数量只有三万多。
    通过netstat -an|grep TIME_WAIT|grep $port|wc -l
    看指定端口的tw连接数,发现443上的tw数有十二万,内部服务端口上的tw连接数两万多
  • 针对443上的tw,刚开始分析时存在一个误区,因为根据client<->server模式,以为只有客户端才会有tw。而443的连接明显是外部请求的打到nginx上,一直想不太通服务端会出现大量tw。经过详细阅读tcp及http keep-alive后,想明白了一个问题,tw并不只有client端出现,而是谁主动断开连接谁就会出现,而http机制中服务端和客户端都可能去断开连接。针对443 tw,写了一个client并发去请求,当客户端为http2及开启keep-alive(默认是走的http1.1,自动开启keep-alive)时在服务端会出现较多的tw;当客户端关闭keep-alive(设置header中connection为close)时,服务端只有少量tw,所以这里理解非keep-alive的请求主要是客户端来断开连接,而keep-alive的连接主要由服务端来断开。
  • 非443的tw,查看发现主要nginx -> server的连接。应该是nginx到服务程序之间用的是短连接,将nginx与server间配置成长连接,tw减少十分之间1以下
upstream web_delivery {
     server 127.0.0.1:8082;
     keepalive 200;
}
location / {
        proxy_pass http://web_delivery;
        proxy_set_header Connection "";
        proxy_http_version 1.1; 
}
  • 如果在业务机上还会出现一类tw,就是业务服务内部请求其他内部服务或组件(memcached,db等)以及请求外部服务。针对内部组件,在连接时可将连接池设大一点。针对外部连接尽量用keep-alive的方式,并调大最大空闲连接数。
  • 最后还有一种方法来控制tw就是调整sysctl参数,针对tw的配置参数主要有三个
net.ipv4.tcp_max_tw_buckets = 131072
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 0

net.ipv4.tcp_max_tw_buckets 参数可以控制tw总量,当连接中tw数量超过时会把tw状态的连接直接转为close。一般情况下这个值是系统根据内存自动计算的。
net.ipv4.tcp_tw_recycle 参数可以快速回收tw连接,不过这个谨慎打开,据说有附作用,blog.csdn.net/zhuyiquan/a…
net.ipv4.tcp_tw_reuse 参数是让tw状态连接重新给新的连接,可打开。

  • 最后总结,tw是tcp协议中一个正常的状态。如果tw是外部请求造成,而且机器连接数并没有超出机器限制时,没有特别大的必要性通过sysctl强制控制。如何怕占用业务服务端口,可以通过设置预留端口:
net.ipv4.ip_local_reserved_ports = 38208-48253