当前位置:   article > 正文

druid数据源连接数异常增长,超过配置的最大连接数_druid连接池超过最大连接数

druid连接池超过最大连接数

个人原创,拒绝转载

数据源连接池配置如下

  1. dataSource.setDriverClassName(driver);
  2. dataSource.setUsername(username);
  3. dataSource.setPassword(password);
  4. dataSource.setUrl(url);
  5. dataSource.setInitialSize(1);
  6. dataSource.setMinIdle(3);
  7. dataSource.setMaxActive(20);
  8. dataSource.setLoginTimeout(50);
  9. dataSource.setKeepAlive(true);
  10. dataSource.setBreakAfterAcquireFailure(true);
  11. dataSource.setConnectionErrorRetryAttempts(5);
  12. dataSource.setMaxWait(80000);
  13. dataSource.setMinEvictableIdleTimeMillis(60000);
  14. dataSource.setTestWhileIdle(true);
  15. if (driver.toLowerCase().contains("oracle")) {
  16. dataSource.setValidationQuery("SELECT 1 FROM dual");
  17. } else {
  18. dataSource.setValidationQuery("SELECT 1");
  19. }
  20. dataSource.setTimeBetweenEvictionRunsMillis(60000);
  21. dataSource.setTestOnBorrow(false);
  22. // dataSource.setTestOnReturn(false);
  23. dataSource.setPoolPreparedStatements(true);
  24. dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
  25. dataSource.setRemoveAbandoned(true);
  26. dataSource.setLogAbandoned(true);

理论上来讲,最大的连接数上限就是20了。但实际并不是

从oracle端查看连接数

可以确认,确实超过了druid的连接池上限。

从网上看来的druid连接池的流程图

大致看完之后开始debug

意外的发现,DestroyTask的逻辑似乎有点问题。

 

 第一次被丢弃的连接,意外的又出现在了connections里

那这里就可以猜测,是connections的维护出了问题,要么是DestroyTask,要么是CreateTask。

最后直接翻源码,加上个人的注释

  1. public void shrink(boolean checkTime, boolean keepAlive) {
  2. try {
  3. lock.lockInterruptibly();
  4. } catch (InterruptedException e) {
  5. return;
  6. }
  7. boolean needFill = false;
  8. int evictCount = 0;
  9. int keepAliveCount = 0;
  10. int fatalErrorIncrement = fatalErrorCount - fatalErrorCountLastShrink;
  11. fatalErrorCountLastShrink = fatalErrorCount;
  12. try {
  13. if (!inited) {
  14. return;
  15. }
  16. final int checkCount = poolingCount - minIdle;
  17. final long currentTimeMillis = System.currentTimeMillis();
  18. for (int i = 0; i < poolingCount; ++i) {
  19. //遍历持有的连接池
  20. DruidConnectionHolder connection = connections[i];
  21. if ((onFatalError || fatalErrorIncrement > 0) && (lastFatalErrorTimeMillis > connection.connectTimeMillis)) {
  22. keepAliveConnections[keepAliveCount++] = connection;
  23. continue;
  24. }
  25. if (checkTime) {
  26. if (phyTimeoutMillis > 0) {
  27. //治理没有设置 不会走该分支
  28. long phyConnectTimeMillis = currentTimeMillis - connection.connectTimeMillis;
  29. if (phyConnectTimeMillis > phyTimeoutMillis) {
  30. evictConnections[evictCount++] = connection;
  31. continue;
  32. }
  33. }
  34. long idleMillis = currentTimeMillis - connection.lastActiveTimeMillis;
  35. if (idleMillis < minEvictableIdleTimeMillis
  36. && idleMillis < keepAliveBetweenTimeMillis
  37. ) {
  38. break;
  39. }
  40. //如果连接的空闲时间超过了配置的最小空闲时间 60s
  41. if (idleMillis >= minEvictableIdleTimeMillis) {
  42. //checkCount是需要驱逐的线程数,即前checkCount个连接直接驱逐
  43. //bug1 connections对象并没有按照时间来排序,如果需要被驱逐的对象下标比checkCount大,则不会被驱逐
  44. if (checkTime && i < checkCount) {
  45. evictConnections[evictCount++] = connection;
  46. continue;
  47. } else if (idleMillis > maxEvictableIdleTimeMillis) {
  48. //如果空闲的时间超过最大的空闲时间 默认7天 直接驱逐
  49. evictConnections[evictCount++] = connection;
  50. continue;
  51. }
  52. }
  53. //如果设置了保活 且空闲时间超过2分钟(默认),进入保活数组
  54. if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis) {
  55. keepAliveConnections[keepAliveCount++] = connection;
  56. }
  57. } else {
  58. if (i < checkCount) {
  59. evictConnections[evictCount++] = connection;
  60. } else {
  61. break;
  62. }
  63. }
  64. }
  65. //需要从connections中移除的连接数 驱逐数+保活数
  66. int removeCount = evictCount + keepAliveCount;
  67. if (removeCount > 0) {
  68. //数组复制 假设connections=[1,2,3,4,5],removeCount=3 得到的新connections=[4,5,3,4,5]
  69. System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
  70. //填充connections = [4,5,null,null,null]
  71. /**
  72. * bug2 如果2,4,5是需要保活的对象,那么此时,4,5是在connections里的,2,4,5在keepAliveConnections中,1,3则直接丢失了引用,那么从这里开始,就会进入异常增长
  73. * 因为1,3只是丢失了引用,实际上连接还存在,只是connections不再维护它
  74. * 为什么minIdle设置为1没用问题?因为会触发强制驱逐的逻辑
  75. */
  76. Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
  77. poolingCount -= removeCount;
  78. }
  79. keepAliveCheckCount += keepAliveCount;
  80. if (keepAlive && poolingCount + activeCount < minIdle) {
  81. //如果removeCount > 0
  82. needFill = true;
  83. }
  84. } finally {
  85. lock.unlock();
  86. }
  87. if (evictCount > 0) {
  88. for (int i = 0; i < evictCount; ++i) {
  89. DruidConnectionHolder item = evictConnections[i];
  90. Connection connection = item.getConnection();
  91. JdbcUtils.close(connection);
  92. destroyCountUpdater.incrementAndGet(this);
  93. }
  94. Arrays.fill(evictConnections, null);
  95. }
  96. if (keepAliveCount > 0) {
  97. // keep order
  98. for (int i = keepAliveCount - 1; i >= 0; --i) {
  99. DruidConnectionHolder holer = keepAliveConnections[i];
  100. Connection connection = holer.getConnection();
  101. holer.incrementKeepAliveCheckCount();
  102. //校验连接是否可用,如果不可用,则丢弃
  103. boolean validate = false;
  104. try {
  105. this.validateConnection(connection);
  106. validate = true;
  107. } catch (Throwable error) {
  108. if (LOG.isDebugEnabled()) {
  109. LOG.debug("keepAliveErr", error);
  110. }
  111. // skip
  112. }
  113. boolean discard = !validate;
  114. if (validate) {
  115. holer.lastKeepTimeMillis = System.currentTimeMillis();
  116. //尝试把保活的connection重新添加到connections中
  117. boolean putOk = put(holer, 0L, true);
  118. if (!putOk) {
  119. //bug2 5重复加入到connections,会返回false,然后进入到丢弃的逻辑
  120. discard = true;
  121. }
  122. }
  123. if (discard) {
  124. try {
  125. //关闭连接5
  126. //bug2 记住此时,连接5已经被关闭了,但是它还在connections中,然后在下一次的循环中,又被取出来了尝试进行保活,
  127. connection.close();
  128. } catch (Exception e) {
  129. // skip
  130. }
  131. lock.lock();
  132. try {
  133. discardCount++;
  134. if (activeCount + poolingCount <= minIdle) {
  135. //因为连接5加入connections失败,所以会触发该信号,create thread会监听这个信号,执行新建连接的逻辑
  136. emptySignal();
  137. }
  138. } finally {
  139. lock.unlock();
  140. }
  141. }
  142. }
  143. this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount);
  144. Arrays.fill(keepAliveConnections, null);
  145. }
  146. if (needFill) {
  147. lock.lock();
  148. try {
  149. int fillCount = minIdle - (activeCount + poolingCount + createTaskCount);
  150. for (int i = 0; i < fillCount; ++i) {
  151. emptySignal();
  152. }
  153. } finally {
  154. lock.unlock();
  155. }
  156. } else if (onFatalError || fatalErrorIncrement > 0) {
  157. lock.lock();
  158. try {
  159. emptySignal();
  160. } finally {
  161. lock.unlock();
  162. }
  163. }
  164. }

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号