一、背景
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用于测试长链接
安装依存性软件包,包括git
和gcc
用于现场编译代码。另外需要注意,由于是静态编译,需要在安装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://blogimg.bitipcman.com/workshop/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秒闲置超时问题。