Spring Boot微服务JdbcTemplate数据库连接异常:连接已关闭
你遇到的是典型的连接池失效连接未被及时清理的问题——SQL Server会主动断开长时间闲置的连接,但你的连接池还误以为这些连接可用,当服务尝试使用已关闭的连接时就会抛出The connection is closed错误。重启服务会重建连接池,所以暂时恢复,但闲置一段时间后问题必然复发。
结合你使用多数据源的场景,下面给出具体的排查和修复方案:
一、核心原因拆解
SQL Server默认会在连接闲置30分钟后自动断开(通过remote query timeout或connection timeout配置),如果你的连接池没有配置连接有效性检测机制,就会导致连接池里堆积大量失效连接,最终服务获取到死连接时触发报错。
二、针对多数据源的修复步骤
1. 配置连接池的有效性检测参数
不管你用的是Spring Boot默认的HikariCP,还是Druid这类第三方连接池,都必须开启连接有效性检测,确保连接池定期清理失效连接,且在获取连接时验证可用性。
如果你用HikariCP(Spring Boot默认)
在每个数据源的配置中添加以下参数:
# 第一个数据源配置 spring.datasource.first.hikari.max-lifetime=1800000 # 连接最大生命周期(30分钟,小于SQL Server闲置超时) spring.datasource.first.hikari.connection-test-query=SELECT 1 spring.datasource.first.hikari.validation-timeout=3000 spring.datasource.first.hikari.leak-detection-threshold=2000 # 第二个数据源配置 spring.datasource.second.hikari.max-lifetime=1800000 spring.datasource.second.hikari.connection-test-query=SELECT 1 spring.datasource.second.hikari.validation-timeout=3000 spring.datasource.second.hikari.leak-detection-threshold=2000
max-lifetime:设置为比SQL Server闲置超时短的值,确保连接池在数据库断开前主动回收连接connection-test-query:获取连接时执行的验证SQL,SQL Server用SELECT 1即可validation-timeout:连接验证的超时时间leak-detection-threshold:检测连接泄漏的阈值,能帮你排查是否有连接未被正确释放
如果你用Druid连接池
在每个数据源配置中添加:
# 第一个数据源 spring.datasource.first.druid.test-on-borrow=true spring.datasource.first.druid.test-while-idle=true spring.datasource.first.druid.validation-query=SELECT 1 spring.datasource.first.druid.time-between-eviction-runs-millis=60000 spring.datasource.first.druid.min-evictable-idle-time-millis=1800000 # 第二个数据源同理复制上述配置
test-on-borrow:获取连接时立即验证有效性test-while-idle:空闲时定期检查连接状态validation-query:验证连接可用的SQL语句time-between-eviction-runs-millis:定期清理失效连接的间隔min-evictable-idle-time-millis:连接闲置多久后被自动回收
2. 检查数据库端的连接超时配置
登录SQL Server,执行以下SQL检查全局连接配置:
-- 查看远程查询超时时间 EXEC sp_configure 'remote query timeout'; -- 查看连接超时时间 SELECT name, value FROM sys.configurations WHERE name = 'connection timeout';
如果remote query timeout设置过短(比如小于20分钟),可以适当调整,但更推荐在连接池端通过max-lifetime适配,避免修改数据库全局配置影响其他应用。
3. 排查代码中的连接泄漏问题
如果配置有效性检测后问题依然存在,大概率是代码中存在连接泄漏——即获取了数据库连接但未正确关闭(比如try-with-resources使用不当,或事务异常导致连接未释放)。
排查方法:
- 开启连接池的泄漏检测(比如HikariCP的
leak-detection-threshold),日志会打印泄漏连接的调用栈,定位问题代码 - 检查所有数据库操作是否用
try-with-resources包裹,确保连接自动关闭:
try (Connection conn = dataSource.getConnection()) { // 执行数据库操作逻辑 } catch (SQLException e) { // 异常处理 }
- 检查Spring事务配置是否正确,确保事务完成后连接被正常释放回连接池
4. 多数据源配置类的注意事项
如果你的自定义数据源配置类是手动创建连接池实例,一定要确保每个数据源都配置了上述有效性参数。比如用HikariCP手动创建数据源的代码:
@Bean @Primary public DataSource firstDataSource() { HikariDataSource dataSource = new HikariDataSource(); dataSource.setJdbcUrl("jdbc:sqlserver://localhost:1433;databaseName=db1"); dataSource.setUsername("user"); dataSource.setPassword("pass"); // 务必添加连接有效性配置 dataSource.setMaxLifetime(1800000); dataSource.setConnectionTestQuery("SELECT 1"); dataSource.setValidationTimeout(3000); dataSource.setLeakDetectionThreshold(2000); return dataSource; }
两个数据源的配置要保持一致,不要遗漏任何一个。
三、验证修复效果
配置完成后部署服务,观察以下几点:
- 查看连接池的监控日志(比如HikariCP会打印连接池状态)
- 让服务运行超过之前出现问题的时长,检查是否还会触发连接关闭错误
- 模拟长时间闲置后发起请求,验证连接池是否能正确获取有效连接
内容的提问来源于stack exchange,提问作者Vitalii




