TCP 会话保持技术
问题起因
在项目开发过程中,我曾遇到过一个难以解决的数据库连接池问题。无论如何配置,始终存在连接异常或被关闭的情况。直到最近偶然阅读了一篇关于负载均衡与会话保持的文章,里面详细介绍了各大云服务商对 HTTP 连接和 TCP 连接的限制。我这才意识到:MySQL 连接池底层依赖的是 TCP 连接,自然也会受到 TCP 空闲连接超时机制的影响。
那么会不会是因为连接在长时间空闲后被云服务器强制断开,导致连接池中的部分连接失效,进而引发连接异常?
其实早在之前排查问题时我就很疑惑:我的数据库服务器上,wait_timeout 和 interactive_timeout 都已经设置为了 28800 秒(8 小时),理论上不应该自动断开连接。但问题依旧频繁出现。
直到看到了这篇文章才意识到:即使没有主动购买负载均衡服务,云服务商通常也会在内部做默认的连接管理。一般在各自的服务商的手册中会提到各种连接的默认配置
TCP 连接超时时间默认为 900 秒,即在没有数据传输的情况下,900 秒后将主动断开连接。
由此推断:并不是数据库本身断开连接,而是 TCP 层在云服务器层面超时回收了连接。
解决方法
找到了问题根源后,我们便可以有针对性地采取多种方式来避免 TCP 连接被过早断开。
方法一:缩短连接池连接生命周期(maxLifetime)
通过设置 HikariCP 的 maxLifetime 参数,使连接在被云服务断开前就主动关闭并重建,从而避免使用已被云服务器关闭的连接。
例如,设置连接最大存活时间为 850 秒(小于 900 秒):
hikari:
max-lifetime: 850000
单位为毫秒,即 850 秒,略小于腾讯云的 900 秒超时阈值。
方法二:修改 TCP KeepAlive 探活参数
如果我们希望保持长时间的 TCP 活跃连接,操作系统层面提供了 TCP KeepAlive 机制。我们可以通过修改内核参数,使系统在连接空闲时周期性发送探测包,从而避免被认为是“死连接”。
1. 查看当前 TCP 探活参数:
# 查看 TCP KeepAlive 各参数
sysctl net.ipv4.tcp_keepalive_time
sysctl net.ipv4.tcp_keepalive_intvl
sysctl net.ipv4.tcp_keepalive_probes
输出示例:
$ sysctl net.ipv4.tcp_keepalive_time
net.ipv4.tcp_keepalive_time = 7200 # 默认 2 小时才开始探活
$ sysctl net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_intvl = 75 # 探活间隔 75 秒
$ sysctl net.ipv4.tcp_keepalive_probes
net.ipv4.tcp_keepalive_probes = 9 # 最多尝试 9 次探活失败才判定断开
2. 修改配置:
编辑系统配置文件:
sudo vim /etc/sysctl.conf
添加或修改以下配置项:
net.ipv4.tcp_keepalive_time = 600 # 10分钟后开始探活
net.ipv4.tcp_keepalive_intvl = 30 # 每30秒探测一次
net.ipv4.tcp_keepalive_probes = 5 # 最多尝试5次
保存后使配置生效:
sudo sysctl -p
修改这些参数可以显著提高 TCP 长连接的存活能力,但实际效果仍取决于各个云服务商的网络策略是否允许。
小问题解答
Q1:为什么即便设置了 HikariCP 的 keepaliveTime 参数,TCP 连接仍然可能被断开?
这是因为:
- HikariCP 的
keepaliveTime是应用层机制,主要用于在连接空闲时执行一次轻量级 SQL 查询(如SELECT 1),以防数据库关闭连接。 - 它并不能控制底层 TCP 层是否发送 KeepAlive 探测包。
也就是说,Hikari 保活只影响连接池和数据库之间的交互,并不能干预 TCP 层连接的存活状态。
因此,如果云服务器在 TCP 层判断连接空闲并主动断开,即便 Hikari 的连接仍“活着”,实质上底层 TCP 已经断开了,继续使用该连接将仍然抛出异常。
欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1701220998@qq.com