TCP的CLOSE_WAIT和TIME_WAIT问题汇总

线上环境挂了,咋整?

排查思路:

  • 是不是特例还是所有情况下的数据都获取不到?

  • 是不是网络断了(比如某厂的光缆又断了?)

  • 是不是服务停了 (Sig 11?OOM?或者core dump)

  • 是不是应用服务器都CPU 100%了?

  • 看看监控系统有没有报警? (当然得有对吧)

  • 看看DB是不是被人删了?(进过某旅游网站的事件后,这总也是一种可能行对吧)

有云监控,可以看下

  • SLB的心跳还活着,排除网络问题

  • 所有服务器的CPU/Memory/IO都还正常没有峰值

  • 关键进程还在

  • DB也还健在(还好 还好)

排查NGINX日志

  • resp_time和upstream_time响应时间
  • wirashark分析,tcpdump tcp port 80 -w staging.pcap
  • watch -d -n1 ‘netstat -s | grep reset’

SSH上去看系统

  • “先听听操作系统的声音,让操作系统来告诉你问题在哪”
  • 从网络开始,netstat -anp tcp 查看tcp连接状态分布
  • Foreign Address,这个就是代表对方的IP地址

TCP参数配置

net.ipv4.tcp_timestamps

RFC 1323 在 TCP Reliability一节里,引入了timestamp的TCP option,两个4字节的时间戳字段,其中第一个4字节字段用来保存发送该数据包的时间,第二个4字节字段用来保存最近一次接收对方发送到数据的时间。有了这两个时间字段,也就有了后续优化的余地。

tcp_tw_reuse 和 tcp_tw_recycle就依赖这些时间字段。

net.ipv4.tcp_tw_reuse

字面意思,reuse TIME_WAIT状态的连接。

时刻记住一条socket连接,就是那个五元组,出现TIME_WAIT状态的连接,一定出现在主动关闭连接的一方。所以,当主动关闭连接的一方,再次向对方发起连接请求的时候(例如,客户端关闭连接,客户端再次连接服务端,此时可以复用了;负载均衡服务器,主动关闭后端的连接,当有新的HTTP请求,负载均衡服务器再次连接后端服务器,此时也可以复用),可以复用TIME_WAIT状态的连接。

通过字面解释,以及例子说明,你看到了,tcp_tw_reuse应用的场景:某一方,需要不断的通过“短连接”连接其他服务器,总是自己先关闭连接(TIME_WAIT在自己这方),关闭后又不断的重新连接对方。

那么,当连接被复用了之后,延迟或者重发的数据包到达,新的连接怎么判断,到达的数据是属于复用后的连接,还是复用前的连接呢?那就需要依赖前面提到的两个时间字段了。复用连接后,这条连接的时间被更新为当前的时间,当延迟的数据达到,延迟数据的时间是小于新连接的时间,所以,内核可以通过时间判断出,延迟的数据可以安全的丢弃掉了。

这个配置,依赖于连接双方,同时对timestamps的支持。同时,这个配置,仅仅影响outbound连接,即做为客户端的角色,连接服务端[connect(dest_ip, dest_port)]时复用TIME_WAIT的socket。

net.ipv4.tcp_tw_recycle

字面意思,销毁掉 TIME_WAIT。

当开启了这个配置后,内核会快速的回收处于TIME_WAIT状态的socket连接。多快?不再是2MSL,而是一个RTO(retransmission timeout,数据包重传的timeout时间)的时间,这个时间根据RTT动态计算出来,但是远小于2MSL。

有了这个配置,还是需要保障 丢失重传或者延迟的数据包,不会被新的连接(注意,这里不再是复用了,而是之前处于TIME_WAIT状态的连接已经被destroy掉了,新的连接,刚好是和某一个被destroy掉的连接使用了相同的五元组而已)所错误的接收。在启用该配置,当一个socket连接进入TIME_WAIT状态后,内核里会记录包括该socket连接对应的五元组中的对方IP等在内的一些统计数据,当然也包括从该对方IP所接收到的最近的一次数据包时间。当有新的数据包到达,只要时间晚于内核记录的这个时间,数据包都会被统统的丢掉。

这个配置,依赖于连接双方对timestamps的支持。同时,这个配置,主要影响到了inbound的连接(对outbound的连接也有影响,但是不是复用),即做为服务端角色,客户端连进来,服务端主动关闭了连接,TIME_WAIT状态的socket处于服务端,服务端快速的回收该状态的连接。

由此,如果客户端处于NAT的网络(多个客户端,同一个IP出口的网络环境),如果配置了tw_recycle,就可能在一个RTO的时间内,只能有一个客户端和自己连接成功(不同的客户端发包的时间不一致,造成服务端直接把数据包丢弃掉)。

查看TCP连接状态脚本

while true;
do 
netstat -atnp | awk '/^tcp/ {++S[$6]} 
END 
{for(a in S) print a, S[a]}';
sleep 5;
echo --------------------;
done

优化的LINUX网络配置建议

vi /etc/sysctl.conf

net.ipv6.conf.all.disable_ipv6 =1

net.ipv4.conf.all.arp_announce=2

net.ipv4.conf.lo.arp_announce=2

fs.file-max=65535

net.core.netdev_max_backlog=30000

net.core.somaxconn=10000

net.core.rps_sock_flow_entries=32768

net.ipv4.tcp_max_syn_backlog=10000

net.ipv4.tcp_max_tw_buckets=10000

net.ipv4.tcp_fin_timeout=10

net.ipv4.tcp_timestamps=1

net.ipv4.tcp_tw_recycle=0

net.ipv4.tcp_tw_reuse=0

net.ipv4.tcp_synack_retries=2

net.ipv4.tcp_window_scaling=1

net.ipv4.tcp_keepalive_time=180

net.ipv4.tcp_keepalive_intvl=30

net.ipv4.tcp_keepalive_probes=5

net.core.wmem_default=8388608

net.core.rmem_default=8388608

net.core.rmem_max=16777216

net.core.wmem_max=16777216

net.ipv4.tcp_syncookies=1

net.ipv4.tcp_mem=94500000915000000 927000000

net.ipv4.tcp_max_orphans=3276800

net.ipv4.ip_local_port_range=102465535

 

FAQ

SLB 作为负载均衡,基本没有业务逻辑,那它会主动关闭连接的场景有哪些?

  • 进程退出(正常或者非正常)

  • TCP 连接超时

为啥一台机器区区几百个close_wait就导致不可继续访问?不合理啊,一台机器不是号称最大可以开到65535个端口吗?

  1. Tornado是单进程启动的服务,所以IOLoop也就一个实例在监听并轮询

  2. Tornado在bind每个socket的时候有默认的链接队列(也叫backlog)为128个

  3. 由于代码错误,我们使用了同步库urllib2 做第三方请求,导致访问第三方的时候当前RequestHandler是同步的(yield不起作用),因此当IOLoop回调这个RequestHandler时会等待它返回

  4. 第三方接口真的不快!

[回答]: 由于原因#4和#3所以导致整个IOLoop慢了,进而因为#2 导致很多请求堆积,也就是说很多请求在被真正处理前已经在backlog里等了一会了。

导致了SLB这端的链接批量的超时,同时又由于close_wait状态不会自动消失,导致最终无法再这个端口上创建新的链接引起了停止服务

为啥明明有多个服务器承载,却几乎同时出了close_wait? 又为什么同时不能再服务?那要SLB还有啥用呢

[回答]: 有了上一个答案,结合SLB的特性,这个也就很好解释。

这就是所谓的洪水蔓延,当SLB发现下面的一个节点不可用时会吧请求routing到其他可用节点上,导致了其他节点压力增大。也犹豫相同原因,加速了其他节点出现close_wait.

所说连接池可以复用连接,是不是意味着,需要等到上个连接time wait结束后才能再次使用?

[回答]: 

所谓连接池复用,复用的一定是活跃的连接,所谓活跃,

第一表明连接池里的连接都是ESTABLISHED的,

第二,连接池做为上层应用,会有定时的心跳去保持连接的活跃性。既然连接都是活跃的,那就不存在有TIME_WAIT的概念了,TIME_WAIT是在主动关闭连接的一方,在关闭连接后才进入的状态。既然已经关闭了,那么这条连接肯定已经不在连接池里面了,即被连接池释放了。

作为负载均衡的机器随机端口使用完的情况下大量time_wait,不调整文字里说的那三个参数,有其他的更好的方案吗?

[回答]: 

第一,随机端口使用完,你可以通过调整/etc/sysctl.conf下的net.ipv4.ip_local_port_range配置,至少修改成 net.ipv4.ip_local_port_range=1024 65535,保证你的负载均衡服务器至少可以使用6万个随机端口,也即可以有6万的反向代理到后端的连接,可以支持每秒1000的并发(想一想,因为TIME_WAIT状态会持续1分钟后消失,所以一分钟最多有6万,每秒1000);

如果这么多端口都使用完了,也证明你应该加服务器了,或者,你的负载均衡服务器需要配置多个IP地址,或者,你的后端服务器需要监听更多的端口和配置更多的IP(想一下socket的五元组)

第二,大量的TIME_WAIT,多大量?如果是几千个,其实不用担心,因为这个内存和CPU的消耗有一些,但是是可以忽略的。

第三,如果真的量很大,上万上万的那种,可以考虑,让后端的服务器主动关闭连接,如果后端服务器没有外网的连接只有负载均衡服务器的连接(主要是没有NAT网络的连接),可以在后端服务器上配置tw_recycle,然后同时,在负载均衡服务器上,配置tw_reuse。

因为TIME_WAIT状态会持续1分钟后消失,所以一分钟最多有6万,每秒1000,为什么?

[回答]: 这个问题还没找到

 

 

 

参考链接

https://mp.weixin.qq.com/s?__biz=MzI4MjA4ODU0Ng==&mid=402163560&idx=1&sn=5269044286ce1d142cca1b5fed3efab1&3rd=MzA3MDU4NTYzMw==&scene=6#rd

https://mp.weixin.qq.com/s/5S39zO3jrsQC1QwC1yye_A

https://mp.weixin.qq.com/s?__biz=MzI4MjA4ODU0Ng==&mid=402415747&idx=1&sn=2458ba4fe1830eecdb8db725d3f395fa&scene=21#wechat_redirect

https://mp.weixin.qq.com/s/AkWEQtcYYaWsE6VzHny5sw

https://mp.weixin.qq.com/s/AQrx06StJ6p4fY0bD6faqA

 

 

 

 

 

你可能感兴趣的:(tcp)