1.removeAbandoned如何排查连接泄露
1.1 什么是连接泄露
连接池中连接数同一时间被用光(长SQL、连接执行完成后没有及时归还等均有可能造成),超过获取连接最大等待时间后仍然没有获取到连接。
1.2 如何通过removeAbandoned排查连接泄露
通过开启removeAbandoned
参数,使其在每次获取连接时记录当前线程的堆栈信息。
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
···省略部分代码···
//判断是否开启removeAbandoned
if (removeAbandoned) {
//记录当前线程的堆栈信息(此操作很耗时)
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
poolableConnection.connectStackTrace = stackTrace;
//记录连接开始时间
poolableConnection.setConnectedTimeNano();
//设置开启跟踪,设置后将在归还连接时清除当前连接的活跃状态
poolableConnection.traceEnable = true;
//获得锁,向activeConnections中置入连接对象
activeConnectionLock.lock();
try {
activeConnections.put(poolableConnection, PRESENT);
} finally {
activeConnectionLock.unlock();
}
}
···省略部分代码···
return poolableConnection;
}
}
开启logAbandoned
参数,使其能够在回收时将当前堆栈信息进行打印。
//如果开启了logAbandoned,则拼接日志并进行输出
if (isLogAbandoned()) {
//拼接异常堆栈信息
StringBuilder buf = new StringBuilder();
buf.append("abandon connection, owner thread: ");
buf.append(pooledConnection.getOwnerThread().getName());
buf.append(", connected at : ");
buf.append(pooledConnection.getConnectedTimeMillis());
buf.append(", open stackTrace\n");
//根据所记录的堆栈信息进行生成
StackTraceElement[] trace = pooledConnection.getConnectStackTrace();
for (int i = 0; i < trace.length; i++) {
buf.append("\tat ");
buf.append(trace[i].toString());
buf.append("\n");
}
buf.append("ownerThread current state is " + pooledConnection.getOwnerThread().getState()
+ ", current stackTrace\n");
//获得当前连接是持有者堆栈进行拼接
trace = pooledConnection.getOwnerThread().getStackTrace();
for (int i = 0; i < trace.length; i++) {
buf.append("\tat ");
buf.append(trace[i].toString());
buf.append("\n");
}
//输出异常日志
LOG.error(buf.toString());
}
通过开启removeAbandoned
与logAbandoned
参数,可以在异常日志中观测到被自动回收的连接(超过removeAbandonedTimeout
)的,可以通过removeAbandonedTimeout
参数调整并对异常日志进行观测,得到长时间未进行资源释放的连接。
2.为什么removeAbandoned不建议在线上开启
在开启removeAbandoned
参数后,initCheck()
方法将输出removeAbandoned is true, not use in production.
日志,由此可见该参数并不建议在生产环境进行开启。
if (removeAbandoned) {
LOG.warn("removeAbandoned is true, not use in production.");
}
该参数开启后存在以下问题:
- 每次获得连接时都会记录当前堆栈信息,造成不必要的空间消耗,并且该操作具有较长的时间消耗
removeAbandonedTimeout
不合理可能将正常的连接进行回收而不是归还