druid解析-应用线程获取连接

在《druid解析-DruidDataSource初始化》文章中提到过连接是通过getConnectionDirect()方法获取的,下面来看下该方法(代码有删减):

	//入参表示获取连接的超时时间
   public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
        int notFullTimeoutRetryCnt = 0;
        //连接拿不到,不断重试
        for (; ; ) {
            DruidPooledConnection poolableConnection;
            try {
                poolableConnection = getConnectionInternal(maxWaitMillis);
            } catch (GetConnectionTimeoutException ex) {
                if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
                    notFullTimeoutRetryCnt++;//超过一定失败次数,则获取连接失败并抛出异常
                    }
                    continue;//连接获取失败,重试
                }
                throw ex;
            }
			//testOnBorrow为true,则在返回连接前对连接校验
            if (testOnBorrow) {
            	//检查连接是否符合要求,检查内容有:
            	//1)ValidConnectionChecker检查连接,2)执行validationQuery的SQL语句
                boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                if (!validate) {
                    discardConnection(poolableConnection.holder);//关闭连接
                    continue;
                }
            } else {
                if (poolableConnection.conn.isClosed()) {
                    discardConnection(poolableConnection.holder); // 传入null,避免重复关闭
                    continue;
                }
				//testWhileIdle为true的话,当连接空闲超过timeBetweenEvictionRunsMillis毫秒时,调用testConnectionInternal()对连接检验
                if (testWhileIdle) {
                    final DruidConnectionHolder holder = poolableConnection.holder;
                    long currentTimeMillis = System.currentTimeMillis();
                    long lastActiveTimeMillis = holder.lastActiveTimeMillis;
                    long lastExecTimeMillis = holder.lastExecTimeMillis;
                    long lastKeepTimeMillis = holder.lastKeepTimeMillis;

                    if (checkExecuteTime
                            && lastExecTimeMillis != lastActiveTimeMillis) {
                        lastActiveTimeMillis = lastExecTimeMillis;
                    }

                    if (lastKeepTimeMillis > lastActiveTimeMillis) {
                        lastActiveTimeMillis = lastKeepTimeMillis;
                    }

                    long idleMillis = currentTimeMillis - lastActiveTimeMillis;

                    long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;

                    if (timeBetweenEvictionRunsMillis <= 0) {
                        timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
                    }
					//idleMillis表示连接的从上一次使用结束后到现在为止的空闲时间
                    if (idleMillis >= timeBetweenEvictionRunsMillis
                            || idleMillis < 0 // unexcepted branch
                    ) {
                    	//校验连接
                        boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                        if (!validate) {
                            discardConnection(poolableConnection.holder);
                            continue;
                        }
                    }
                }
            }

            if (removeAbandoned) {
                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                poolableConnection.connectStackTrace = stackTrace;
                poolableConnection.setConnectedTimeNano();
                poolableConnection.traceEnable = true;

                activeConnectionLock.lock();
                try {
                	//将连接保存在Map中,druid有一个清理线程,清理线程将该Map里面保存的不可用的连接销毁
                    activeConnections.put(poolableConnection, PRESENT);
                } finally {
                    activeConnectionLock.unlock();
                }
            }

            if (!this.defaultAutoCommit) {
                poolableConnection.setAutoCommit(false);//设置连接非自动提交,默认连接是自动提交
            }
            return poolableConnection;
        }
    }

getConnectionDirect()相对来说比较简单:

  1. 调用getConnectionInternal()获取连接;
  2. 如果testOnBorrow=true,则对连接进行校验;
  3. 如果testWhileIdle=true,testOnBorrow=false且连接空闲超过一定时间,则对连接进行校验;
  4. 如果removeAbandoned=true,则将连接放到一个Map中,供清理线程对连接清理。

下面介绍getConnectionInternal()方法,该方法是获取连接的核心方法(方法有删减):

    private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
        final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait);
        final int maxWaitThreadCount = this.maxWaitThreadCount;

        DruidConnectionHolder holder;

        for (boolean createDirect = false; ; ) {
            if (createDirect) {//如果createDirect=true,则直接创建连接,不通过连接池获取
                //createStartNanosUpdater没发现有什么用途
                createStartNanosUpdater.set(this, System.nanoTime());
                if (creatingCountUpdater.compareAndSet(this, 0, 1)) {
                    //创建连接
                    PhysicalConnectionInfo pyConnInfo = DruidDataSource.this.createPhysicalConnection();
                    holder = new DruidConnectionHolder(this, pyConnInfo);
                    holder.lastActiveTimeMillis = System.currentTimeMillis();

                    creatingCountUpdater.decrementAndGet(this);
                    directCreateCountUpdater.incrementAndGet(this);

                    boolean discard;
                    lock.lock();
                    try {

                        if (activeCount < maxActive) {
                            activeCount++;//活动连接数加1
                            holder.active = true;
                            if (activeCount > activePeak) {
                                activePeak = activeCount;
                                activePeakTime = System.currentTimeMillis();
                            }
                            break;//获取到连接,直接退出
                        } else {
                            //当活动连接数超过最大活动连接数时,需要将当前连接销毁
                            discard = true;
                        }
                    } finally {
                        lock.unlock();
                    }

                    if (discard) {
                        JdbcUtils.close(pyConnInfo.getPhysicalConnection());
                    }
                }
            }

            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                connectErrorCountUpdater.incrementAndGet(this);
                throw new SQLException("interrupt", e);
            }

            try {
                //如果当前等待获取连接的应用线程数超过了maxWaitThreadCount,则抛出异常
                //这也就意味着禁止后续的线程获取连接了
                if (maxWaitThreadCount > 0
                        && notEmptyWaitThreadCount >= maxWaitThreadCount) {
                    connectErrorCountUpdater.incrementAndGet(this);
                    throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count "
                            + lock.getQueueLength());
                }

                if (onFatalError
                        && onFatalErrorMaxActive > 0
                        && activeCount >= onFatalErrorMaxActive) {
                    connectErrorCountUpdater.incrementAndGet(this);

                    throw new SQLException(lastFatalError);
                }
                //计数器,表示总共获取连接的次数
                connectCount++;
                //如果定时任务不为空,连接池个数为0,当前活动连接数小于最大连接数,
                //定时任务里面有待执行任务,且没有其他线程正在创建连接,
                //那么允许通过Driver直接创建连接
                if (createScheduler != null
                        && poolingCount == 0
                        && activeCount < maxActive
                        && creatingCountUpdater.get(this) == 0
                        && createScheduler instanceof ScheduledThreadPoolExecutor) {
                    ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) createScheduler;
                    if (executor.getQueue().size() > 0) {
                        createDirect = true;
                        continue;
                    }
                }
                //下面的方法为从连接池里面拿连接
                if (maxWait > 0) {
                    holder = pollLast(nanos);
                } else {
                    holder = takeLast();
                }

                if (holder != null) {
                    if (holder.discard) {
                        continue;
                    }
                    //活动连接数加1
                    activeCount++;
                    holder.active = true;
                    if (activeCount > activePeak) {
                        activePeak = activeCount;
                        activePeakTime = System.currentTimeMillis();
                    }
                }
            } catch (InterruptedException e) {
                throw new SQLException(e.getMessage(), e);
            } catch (SQLException e) {
                throw e;
            } finally {
                lock.unlock();
            }

            break;
        }

        if (holder == null) {
            //如果连接没有获取到,则抛出异常,这里代码做了省略
        }
        //用于统计,如果连接使用一次,计数器加1
        holder.incrementUseCount();

        DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);
        return poolalbeConnection;
    }

getConnectionInternal()方法有两种方式返回连接,一种是从连接池里面获取,另一种是直接调用Driver的创建连接方法获取,但是要使用第二种需要满足非常苛刻的条件:

  • 定时任务对象不为null;
  • 连接池个数为0;
  • 当前活动连接数小于最大活动连接数;
  • 定时任务里面有待执行任务;
  • 没有其他线程正在创建连接。

方法getConnectionInternal()通过pollLast(nanos)或者takeLast()从连接池里面拿连接,而且都是从连接池的最后获取,这就意味着越频繁使用的连接越会在下次使用。这两个方法的最后都是执行下面的逻辑:

		//连接池个数减1
        decrementPoolingCount();
        //从连接池的最后获取连接,last对象就是方法返回值
        DruidConnectionHolder last = connections[poolingCount];
        connections[poolingCount] = null;

pollLast(nanos)和takeLast()两个方法的区别在于,pollLast()会不断的尝试获取连接,当获取连接的等待时间超过了入参nanos,该方法直接返回null,而takeLast()会一直尝试获取连接,直到获取到连接为止。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值