如何调试HikariCP连接丢失?Play 2.6.10连接泄漏排查求助
解决Play 2.6.10 + HikariCP连接泄漏的实战思路
嘿,从你给出的日志和场景来看,这个连接泄漏问题确实有点棘手——毕竟你明明用了Play的withConnection,理论上连接应该自动归还池里才对。结合你提到的setNetworkTimeout这个特殊操作,我梳理几个大概率的排查方向和解决步骤:
一、先怀疑你的setNetworkTimeout操作
你说每个连接都要调用这个方法,设置低至10秒的超时,这很可能是罪魁祸首:
- 旧版本的MySQL JDBC驱动(比如5.1.x系列)在处理
setNetworkTimeout时存在已知bug,会导致连接的内部状态紊乱,HikariCP在尝试回收连接时无法正确识别,直接把这个连接当成“不可用”丢弃,但又没从active计数里减掉,久而久之池就被耗尽了。 - Play的
withConnection是靠try-finally来保证归还的,但如果在finally块执行归还时,因为之前的setNetworkTimeout导致连接抛出异常,这个异常可能被Play的封装层静默吞掉,结果就是连接没还回去。
验证方法:
- 先临时把
setNetworkTimeout的代码注释掉,跑个几天看看还会不会泄漏。如果问题消失,那基本实锤是这个操作的锅。 - 赶紧升级你的
mysql-connector-java版本,建议直接更到8.0.x的稳定版,新驱动修复了不少网络超时相关的bug。
二、检查Play与Hikari的配置兼容性
Play 2.6.x默认用Hikari,但有时候配置冲突也会搞出问题:
- 打开你的
application.conf,核对下这些参数:db.default.hikaricp.leakDetectionThreshold = 60000 # 你已经开了,这个没问题 db.default.hikaricp.maximumPoolSize = 10 # 日志显示total=10,和你的配置一致 db.default.hikaricp.connectionTimeout = 30000 # 确保连接超时不要设得太长 db.default.hikaricp.maxLifetime = 1800000 # 建议设成比MySQL的wait_timeout小,比如30分钟 - 再确认下有没有其他地方手动获取过连接(比如绕过Play的封装直接拿Hikari的连接),如果有,是不是没调用
close()归还?虽然你说只用了withConnection,但这种小疏忽很容易犯。
三、把调试粒度拉满,定位泄漏点
既然已经开了leakDetectionThreshold,可以再优化下日志来抓细节:
- 把阈值调小一点,比如设成30000(30秒),这样不用等几天,只要有连接超过30秒没归还,就能立刻抓到堆栈,方便你定位对应的业务代码。
- 打开HikariCP的DEBUG日志,这样能看到每一个连接的获取、归还、销毁日志,对比active连接数的变化,就能清楚看到是哪些连接只拿不还。
- 下次出现连接耗尽时,除了看阻塞的线程,还要找那些持有active连接的线程——它们可能卡在慢查询、第三方API调用或者死锁上,导致连接被长时间占用,这也算一种“隐性泄漏”。
四、绕过Play的封装,直接用Hikari管理连接
如果怀疑是Play的withConnection和Hikari的兼容性问题,可以试试直接用HikariDataSource来管理连接:
import com.zaxxer.hikari.HikariDataSource import javax.inject.Inject import java.sql.Connection import java.util.concurrent.Executors class ActionSummary @Inject()(hikariDS: HikariDataSource) { def listByStripe() = { var conn: Connection = null try { conn = hikariDS.getConnection() // 如果一定要设置超时,这里加,但记得用最新驱动 conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), 10000) // 你的查询逻辑写在这里 } finally { if (conn != null) { try { conn.close() // 手动归还,确保万无一失 } catch { case e: Exception => // 捕获关闭时的异常,别让它掩盖了原问题 e.printStackTrace() } } } } }
这种方式更直接,能排除Play封装层的干扰,如果这样改了之后不再泄漏,那就是Play和Hikari的交互有问题。
五、检查MySQL服务器的连接配置
有时候问题不在应用端,而是MySQL主动断开了连接:
- 查看MySQL的
wait_timeout和interactive_timeout参数,如果这两个值比Hikari的maxLifetime小,MySQL会主动断开闲置的连接,但Hikari池里还以为这个连接可用,后续使用时会抛出异常,可能导致连接无法正常归还。 - 把Hikari的
maxLifetime设成比MySQL的wait_timeout小10%左右,比如MySQL的wait_timeout是8小时,Hikari就设成7小时10分钟,让Hikari在MySQL断开前主动回收连接。
内容的提问来源于stack exchange,提问作者wingedsubmariner
相关产品推荐
相关产品推荐

