赞
踩
个人原创,拒绝转载
数据源连接池配置如下
- dataSource.setDriverClassName(driver);
- dataSource.setUsername(username);
- dataSource.setPassword(password);
- dataSource.setUrl(url);
- dataSource.setInitialSize(1);
- dataSource.setMinIdle(3);
- dataSource.setMaxActive(20);
- dataSource.setLoginTimeout(50);
- dataSource.setKeepAlive(true);
- dataSource.setBreakAfterAcquireFailure(true);
- dataSource.setConnectionErrorRetryAttempts(5);
-
- dataSource.setMaxWait(80000);
-
- dataSource.setMinEvictableIdleTimeMillis(60000);
-
- dataSource.setTestWhileIdle(true);
- if (driver.toLowerCase().contains("oracle")) {
- dataSource.setValidationQuery("SELECT 1 FROM dual");
- } else {
- dataSource.setValidationQuery("SELECT 1");
- }
-
- dataSource.setTimeBetweenEvictionRunsMillis(60000);
-
- dataSource.setTestOnBorrow(false);
- // dataSource.setTestOnReturn(false);
-
-
- dataSource.setPoolPreparedStatements(true);
- dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
-
- dataSource.setRemoveAbandoned(true);
- dataSource.setLogAbandoned(true);
理论上来讲,最大的连接数上限就是20了。但实际并不是
从oracle端查看连接数
可以确认,确实超过了druid的连接池上限。
从网上看来的druid连接池的流程图
大致看完之后开始debug
意外的发现,DestroyTask的逻辑似乎有点问题。
第一次被丢弃的连接,意外的又出现在了connections里
那这里就可以猜测,是connections的维护出了问题,要么是DestroyTask,要么是CreateTask。
最后直接翻源码,加上个人的注释
- public void shrink(boolean checkTime, boolean keepAlive) {
- try {
- lock.lockInterruptibly();
- } catch (InterruptedException e) {
- return;
- }
-
- boolean needFill = false;
- int evictCount = 0;
- int keepAliveCount = 0;
- int fatalErrorIncrement = fatalErrorCount - fatalErrorCountLastShrink;
- fatalErrorCountLastShrink = fatalErrorCount;
-
- try {
- if (!inited) {
- return;
- }
-
- final int checkCount = poolingCount - minIdle;
- final long currentTimeMillis = System.currentTimeMillis();
- for (int i = 0; i < poolingCount; ++i) {
- //遍历持有的连接池
- DruidConnectionHolder connection = connections[i];
-
- if ((onFatalError || fatalErrorIncrement > 0) && (lastFatalErrorTimeMillis > connection.connectTimeMillis)) {
- keepAliveConnections[keepAliveCount++] = connection;
- continue;
- }
-
- if (checkTime) {
- if (phyTimeoutMillis > 0) {
- //治理没有设置 不会走该分支
- long phyConnectTimeMillis = currentTimeMillis - connection.connectTimeMillis;
- if (phyConnectTimeMillis > phyTimeoutMillis) {
- evictConnections[evictCount++] = connection;
- continue;
- }
- }
-
- long idleMillis = currentTimeMillis - connection.lastActiveTimeMillis;
-
- if (idleMillis < minEvictableIdleTimeMillis
- && idleMillis < keepAliveBetweenTimeMillis
- ) {
- break;
- }
-
- //如果连接的空闲时间超过了配置的最小空闲时间 60s
- if (idleMillis >= minEvictableIdleTimeMillis) {
-
- //checkCount是需要驱逐的线程数,即前checkCount个连接直接驱逐
- //bug1 connections对象并没有按照时间来排序,如果需要被驱逐的对象下标比checkCount大,则不会被驱逐
- if (checkTime && i < checkCount) {
- evictConnections[evictCount++] = connection;
- continue;
- } else if (idleMillis > maxEvictableIdleTimeMillis) {
- //如果空闲的时间超过最大的空闲时间 默认7天 直接驱逐
- evictConnections[evictCount++] = connection;
- continue;
- }
- }
-
- //如果设置了保活 且空闲时间超过2分钟(默认),进入保活数组
- if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis) {
- keepAliveConnections[keepAliveCount++] = connection;
- }
- } else {
- if (i < checkCount) {
- evictConnections[evictCount++] = connection;
- } else {
- break;
- }
- }
- }
-
- //需要从connections中移除的连接数 驱逐数+保活数
- int removeCount = evictCount + keepAliveCount;
- if (removeCount > 0) {
- //数组复制 假设connections=[1,2,3,4,5],removeCount=3 得到的新connections=[4,5,3,4,5]
- System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
- //填充connections = [4,5,null,null,null]
- /**
- * bug2 如果2,4,5是需要保活的对象,那么此时,4,5是在connections里的,2,4,5在keepAliveConnections中,1,3则直接丢失了引用,那么从这里开始,就会进入异常增长
- * 因为1,3只是丢失了引用,实际上连接还存在,只是connections不再维护它
- * 为什么minIdle设置为1没用问题?因为会触发强制驱逐的逻辑
- */
-
- Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
- poolingCount -= removeCount;
- }
- keepAliveCheckCount += keepAliveCount;
-
- if (keepAlive && poolingCount + activeCount < minIdle) {
- //如果removeCount > 0
- needFill = true;
- }
- } finally {
- lock.unlock();
- }
-
- if (evictCount > 0) {
- for (int i = 0; i < evictCount; ++i) {
- DruidConnectionHolder item = evictConnections[i];
- Connection connection = item.getConnection();
- JdbcUtils.close(connection);
- destroyCountUpdater.incrementAndGet(this);
- }
- Arrays.fill(evictConnections, null);
- }
-
- if (keepAliveCount > 0) {
- // keep order
- for (int i = keepAliveCount - 1; i >= 0; --i) {
- DruidConnectionHolder holer = keepAliveConnections[i];
- Connection connection = holer.getConnection();
- holer.incrementKeepAliveCheckCount();
-
- //校验连接是否可用,如果不可用,则丢弃
- boolean validate = false;
- try {
- this.validateConnection(connection);
- validate = true;
- } catch (Throwable error) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("keepAliveErr", error);
- }
- // skip
- }
-
- boolean discard = !validate;
- if (validate) {
- holer.lastKeepTimeMillis = System.currentTimeMillis();
- //尝试把保活的connection重新添加到connections中
-
- boolean putOk = put(holer, 0L, true);
- if (!putOk) {
- //bug2 5重复加入到connections,会返回false,然后进入到丢弃的逻辑
- discard = true;
- }
- }
-
- if (discard) {
- try {
- //关闭连接5
- //bug2 记住此时,连接5已经被关闭了,但是它还在connections中,然后在下一次的循环中,又被取出来了尝试进行保活,
- connection.close();
- } catch (Exception e) {
- // skip
- }
-
- lock.lock();
- try {
- discardCount++;
-
- if (activeCount + poolingCount <= minIdle) {
- //因为连接5加入connections失败,所以会触发该信号,create thread会监听这个信号,执行新建连接的逻辑
- emptySignal();
- }
- } finally {
- lock.unlock();
- }
- }
- }
- this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount);
- Arrays.fill(keepAliveConnections, null);
- }
-
- if (needFill) {
- lock.lock();
- try {
- int fillCount = minIdle - (activeCount + poolingCount + createTaskCount);
- for (int i = 0; i < fillCount; ++i) {
- emptySignal();
- }
- } finally {
- lock.unlock();
- }
- } else if (onFatalError || fatalErrorIncrement > 0) {
- lock.lock();
- try {
- emptySignal();
- } finally {
- lock.unlock();
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。