连接池偶现15分钟超时问题

背景知识

如果你的请求超时,甚至处于read/write阶段的连接突然没了(被drop,客户端无法感知),跟连接空闲时长没太大关系,可能是你的网络组件如ovs-agent(Open vSwitch Agent)自身的bug或者跟ovs版本不兼容导致的,此种情况需要升级ovs-agent解决。

为什么防火墙要断开空闲连接

防火墙资源有限(如会话表、内存等)。长时间空闲的连接会占用资源,影响其他正常连接。

防火墙断开连接的常见方式

防火墙可能通过以下方式终止连接:

  • 丢包(Drop):是为了增强安全性,避免暴露主机或网络拓扑信息。防火墙直接丢弃数据包,而不发送任何反馈(如 TCP RST 或 ICMP 错误消息)
  • 显示拒绝:防火墙发送 TCP RST(重置连接)或 ICMP 消息(如 “Destination Unreachable”)

对于丢包的方式,客户端无法感知连接已经断开,仍然尝试发送数据包。TCP 协议会认为网络拥塞导致数据包未到达,于是进入重传和退避机制,直到超时。

对于显式拒绝的方式,客户端立即感知到连接断开,释放资源。

防火墙/NAT 设备如何判断连接是否活跃?

大部分防火墙/NAT 设备使用会话超时机制来决定是否清除连接,主要依据:

  1. 有无数据流量(Data Traffic)
    设备通常认为只有真实的数据包(如 HTTP、WebSocket、数据库查询)才算“活跃”。
  2. 是否使用 TCP KeepAlive
    部分设备会忽略 TCP KeepAlive,因为它是协议级别的“空包”,不会被认为是真正的应用流量。
  3. 是否使用 NAT 映射(NAT Table)
    NAT 设备通常会跟踪 TCP 连接,并在长时间无流量时回收 NAT 端口映射,导致连接被强行中断。

TCP KeepAlive 对防火墙/NAT 的影响

设备类型是否认可 TCP KeepAlive典型超时时间
Linux 内核✅ 认可,可防止连接断开2 小时(默认)
企业级防火墙(如 Cisco、Palo Alto)❌ 可能不认可(默认忽略)5-30 分钟
云厂商负载均衡(AWS ELB, Azure ALB)❌ 不认可,仍会超时4-10 分钟
NAT 设备(如家用路由器)❌ 可能不认可5-15 分钟

结论:TCP KeepAlive 可能会被 NAT 设备、防火墙忽略,导致连接仍然会超时。

如何让防火墙/NAT 设备认可连接是活跃的?

✅ 方法 1:应用层心跳(HTTP/WebSocket/数据库)

发送真实的应用层请求,如:

  • HTTP:每隔 30 秒 GET /ping
  • WebSocket:发送 ping 帧
  • 数据库:SELECT 1
import time
import requests

while True:
    try:
        requests.get("http://example.com/ping", timeout=5)
    except Exception:
        pass
    time.sleep(30)  # 30 秒一次

问题描述

网络环境中的防火墙或者代理层,可能会对一些空闲达到一定时长的连接进行丢弃。客户端无法感知连接已经断开,仍然尝试发送数据包。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秒(第一次正常发起请求等待0.2秒,第一次重试的请求等待0.4秒超时,依此类推)

可以使用以下命令查看当前操作系统关于tcp keepalive时长和重试的一些配置。

# 默认7200秒
sysctl net.ipv4.tcp_keepalive_time
# 默认重试15次
sysctl net.ipv4.tcp_retries2

解决方案

可以考虑缩短操作系统的tcp保活时间(小于防火墙断开空闲连接的时长)。

如果希望更快地检测到连接问题,可以缩短 tcp_retries2 的值。

参数说明:
当一个 TCP 连接长时间没有数据传输时,系统不会主动关闭它,而是等到 tcp_keepalive_time 设定的时间后,开始发送 TCP Keepalive 探测包。如果对方正常响应,连接就会继续保持;如果对方无响应,系统会继续发送一定次数的探测包(由 net.ipv4.tcp_keepalive_probes 控制,每次间隔tcp_keepalive_intvl秒),如果还是无响应,就会关闭连接。

# 缩短tcp_keepalive_time(小于防火墙断开空闲连接的时长)。
sudo sysctl -w net.ipv4.tcp_keepalive_time=180
sysctl -w net.ipv4.tcp_keepalive_intvl=60
sysctl -w net.ipv4.tcp_keepalive_probes=3

# 减小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=180
          sysctl -w net.ipv4.tcp_keepalive_intvl=60
          sysctl -w net.ipv4.tcp_keepalive_probes=3
          sysctl -w net.ipv4.tcp_retries2=5
        image: busybox:stable
        imagePullPolicy: Always
        name: setsysctl
        resources: {}
        securityContext:
          privileged: true

也可以考虑应用层增加健康检查机制,此种方案需要改造连接池的逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值