Spring Boot应用Hikari连接池调试:如何获取连接唯一标识?
解决Hikari连接池连接标识打印及并发连接关闭问题
一、调试时连接哈希码与启动日志不一致的原因
Hikari连接池会对原始数据库连接(如PgConnection)做代理包装,生成HikariProxyConnection对象。你调试时拿到的是这个代理实例,而启动日志输出的是原始数据库连接的哈希码,因此两者无法匹配。
二、获取原始连接并打印一致标识
要拿到和启动日志一致的连接标识,需从代理对象中剥离出真实的数据库连接,以下是两种可行方案:
方案1:使用Hikari官方工具类(推荐)
Hikari提供com.zaxxer.hikari.util.HikariUnwrapper工具类,可直接获取原始连接,无需反射:
import com.zaxxer.hikari.util.HikariUnwrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.datasource.DelegatingDataSource; import java.sql.Connection; import java.sql.SQLException; public class LoggingDelegatingDataSource extends DelegatingDataSource { private static final Logger log = LoggerFactory.getLogger(LoggingDelegatingDataSource.class); @Override public Connection getConnection() throws SQLException { Connection proxyConnection = super.getConnection(); // 剥离出原始数据库连接 Connection rawConnection = HikariUnwrapper.unwrap(proxyConnection); // 打印与启动日志一致的连接标识 log.info("获取到连接,标识:{}", rawConnection); return proxyConnection; } @Override public Connection getConnection(String username, String password) throws SQLException { Connection proxyConnection = super.getConnection(username, password); Connection rawConnection = HikariUnwrapper.unwrap(proxyConnection); log.info("通过用户名密码获取到连接,标识:{}", rawConnection); return proxyConnection; } }
方案2:反射获取原始连接(兼容旧版Hikari)
如果你的Hikari版本未提供HikariUnwrapper,可通过反射读取代理对象的delegate字段获取原始连接:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.datasource.DelegatingDataSource; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.SQLException; public class LoggingDelegatingDataSource extends DelegatingDataSource { private static final Logger log = LoggerFactory.getLogger(LoggingDelegatingDataSource.class); @Override public Connection getConnection() throws SQLException { Connection proxyConnection = super.getConnection(); Connection rawConnection = getRawConnection(proxyConnection); if (rawConnection != null) { log.info("获取到连接,标识:{}", rawConnection); } return proxyConnection; } private Connection getRawConnection(Connection proxy) { try { Field delegateField = proxy.getClass().getDeclaredField("delegate"); delegateField.setAccessible(true); Object delegate = delegateField.get(proxy); // 递归处理多层代理(若存在) if (delegate instanceof Connection) { return getRawConnection((Connection) delegate); } return (Connection) delegate; } catch (NoSuchFieldException | IllegalAccessException e) { log.warn("无法获取原始连接,使用代理连接标识:{}", proxy); return proxy; } } }
三、额外调试优化建议
- 开启Hikari TRACE日志:在
application.yml中配置,可输出连接的获取、归还、关闭全链路日志,追踪连接生命周期logging: level: com.zaxxer.hikari: TRACE - 关联线程信息:打印连接标识时加入当前线程ID和名称,方便定位并发场景下的线程交互
log.info("线程[{}:{}]获取到连接,标识:{}", Thread.currentThread().getId(), Thread.currentThread().getName(), rawConnection); - 增加归还日志:在重写的
close方法(需包装Connection)中打印归还的连接标识,对比获取与归还的连接是否一致,排查异常关闭情况
内容的提问来源于stack exchange,提问作者Diego Sanz
相关产品推荐
相关产品推荐

