使用Keepalive机制在NAT后保持长链接

一、背景

1、NAT的长链接的局限

AWS云上的NAT/NLB/GWLB在针对长连接的场景上,存在350秒闲置超时的物理机制,且这是一个硬限制,无法通过开case提升limit的办法修改。这一限制的官方文档描述参考如下:NAT ,NLB ,GWLB 。

为了解决这一限制问题,需要同时在OS层、应用成打开keepalive设置,并且需要在服务器端和客户端分别配置。

2、使用Linux自带NC工具测试的局限

NC常被用于验证网络连接特性,但是linux自带的nc程序是不支持keepalive的,使用nc建立的网络连接在闲置350秒后就会被断开。为了测试keepalive可以使用如下方案。

二、测试Keepalive

1、搭建运行环境

构建如下的架构图。

在以上架构图中,左侧的是客户端EC2位于私有子网,然后经过NAT Gateway访问到另一个VPC的公有子网内的EC2服务器。NAT Gateway和作为服务器的EC2上绑定了EIP。

接下来,分别在两个EC2上部署Server端程序和客户端程序,然后测试长连接请求。

2、修改Linux OS系统配置

请分别对服务器端和客户端执行配置。编辑Linux操作系统的/etc/sysctl.conf文件,在其中添加如下内容:

net.ipv4.tcp_keepalive_time = 100
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 30

修改后保存退出,执行sysctl -p命令将以上配置生效。请分别对服务器端和客户端执行以上配置。

三、使用支持keepalive的nc测试长链接

1、下载并编译支持keepalive的nc用于测试长链接

安装依存性软件包,包括gitgcc用于现场编译代码。另外需要注意,由于是静态编译,需要在安装gcc的基础上额外安装glibc-static包,否则编译无法通过。执行如下命令:

yum update -y
yum install git gcc glibc-static -y

然后从Github上获取代码,执行如下命令:

git clone https://github.com/cyberelf/netcat-keepalive.git

如果从Github官网位于海外的服务器上clone代码速度慢或者卡住,也可以从如下网址下载:

wget https://myworkshop.bitipcman.com/NAT-and-NLB-keepalive/netcat-keepalive.tar.gz

解压缩后,进入目录,然后编译:

tar zxvf netcat-keepalive.tar.gz
cd netcat-keepalive
make linux

然后即可在本目录内获得名为 nckl-linux 的可执行文件。至此编译完成。

2、启动服务器端和客户端服务

服务器端运行如下命令(注意字母l小写、而K是大写):

./nckl-linux -lK 8080

客户端运行如下命令(注意字母K大写):

./nckl-linux -vK 52.82.43.217 8080

在以上命令中,大写字母K表示开启keepalive,而具体的keepalive的周期参数,是通过操作系统 /etc/sysctl.conf 中的参数指定。

3、验证测试

两个程序都启动后,在client端窗口中输入一些文字,可以看到server端窗口也会出现对应的问题,在server端输入文字,client端也能看到。接下来等待350秒以上,也就是NAT网关的超时窗口,比如等待到600秒(10分钟)。然后回到server窗口,再次输入一些信息并按回车。如果信息输入正常,那么client端窗口能够收到并显示这些信息,则表示长链接没有中断,测试成功。

如下截图中可以看到客户端发送消息后,经过了6分钟(大于350秒)以上,再次从服务器端发送消息,客户端正常收到,表示长链接稳定。

四、使用Python程序创建socket长链接的测试

除了使用修改过的nc程序测试长连接外,还可以自己编写一个python程序用于建立网络连接。

1、服务器端代码

在Amazon Linux 2环境上python3.7版本测试通过。请将以下文件保存为server.py

import socket
from struct import unpack

sockServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockServer.bind(('',8080))
sockServer.listen(1)
conn, addr = sockServer.accept()
while True:
    data_length = conn.recv(4)
    if not data_length:
        break
    data_length = unpack('I', data_length)[0]
    data = conn.recv(data_length).decode()
    print(data)

在以上程序中,绑定了8080端口,可根据需要修改端口。如果在中国区测试,需要注意EIP公网IP地址的8080端口需要在通过ICP备案才能使用,内网IP的8080端口不受限制。

启动这个程序,在server端执行如下命令:

python3 server.py

2、客户端代码

在Amazon Linux 2环境上python3.7版本测试通过。请将以下文件保存为client.py

import socket
from time import sleep
from struct import pack
from datetime import datetime

sockClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockClient.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)
sockClient.connect(('52.82.43.217',8080))

for i in range(5):
    msg = str(datetime.now())[:19]
    print(msg)
    msg = msg.encode()
    sockClient.sendall(pack('i', len(msg)))
    sockClient.sendall(msg)
    sleep(600)
sockClient.close()

在以上程序中,填写的IP地址是要连接的远程服务器IP地址,8080端口是要连接的远程服务器端口。另外sleep(600)表示程序闲置600秒,然后继续发送下一个数据包。之所以使用闲置600秒的原因是,NAT网关默认的闲置超时是350秒断开链接,因此测试程序如果闲置600秒,还能维持连接,就意味着Keepalive生效。

现在启动这个程序,在client端执行如下命令:

python3 client.py

3、验证测试

客户端程序启动后,会立刻在屏幕上打出当前时间戳。等待600秒即10分钟闲置时间后,屏幕再次打印出来下一个时间戳。从如下截图中可看到经过了10分钟程序是没有中断的,继续打出了新时间戳。由此可验证OS和应用通过自动发送keepalive确保了长连接。如下截图。

五、结论

至此,验证了通过服务器端OS、客户端OS、应用程序侧分别配置Keepalive,即可解决NAT网关的350秒闲置超时问题。