oracle tcp空包请求,再谈 TCP 的 CLOSE_WAIT

背景

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

c7f5ed5177ab48d9b85268f3f1535d59.png

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

keepalive

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

int opt = 1;

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

1

2

3

4

intopt=1;

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

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

b3273ee25a6d997621ccc085381dbfe8.png

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

net.ipv4.tcp_keepalive_intvl = 75 //探测间隔(秒)

net.ipv4.tcp_keepalive_probes = 9 //探测次数

net.ipv4.tcp_keepalive_time = 7200 //探测超时(秒)

1

2

3

4

5

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秒。

872aed5f9eb23cbaa040c4dffb835ce0.png

上图的例子中,服务端监听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

没有相关文章.

(转载请注明作者和出处 easyice.cn ,请勿用于任何商业用途)

f67a8a4f3e76ee0f8e0e17ab585ddf75.gif

f67a8a4f3e76ee0f8e0e17ab585ddf75.gif

f67a8a4f3e76ee0f8e0e17ab585ddf75.gif

f67a8a4f3e76ee0f8e0e17ab585ddf75.gif

f67a8a4f3e76ee0f8e0e17ab585ddf75.gif (欢迎评分)

e763a4bc2be3a675246b234f98cfeeda.gifLoading...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值