C3P0升级到最新0.9.5.5:并可以打印持有连接的线程堆栈

文章来源原创   作者:许秋冬   发布时间:2020-06-28   阅读:2669   标签:连接池 分类:第三方包 专题:

[toc]

003-C3P0升级到最新0.9.5.5:并可以打印持有连接的线程

缘由是,这个异常:c3p0 force_kill_acquires A ResourcePool cannot acquire a new resource
https://github.com/swaldman/c3p0/issues/117;

2019年后,C3P0又更新了, 所以升级一波, 同时又希望可以看到连接中的堆栈信息

官网地址:https://www.mchange.com/projects/c3p0/

重要参数:

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 set debugUnreturnedConnectionStackTraces to true, 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构造函数中 传入了 上述两个参数
  1. C3P0PooledConnectionPool(
  2. ......
  3. int unreturnedConnectionTimeout, //seconds
  4. boolean debugUnreturnedConnectionStackTraces,
  5. final ResourcePoolFactory fact
  6. ......
  7. ){
  8. ....
  9. fact.setDestroyOverdueResourceTime( unreturnedConnectionTimeout * 1000 );
  10. fact.setDebugStoreCheckoutStackTrace( debugUnreturnedConnectionStackTraces );
  11. ....
  12. }

相关参数(代码太长 略)

BasicResourcePool#checkoutResource
  1. public Object checkoutResource( long timeout )
  2. throws TimeoutException, ResourcePoolException, InterruptedException
  3. {
  4. .....
  5. if (debug_store_checkout_exceptions)
  6. card.checkoutStackTraceException = new Exception("DEBUG STACK TRACE: Overdue resource check-out stack trace.");
  7. }
  8. ......
  9. }

其中的

  1. 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 中追加如下代码
  1. public C3P0PooledConnectionPoolManager getPoolManagerRead() throws SQLException;
AbstractPoolBackedDataSource中追加如下实现
  1. public C3P0PooledConnectionPoolManager getPoolManagerRead() {
  2. return poolManager;
  3. }

C3P0PooledConnectionPool中追加如下代码

  1. // 以下代码为追加
  2. public HashMap getManaged() {
  3. return rp.getManaged();
  4. }
  5. public Exception getValueException(Object res) {
  6. return rp.getValueException(res);
  7. }
  8. public long getCheckoutTime(Object res) {
  9. return rp.getCheckoutTime(res);
  10. }

ResourcePool中追加如下代码

  1. public HashMap getManaged();
  2. public Exception getValueException(Object resource);
  3. public long getCheckoutTime(Object resource);

BasicResourcePool中追加如下代码实现

  1. public HashMap getManaged() {
  2. return cloneOfManaged();
  3. }
  4. public Exception getValueException(Object resource) {
  5. return ((PunchCard) this.managed.get(resource)).checkoutStackTraceException;
  6. }
  7. public long getCheckoutTime(Object resource) {
  8. return ((PunchCard) this.managed.get(resource)).checkout_time;
  9. }

BasicResourcePool#checkoutResource中修改去掉debug_store_checkout_exceptions判断, 持有堆栈

  1. // 去掉判断, 持有堆栈, 但是会降低checkout的效率
  2. // if (debug_store_checkout_exceptions)
  3. card.checkoutStackTraceException = new Exception(
  4. "DEBUG STACK TRACE: Overdue resource check-out stack trace.");

获取当前持连接的堆栈信息代码片段

  1. StringBuilder sb = new StringBuilder();
  2. for (Iterator ii = C3P0Registry.getPooledDataSources().iterator(); ii.hasNext();) {
  3. PooledDataSource pds = (PooledDataSource) ii.next();
  4. sb.append(pds.toString());
  5. // sb.append("池中的链接数:" + pds.getNumConnections());
  6. sb.append("池中的链接数:" + pds.getNumConnectionsDefaultUser());
  7. // sb.append("池中的空闲链接数:" + pds.getNumIdleConnections());
  8. sb.append("池中的空闲链接数:" + pds.getNumIdleConnectionsDefaultUser());
  9. // sb.append("池中的被checkout链接数:" + pds.getNumBusyConnections());
  10. sb.append("池中的被checkout链接数:" + pds.getNumBusyConnectionsDefaultUser());
  11. C3P0PooledConnectionPool pool = null;
  12. try {
  13. pool = pds.getPoolManagerRead().getPool();
  14. } catch (Exception e) {
  15. logger.error(e.getMessage(), e);
  16. }
  17. int timeCost = 18 * 1000;
  18. Set keySet = null;
  19. try {
  20. keySet = pool.getManaged().keySet();
  21. } catch (Exception e) {
  22. logger.error(e.getMessage(), e);
  23. }
  24. if (keySet != null && !keySet.isEmpty()) {
  25. for (Object key : keySet) {
  26. long checkoutTime = 0;
  27. try {
  28. checkoutTime = pool.getCheckoutTime(key);
  29. } catch (Exception e) {
  30. logger.error(e.getMessage(), e);
  31. }
  32. if (checkoutTime > 0) {
  33. long sub = System.currentTimeMillis() - checkoutTime;
  34. if (sub > timeCost) {
  35. Exception valueException = pool.getValueException(key);
  36. if (valueException == null) {
  37. continue;
  38. }
  39. {
  40. String mess = "cost more than " + timeCost + " : " + sub;
  41. logger.error(mess, valueException);
  42. sb.append("-----------------------------\n");
  43. StringWriter sw = new StringWriter();
  44. valueException.printStackTrace(new PrintWriter(sw, true));
  45. sb.append(mess);
  46. sb.append(sw.getBuffer().toString());
  47. sb.append("-----------------------------\n");
  48. }
  49. }
  50. }
  51. }
  52. }
  53. }
  54. sb.append("\n" + "unclosedPooledDataSources/getNumPoolsAllDataSources:" + C3P0Registry.getNumPooledDataSources()
  55. + "/" + C3P0Registry.getNumPoolsAllDataSources());
  56. Runtime runtime = Runtime.getRuntime();
  57. long max = runtime.maxMemory();
  58. sb.append("最大内存(是通过启动JAVA虚拟机时使用参数-Xmx100m指定的)=" + (max / 1024 / 1024) + "M");
  59. long total = runtime.totalMemory();
  60. sb.append("已分配内存 =" + (total / 1024 / 1024) + "M");
  61. long free = runtime.freeMemory();
  62. sb.append("已分配内存中的剩余空间=" + (free / 1024 / 1024) + "M");
  63. sb.append("最大可用内存 =" + ((max - total + free) / 1024 / 1024) + "M");
  64. String string = sb.toString();
  65. string = string.replaceAll("->", "\t");
  66. string = string.replaceAll(",", "\n");
  67. logger.warn(string);
  68. return string;

发表评论

目录