背景知识
为什么防火墙要断开空闲连接
防火墙资源有限(如会话表、内存等)。长时间空闲的连接会占用资源,影响其他正常连接。
防火墙断开连接的常见方式
防火墙可能通过以下方式终止连接:
- 丢包(Drop):是为了增强安全性,避免暴露主机或网络拓扑信息。防火墙直接丢弃数据包,而不发送任何反馈(如 TCP RST 或 ICMP 错误消息)
- 显示拒绝:防火墙发送 TCP RST(重置连接)或 ICMP 消息(如 “Destination Unreachable”)
对于丢包的方式,客户端无法感知连接已经断开,仍然尝试发送数据包。TCP 协议会认为网络拥塞导致数据包未到达,于是进入重传和退避机制,直到超时。
对于显式拒绝的方式,客户端立即感知到连接断开,释放资源。
问题描述
网络环境中的防火墙或者代理层,可能会对一些空闲达到一定时长的连接进行丢弃。客户端无法感知连接已经断开,仍然尝试发送数据包。TCP 协议会认为网络拥塞导致数据包未到达,于是进入重传和退避机制,直到超时。
为什么是15分钟
RTO:Retransmission Timeout(重传超时)。Linux2.6+ 的TCP RTO_MIN=200ms, RTO_MAX=120s,每次超时时间是增加1倍。
每次超时时间为(秒):0.2, 0.4, 0.8, 1.6, 3.2, 6.4, 12.8, 25.6, 51.2, 102.4, 120.0, 120.0, ……
计算重试次数需要+1,因为第一次正常请求就是0.2,例如重试2次,实际超时时间为 0.2 + 0.4 + 0.8 = 1.4秒
可以使用以下命令查看当前操作系统关于tcp keepalive时长和重试的一些配置。
# 默认7200秒
sysctl net.ipv4.tcp_keepalive_time
# 默认重试15次
sysctl net.ipv4.tcp_retries2
解决方案
可以考虑缩短操作系统的tcp保活时间(小于防火墙断开空闲连接的时长)。
如果希望更快地检测到连接问题,可以缩短 tcp_retries2 的值
# 缩短tcp_keepalive_time(小于防火墙断开空闲连接的时长)。
sudo sysctl -w net.ipv4.tcp_keepalive_time=300
# 减小tcp重试次数8次大约是13秒
sudo sysctl -w net.ipv4.tcp_retries2=5
如果你是k8s pod环境没权限改,可以通过initContainer的方式修改(如果图方便可以直接修改container特权模式启动,在启动命令里修改这些参数):
kind: Deployment
apiVersion: apps/v1
spec:
template:
spec:
initContainers:
- command:
- sh
- -c
- |
sysctl -w net.ipv4.tcp_keepalive_time=300
sysctl -w net.ipv4.tcp_retries2=5
image: busybox:stable
imagePullPolicy: Always
name: setsysctl
resources: {}
securityContext:
privileged: true
也可以考虑应用层增加健康检查机制,此种方案需要改造连接池的逻辑。