文章来源:临窗旋墨的博客
003-C3P0升级到最新0.9.5.5:并可以打印持有连接的线程
缘由是,这个异常:c3p0 force_kill_acquires A ResourcePool cannot acquire a new resource github.com/swaldman/c3…
2019年后,C3P0又更新了, 所以升级一波, 同时又希望可以看到连接中的堆栈信息
官网地址:www.mchange.com/projects/c3…
重要参数:
unreturnedConnectionTimeout
defines a limit (in seconds) to how long a Connection may remain checked out. If set to a nozero value, unreturned, checked-out Connections that exceed this limit will be summarily destroyed, and then replaced in the pool. Obviously, you must take care to set this parameter to a value large enough that all intended operations on checked out Connections have time to complete. You can use this parameter to merely workaround unreliable client apps that fail to close() Connections.Much better than working-around is fixing. If, in addition to setting
unreturnedConnectionTimeout
, you setdebugUnreturnedConnectionStackTraces
totrue
, then a stack trace will be captured each time a Connection is checked-out. Whenever an unreturned Connection times out, that stack trace will be printed, revealing where a Connection was checked out that was not checked in promptly.debugUnreturnedConnectionStackTraces
is intended to be used only for debugging, as capturing a stack trace can slow down Connection check-out.
unreturnedConnectionTimeout
大概意思就是一个连接可以保持多久,超出这个限制就会被销毁debugUnreturnedConnectionStackTraces
:大概意思就是每次checkout连接的时候都会捕捉堆栈信息, 当未退出的连接超时的时候,将打印堆栈跟踪, 显示相关代码位置- (上面的翻译是我瞎蒙的......)
源码的一些跟踪
C3P0PooledConnectionPool
构造函数中 传入了 上述两个参数
C3P0PooledConnectionPool(
......
int unreturnedConnectionTimeout, //seconds
boolean debugUnreturnedConnectionStackTraces,
final ResourcePoolFactory fact
......
){
....
fact.setDestroyOverdueResourceTime( unreturnedConnectionTimeout * 1000 );
fact.setDebugStoreCheckoutStackTrace( debugUnreturnedConnectionStackTraces );
....
}
相关参数(代码太长 略)
BasicResourcePool#checkoutResource
public Object checkoutResource( long timeout )
throws TimeoutException, ResourcePoolException, InterruptedException
{
.....
if (debug_store_checkout_exceptions)
card.checkoutStackTraceException = new Exception("DEBUG STACK TRACE: Overdue resource check-out stack trace.");
}
......
}
其中的
this.debug_store_checkout_exceptions = (debug_store_checkout_exceptions && destroy_unreturned_resc_time > 0);
- debug_store_checkout_exceptions: 即配置的
debugUnreturnedConnectionStackTraces
- destroy_unreturned_resc_time: 即构建
BasicResourcePool
时的destroy_overdue_resc_time参数, 也即来自配置的unreturnedConnectionTimeout
参数乘以1000
修改思路
由于本系统不适合设置unreturnedConnectionTimeout
参数, 因为我们的连接可能需要保持很久很久,
但是我们依然希望能捕捉到长时间保持连接的线程,因此可以在不新增参数的情况下,去掉这个debug_store_checkout_exceptions
判断, 直接在card中持有线程信息;
弊端:
如官网所说:捕获堆栈会降低checkout的效率
debugUnreturnedConnectionStackTraces
is intended to be used only for debugging, as capturing a stack trace can slow down Connection check-out.
代码修改:
PooledDataSource.java 中追加如下代码
public C3P0PooledConnectionPoolManager getPoolManagerRead() throws SQLException;
AbstractPoolBackedDataSource中追加如下实现
public C3P0PooledConnectionPoolManager getPoolManagerRead() {
return poolManager;
}
C3P0PooledConnectionPool中追加如下代码
// 以下代码为追加
public HashMap getManaged() {
return rp.getManaged();
}
public Exception getValueException(Object res) {
return rp.getValueException(res);
}
public long getCheckoutTime(Object res) {
return rp.getCheckoutTime(res);
}
ResourcePool中追加如下代码
public HashMap getManaged();
public Exception getValueException(Object resource);
public long getCheckoutTime(Object resource);
BasicResourcePool中追加如下代码实现
public HashMap getManaged() {
return cloneOfManaged();
}
public Exception getValueException(Object resource) {
return ((PunchCard) this.managed.get(resource)).checkoutStackTraceException;
}
public long getCheckoutTime(Object resource) {
return ((PunchCard) this.managed.get(resource)).checkout_time;
}
BasicResourcePool#checkoutResource中修改去掉debug_store_checkout_exceptions判断, 持有堆栈
// 去掉判断, 持有堆栈, 但是会降低checkout的效率
// if (debug_store_checkout_exceptions)
card.checkoutStackTraceException = new Exception(
"DEBUG STACK TRACE: Overdue resource check-out stack trace.");
获取当前持连接的堆栈信息代码片段
StringBuilder sb = new StringBuilder();
for (Iterator ii = C3P0Registry.getPooledDataSources().iterator(); ii.hasNext();) {
PooledDataSource pds = (PooledDataSource) ii.next();
sb.append(pds.toString());
// sb.append("池中的链接数:" + pds.getNumConnections());
sb.append("池中的链接数:" + pds.getNumConnectionsDefaultUser());
// sb.append("池中的空闲链接数:" + pds.getNumIdleConnections());
sb.append("池中的空闲链接数:" + pds.getNumIdleConnectionsDefaultUser());
// sb.append("池中的被checkout链接数:" + pds.getNumBusyConnections());
sb.append("池中的被checkout链接数:" + pds.getNumBusyConnectionsDefaultUser());
C3P0PooledConnectionPool pool = null;
try {
pool = pds.getPoolManagerRead().getPool();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
int timeCost = 18 * 1000;
Set keySet = null;
try {
keySet = pool.getManaged().keySet();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (keySet != null && !keySet.isEmpty()) {
for (Object key : keySet) {
long checkoutTime = 0;
try {
checkoutTime = pool.getCheckoutTime(key);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (checkoutTime > 0) {
long sub = System.currentTimeMillis() - checkoutTime;
if (sub > timeCost) {
Exception valueException = pool.getValueException(key);
if (valueException == null) {
continue;
}
{
String mess = "cost more than " + timeCost + " : " + sub;
logger.error(mess, valueException);
sb.append("-----------------------------\n");
StringWriter sw = new StringWriter();
valueException.printStackTrace(new PrintWriter(sw, true));
sb.append(mess);
sb.append(sw.getBuffer().toString());
sb.append("-----------------------------\n");
}
}
}
}
}
}
sb.append("\n" + "unclosedPooledDataSources/getNumPoolsAllDataSources:" + C3P0Registry.getNumPooledDataSources()
+ "/" + C3P0Registry.getNumPoolsAllDataSources());
Runtime runtime = Runtime.getRuntime();
long max = runtime.maxMemory();
sb.append("最大内存(是通过启动JAVA虚拟机时使用参数-Xmx100m指定的)=" + (max / 1024 / 1024) + "M");
long total = runtime.totalMemory();
sb.append("已分配内存 =" + (total / 1024 / 1024) + "M");
long free = runtime.freeMemory();
sb.append("已分配内存中的剩余空间=" + (free / 1024 / 1024) + "M");
sb.append("最大可用内存 =" + ((max - total + free) / 1024 / 1024) + "M");
String string = sb.toString();
string = string.replaceAll("->", "\t");
string = string.replaceAll(",", "\n");
logger.warn(string);
return string;
文章来源:临窗旋墨的博客