再谈 TCP 的 CLOSE_WAIT

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

内容简介:某日集群告警,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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

JUnit Recipes中文版

JUnit Recipes中文版

陈浩等译 / 电子工业 / 2006-9 / 69.00元

《JUnit Recipes中文版:程序员实用测试技巧》主要介绍了在Java开发中使用JUnit进行单元测试的各种方法、原则、技巧与实践。本书出自开发一线专家之手,本着实用的原则,涵盖各类Java开发中应用JUnit的实用技巧,内容丰富、全面深入;无论对于需要应用JUnit进行单元测试的一线Java开发人员,还是JUnit入门、进阶者,本书都是一本不可多得的实用指南。这本书介绍了大量的JUnit实......一起来看看 《JUnit Recipes中文版》 这本书的介绍吧!

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

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

RGB CMYK 互转工具