druid源码分析—DruidDataSource的基本分析(七)

销毁连接分析

在创建连接的init()方法,注意到创建了线程DestroyConnectionThread用来销毁连接的,线程循环是通过timeBetweenEvictionRunsMillis 参数来设置连接检查的时间间隔。

        public void run() {
        	//计数器减一
            initedLatch.countDown();

            for (;;) {
                // 从前面开始删除
                try {
                	//如果连接池未关闭
                    if (closed || closing) {
                        break;
                    }
                    //设置时间间隔休眠
                    if (timeBetweenEvictionRunsMillis > 0) {
                        Thread.sleep(timeBetweenEvictionRunsMillis);
                    } else {
                        Thread.sleep(1000); 
                    }
                    //如果发生中断异常则退出
                    if (Thread.interrupted()) {
                        break;
                    }
                    //运行销毁任务
                    destroyTask.run();
                } catch (InterruptedException e) {
                    break;
                }
            }
        }

DestroyTask

销毁任务类,通过DestroyConnectionThread线程直接调用,一旦有自定义销毁任务时,才会变为线程执行。

        @Override
        public void run() {
        	//检查连接
            shrink(true, keepAlive);
            //移出废弃的连接
            if (isRemoveAbandoned()) {
                removeAbandoned();
            }
        }

shrink

该方法主要是检查连接,并销毁连接。

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 {
        	//如果还未进行初始化,则进行init()
            if (!inited) {
                return;
            }
            //剩余空闲连接数 = 连接池中连接数- 最小空闲连接数
            final int checkCount = poolingCount - minIdle;
            final long currentTimeMillis = System.currentTimeMillis();
            for (int i = 0; i < poolingCount; ++i) {
            	//从连接池中拿连接
                DruidConnectionHolder connection = connections[i];
                //(如果发生致命错误或者致命错误次数大于0)&& 最后致命错误时间大于当前连接的连接时间
                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;
                    }
                    //空闲时间 >= 最小存活时间,将连接放入销毁池中
                    if (idleMillis >= minEvictableIdleTimeMillis) {
                        if (checkTime && i < checkCount) {
                            evictConnections[evictCount++] = connection;
                            continue;
                        } else if (idleMillis > maxEvictableIdleTimeMillis) {
                            evictConnections[evictCount++] = connection;
                            continue;
                        }
                    }
                    //开启保活机制 && 空闲时间 >= 连接保活时间,将连接放入存活池中。
                    if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis) {
                        keepAliveConnections[keepAliveCount++] = connection;
                    }
                //如果未开启检查,则仅留下最小空闲连接。
                } else {
                    if (i < checkCount) {
                        evictConnections[evictCount++] = connection;
                    } else {
                        break;
                    }
                }
            }
            //删除数量 = 销毁连接数量+ 活跃连接数量
            int removeCount = evictCount + keepAliveCount;
            if (removeCount > 0) {
                System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
                Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
                //空闲连接池数量 = 空闲连接池数量-删除数量
                poolingCount -= removeCount;
            }
            //连接池检查数量 = 连接池检查数量 + 活跃连接数量
            keepAliveCheckCount += keepAliveCount;
            //
            if (keepAlive && poolingCount + activeCount < minIdle) {
                needFill = true;
            }
        } finally {
            lock.unlock();
        }
        //如果销毁数量大于0,则关闭这些连接
        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);
        }
        //如果活跃连接数量 >0 ,则对连接进行校验
        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();
                    boolean putOk = put(holer, 0L, true);
                    if (!putOk) {
                        discard = true;
                    }
                }
                //如果连接不可用,则关闭
                if (discard) {
                    try {
                        connection.close();
                    } catch (Exception e) {
                        // skip
                    }

                    lock.lock();
                    try {
                        discardCount++;

                        if (activeCount + poolingCount <= minIdle) {
                            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();
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值