已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
【背景】
我负责一个交易清算服务:Spring Boot 3.2 + JDK 17 + MySQL 8.0(RDS 主从)+ HikariCP 5.1,K8s 部署,4 副本,QPS 峰值约 1.2k。链路是「清算编排 → 账务落库」。连接走公司 VPC,中间有一层 NAT 网关(空闲连接默认回收,文档标称 60s 但没写死)+ RDS 侧 wait_timeout=28800(8h)。
【现象】
线上偶发(约每天 2~5 次、多在业务低峰的凌晨)抛 java.sql.SQLTransientConnectionException: ... Connection is not available, request timed out after 3000ms,以及少量 java.net.SocketException: Connection reset by peer。特征很怪:
maxLifetime=1800000(30min)、minIdle=5、connectionTimeout=3000、keepaliveTime=300000(5min),理论上比 NAT 60s 长得多,却仍偶发;validationTimeout=2000,connectionTestQuery 用的默认(MySQL 走 ping)。【已尝试的排查与结果】
wait_timeout:查了 RDS 参数,wait_timeout/interactive_timeout 都是 8h,明显长于连接空闲时间,基本排除;PoolEntry 日志,keepalive 线程确实在 5min 粒度跑 PING,但凌晨低峰时这条连接空闲时间常常 > 5min,keepalive 间隔和 NAT 回收之间没有强约束关系;ss -tni 看到 TIME_WAIT/FIN_WAIT 堆积,证据链断在中间设备;maxLifetime 压到 50s(< NAT 60s)观察一周,报障频率降了但没归零,怀疑 NAT 的 60s 只是标称、实际可能更短,或 RDS 链路里还有别的空闲回收点。【期望得到的答案形态】
connectionTestQuery 失败码、FIN vs RST 的区分、或主动注入不同空闲时长的探针连接做二分);maxLifetime / keepaliveTime / connectionTimeout / DB wait_timeout 这四者之间,到底该由谁设成最小值来"主动放弃"空闲连接,才能既避开网络层回收、又不至于连接频繁重建拖垮 DB;我倾向的答案是"客户端主动、以 NAT 标称空闲时间为上限来定 maxLifetime/keepalive",但目前证据链不够硬,想听大家有没有更系统的判定路径。