内容简介:某日集群告警,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 的正常关闭过程:
无论出于任何原因程序没有执行 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 的新连接会继承这个该属性。
在 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秒。
上图的例子中,服务端监听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)。
重点回顾
- CLOSE_WAIT的连接可以通过系统底层的keepalive机制来关闭
- 开启keepalive必须在socket上单独setsockopt,否则内核参数怎么调都没用
- 调整内核参数后对当前已建立的连接立即生效。
参考
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中文版
陈浩等译 / 电子工业 / 2006-9 / 69.00元
《JUnit Recipes中文版:程序员实用测试技巧》主要介绍了在Java开发中使用JUnit进行单元测试的各种方法、原则、技巧与实践。本书出自开发一线专家之手,本着实用的原则,涵盖各类Java开发中应用JUnit的实用技巧,内容丰富、全面深入;无论对于需要应用JUnit进行单元测试的一线Java开发人员,还是JUnit入门、进阶者,本书都是一本不可多得的实用指南。这本书介绍了大量的JUnit实......一起来看看 《JUnit Recipes中文版》 这本书的介绍吧!
RGB HSV 转换
RGB HSV 互转工具
RGB CMYK 转换工具
RGB CMYK 互转工具