Tomcat服务器任务执行期间JDBC连接丢失问题求助
我之前在类似的Spring Boot + Tomcat环境里碰到过一模一样的JDBC连接丢失问题,结合你的架构和调用链路,给你梳理下排查方向和解决办法:
先明确下你的系统架构背景
- 认证服务:基于Keycloak
- 应用服务器:单台Tomcat 8
- 核心公共服务:应用A(部署在Tomcat 8上,Spring Boot + Java 8开发)
- 客户端应用:应用B等其他Web应用(均为Spring Boot + Java 8开发)
- 数据库层:
- MySQL(读写分离,使用独立Schema)
- 只读SQL Server
你的问题场景:应用B调用应用A的API,应用A连接只读SQL Server读取记录时,Tomcat任务执行期间出现JDBC连接丢失。
一、先从SQL Server端排查根源
检查数据库连接超时配置
很多时候是SQL Server主动踢掉了超时连接,先查这两个关键配置:- 执行以下SQL查看远程查询超时时间(默认600秒/10分钟):
如果你的API查询耗时超过这个值,数据库会强制断开连接。SELECT name, value_in_use FROM sys.configurations WHERE name = 'remote query timeout (s)' - 查看SQL Server的连接超时设置(默认30分钟),确保连接池的存活时间小于这个值,避免持有无效连接。
- 执行以下SQL查看远程查询超时时间(默认600秒/10分钟):
查看SQL Server错误日志
直接找数据库日志里的连接断开相关错误,比如:Error: 10054, Severity: 20, State: 0:TCP连接被重置(通常是网络或防火墙问题)Error: 1205, Severity: 13, State: 51:死锁导致连接断开
这些日志能直接帮你定位到数据库端的问题。
二、排查应用A的Spring Boot连接池配置
Spring Boot默认用HikariCP连接池,这是最容易出问题的环节:
调整连接池超时与存活配置
- 确保
spring.datasource.hikari.max-lifetime设置小于SQL Server的连接超时(比如SQL Server默认30分钟,就把max-lifetime设为1740000毫秒/29分钟),避免连接池持有已被数据库踢掉的无效连接。 - 开启连接有效性验证,每次获取连接时都检查是否可用:
spring.datasource.hikari.test-on-borrow=true spring.datasource.hikari.validation-query=SELECT 1 spring.datasource.hikari.validation-timeout=5000 - 如果API执行耗时较长,调大
spring.datasource.hikari.connection-timeout,避免连接池在获取连接时超时。
- 确保
检测连接泄漏
- 启用HikariCP的泄漏检测:
spring.datasource.hikari.leak-detection-threshold=2000(单位毫秒),如果代码里有未正确释放的连接,日志会打印泄漏的栈信息。 - 检查代码是否用了
try-with-resources语法确保资源释放:try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { // 处理查询结果 } catch (SQLException e) { // 异常处理逻辑 }
这种语法能自动关闭Connection、Statement、ResultSet,避免连接泄漏。
- 启用HikariCP的泄漏检测:
三、排查Tomcat 8的线程与连接配置
Tomcat线程池配置
检查server.xml里的线程池和Connector配置,确保线程数足够,不会因为线程耗尽导致任务阻塞:<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="200" minSpareThreads="20" maxIdleTime="60000"/> <Connector port="8080" protocol="HTTP/1.1" executor="tomcatThreadPool" connectionTimeout="20000" redirectPort="8443"/>注意这里的
connectionTimeout是Tomcat处理HTTP请求的超时,不是JDBC连接超时,但如果应用B的请求超时,也可能引发后续JDBC连接异常。如果用了Tomcat自带的JDBC连接池
若应用A没使用HikariCP而是Tomcat JDBC池,启用废弃连接回收和日志:<Resource name="jdbc/SQLServerDB" auth="Container" type="javax.sql.DataSource" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" url="jdbc:sqlserver://localhost:1433;databaseName=YourReadOnlyDB" username="readonlyUser" password="yourPass" maxTotal="100" maxIdle="20" minIdle="5" removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"/>
四、排查网络层面问题
检查防火墙/中间件超时
如果应用A和SQL Server之间有防火墙、负载均衡或者代理,这些设备通常会主动断开长时间空闲或超时的连接(比如很多防火墙默认5分钟超时)。检查这些设备的连接超时设置,确保大于你的API最大执行时间。测试网络稳定性
用ping(Windows/Linux)或mtr(Linux)测试应用A服务器到SQL Server的连通性,看是否有丢包、延迟过高的情况,网络不稳定也会导致连接随机断开。
五、代码与查询优化
优化SQL查询性能
慢查询是连接超时的常见原因,用SQL Server的执行计划分析你的查询语句,看是否有全表扫描、缺少索引的情况,添加合适的索引能大幅缩短查询时间,避免超时。异步处理(可选)
如果API确实需要长时间执行,可以用Spring的@Async注解把查询逻辑改成异步,避免长时间占用JDBC连接和Tomcat线程:@Service public class SqlServerService { @Async public CompletableFuture<YourRecord> fetchRecordAsync(String id) { // 执行SQL查询逻辑 return CompletableFuture.completedFuture(record); } }
内容的提问来源于stack exchange,提问作者PaolaG

