再谈 TCP 的 CLOSE_WAIT

栏目: 服务器 · 发布时间: 6年前

内容简介:某日集群告警,hbase regionserver 因 fd 不足导致进程主动退出,简单排查后发现regionserver 到 datanode 的TCP 连接存在大量 CLOSE_WAIT,单机总数有10万之多,众所周知,CLOSE_WAIT 产生的原因在于收到 FIN 请求后没有调用 close()函数导致,回顾一下 TCP 的正常关闭过程:无论出于任何原因程序没有执行 close,我们可以借助操作系统的keepalive机制将这些不活跃的连接关闭掉。在一个空闲的 TCP 连接上,不执行任何收发数据的操

背景

某日集群告警,hbase regionserver 因 fd 不足导致进程主动退出,简单排查后发现regionserver 到 datanode 的TCP 连接存在大量 CLOSE_WAIT,单机总数有10万之多,众所周知,CLOSE_WAIT 产生的原因在于收到 FIN 请求后没有调用 close()函数导致,回顾一下 TCP 的正常关闭过程:

再谈 TCP 的 CLOSE_WAIT

无论出于任何原因程序没有执行 close,我们可以借助操作系统的keepalive机制将这些不活跃的连接关闭掉。在一个空闲的 TCP 连接上,不执行任何收发数据的操作,当另一端掉电的时候,TCP 无法发现连接出现问题,keepalive机制会在多久没有收到数据之后发送探测包来进行检测,从而避免类似问题。

keepalive

要启用 TCP 的 keepalive 机制,必须在 socket 中设置了以下选项的时候才会生效:

 
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&opt, sizeof(opt));
 

发送方或接受方进行设置都可以生效,最终由设置了该选项了一段发送keepalive探测包(实际上是一个 flag 为 ACK 的空包),如果在 server 端设置,可以设置在监听的套接字上,accept 的新连接会继承这个该属性。

再谈 TCP 的 CLOSE_WAIT

linux 下,keepalive 有如下三个参数进行调整(以及他们的默认值):

 
net.ipv4.tcp_keepalive_intvl = 75    //探测间隔(秒)
net.ipv4.tcp_keepalive_probes = 9    //探测次数
net.ipv4.tcp_keepalive_time = 7200    //探测超时(秒)
 

这三个参数的含义,误的建议参考《UNIX网络编程》,按照默认值,上述三个参数的正确理解为:在一个TCP 连接上,keepalive_time时间内没有任何数据包传输,则开启keepalive的一段发送keepalive包,如果经过tcp_keepalive_time的时间后没有收到应答,则间隔tcp_keepalive_intvl的时间再次发送。经过tcp_keepalive_probes的次数后仍然没有收到应答,则发送 RST包关闭该连接。

如果对端正常回复了keepalive消息,则下一次keepalive包在等待keepalive_time时间后发送。

你可以通过 sysctl -w 来修改内核参数,或者修改/etc/sysctl.conf 后 sysctl -p 让参数生效,两种方式均无需重启系统或应用程序,且对当前已建立的 socket 动态生效。

虽然keepalive可以只在一端启用,但是仍然建议在 socket 两端同时进行设置。因为当一端调用 close 后,这段就不会再发送keepalive包。例如只有服务端开启,而客户端没有开启,当服务端调用了 close,服务端就不会再发送keepalive包,而如果客户端收到 FIN 后没有执行关闭,连接仍然会长期处于 CLOSE_WAIT状态。

keepalive && CLOSE_WAIT

搞清楚了 tcp 的keepalive,我们再看看keepalive与CLOSE_WAIT 关系。当CLOSE_WAIT最初产生时,主动关闭的一端处于FIN_WAIT_1或 FIN_WAIT_2状态,一般FIN_WAIT_1很快会结束,因为对端返回 ack 一般很快(长时间的FIN_WAIT_1与tcp_orphan_retries参数有关)。在主动关闭一端处于FIN_WAIT_2状态时,可以正常返回对端的keepalive消息,当FIN_WAIT_2超时,主动关闭这段会彻底释放这个连接,这时,对端的keepalive消息再发生过来之后,由于本端已关闭,会给对方回复一个 RST,对方收到这个 RST 后,协议栈就会清理这个 CLOSE_WAIT 状态的连接。

那么 FIN_WAIT_2超时时间是多久?在 linux 下,由 net.ipv4.tcp_fin_timeout参数决定,默认为60秒。

再谈 TCP 的 CLOSE_WAIT

上图的例子中,服务端监听7788端口,收到对方数据后就调用 close,然后持续 sleep,客户端开启 keepalive,keepalive_time设置为5,可以看到最后一个数据包是服务端对 keepalive 消息返回了 RST

如何解决问题?

回归本文主题,通过设置 socket 选项开启 keepalive机制,同时调整相关内核参数可以让系统清理掉 CLOSE_WAIT 状态的连接,但是具体到 hbase 与 hdfs 的问题上,即使 CLOSE_WAIT 被清理掉了,regionserver 与 hdfs 仍然会存在大量连接。regionserver open region 的时候,要读取所有的 hfile 文件,这些 fd(socket)没有被关闭掉,留着后续 scan 的时候使用,其余正常的读写使用 pread,单独开一条 socket。因此这些 CLOSE_WAIT 的连接本来是 EST 的,即使客户端正确处理了异常,或者通过 keepalive 清理了连接,本质上 regionserver 与 hdfs 之间存在了过多的 TCP 连接,因此最终两种处理方式:

  • 进行 compation,降低 hfile 数量
  • open region 之后关闭 fd,scan 的时候重新打开(或者 scan 走 mr)。

重点回顾

  1. CLOSE_WAIT的连接可以通过系统底层的keepalive机制来关闭
  2. 开启keepalive必须在socket上单独setsockopt,否则内核参数怎么调都没用
  3. 调整内核参数后对当前已建立的连接立即生效。

参考

https://huoding.com/2014/11/06/383

https://blog.csdn.net/ctthuangcheng/article/details/8596818

https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt


以上所述就是小编给大家介绍的《再谈 TCP 的 CLOSE_WAIT》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Beautiful Code

Beautiful Code

Greg Wilson、Andy Oram / O'Reilly Media / 2007-7-6 / GBP 35.99

In this unique work, leading computer scientists discuss how they found unusual, carefully designed solutions to difficult problems. This book lets the reader look over the shoulder of major coding an......一起来看看 《Beautiful Code》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具