清除僵尸连接的妙招
一个网络服务程序如果没有开启tcp keepalive机制,那么遇到客户端与服务端网络故障,导致断链接的TCP FIN报文丢失的情况,服务端的对应链接就不会释放,一直积累,当超过该服务的最大允许链接数时,就无法接入服务了。这时最简单的方法是直接重启服务。但是如果是极其重要的业务服务,不能容忍重启带来的业务损失,那么该怎么办呢?
下面说一个巧妙的方法,可以解决这个问题,但仅限于客户端与服务端处于一个局域网内的情况。
# ss -ant | grep :2380 | grep ES
ESTAB 0 0 10.18.210.83:2380 10.18.210.249:49735
ESTAB 0 0 10.18.210.83:34565 10.18.210.248:2380
ESTAB 0 0 10.18.210.83:2380 10.18.210.249:35721
ESTAB 0 0 10.18.210.83:2380 10.18.210.248:16912
ESTAB 0 0 10.18.210.83:34564 10.18.210.248:2380
ESTAB 0 0 10.18.210.83:2380 10.18.210.248:16929
ESTAB 0 0 10.18.210.83:36784 10.18.210.248:2380
ESTAB 0 0 10.18.210.83:59998 10.18.210.249:2380
ESTAB 0 0 10.18.210.83:2380 10.18.210.248:33639
假设上面端口2380的服务积累了许多的无效链接。
- 我们可以模拟对应链接的客户端向服务端发起TCP ACK请求。 如在服务器使用模拟工具 模拟源ip和端口 10.18.210.249: 16929 , 目的IP与端口10.18.210.83:2380的ACK请求,序号设置为1。
- 使用模拟工具将该请求发送到服务端自身。
- 服务端协议栈接受到ACK请求后,发现对应的序号不对,向客户端回应ACK。
- 客户端接到服务端的ACK报文后,发现自己根本就没有这个链接,直接回应TCP RST报文。
- 服务端接收到RST报文,关闭对应的链接。
实例的perl代码tcpclean.pl 如下,利用报文构造工具hping3:
if (@ARGV != 1) {
print "tcpclean port";
exit -1;
}
$port = $ARGV[0];
@s = `ss -ant | grep $port`;
$count = 0;
for my $line (@s) {
$line =~ s/::ffff://g;
#print $line,"\n";
if ($line =~ /ESTAB\s+\w+\s+\w+\s+([^:]+):$port\s+([^:]+):(\d+)/) {
$count++;
$client_ip = $2;
$client_seq = $3;
$server_ip = $1;
my $m = "./hping3 -A -c 1 -a $client_ip -s $client_seq -p $port $server_ip";
print $m,"\n";
system $m;
}
}
在问题服务器上执行perl tcpclean.pl 2380
发表评论