2022-05-17 Druid源码阅读——Druid在什么时候会创建连接(二)

Druid在什么时候会创建连接

  • CreateConnectionTask#runInternal

    CreateConnectionTask是用于Druid中用于创建连接的任务,实现了Runnable接口,主要用于向线程池中提交任务。

    public class CreateConnectionTask implements Runnable {
    
        private int errorCount   = 0;
        private boolean initTask = false;
        private final long taskId;
    
        public CreateConnectionTask() {
            taskId = createTaskIdSeedUpdater.getAndIncrement(DruidDataSource.this);
        }
    
        public CreateConnectionTask(boolean initTask) {
            taskId = createTaskIdSeedUpdater.getAndIncrement(DruidDataSource.this);
            this.initTask = initTask;
        }
    
        @Override
        public void run() {
            runInternal();
        }
    
        private void runInternal() {
            //创建循环,保证可以获取到连接
            for (;;) {
    
                // addLast
                //加锁创建
                lock.lock();
                try {
                    //如果当前连接池被关闭或者处于关闭状态
                    if (closed || closing) {
                        //清除创建任务,停止执行
                        clearCreateTask(taskId);
                        return;
                    }
                    //空等待状态
                    boolean emptyWait = true;
                    //如果出现创建异常并且连接池中没有连接,空等待状态设为false
                    if (createError != null && poolingCount == 0) {
                        emptyWait = false;
                    }
                    //判断是否存在空等待状态
                    if (emptyWait) {
                        // 必须存在线程等待,才创建连接
                        if (poolingCount >= notEmptyWaitThreadCount //
                            && (!(keepAlive && activeCount + poolingCount < minIdle)) // 在keepAlive场景不能放弃创建
                            && (!initTask) // 线程池初始化时的任务不能放弃创建
                            && !isFailContinuous() // failContinuous时不能放弃创建,否则会无法创建线程
                            && !isOnFatalError() // onFatalError时不能放弃创建,否则会无法创建线程
                           ) {
                            //清理任务
                            clearCreateTask(taskId);
                            return;
                        }
    
                        // 防止创建超过maxActive数量的连接
                        if (activeCount + poolingCount >= maxActive) {
                            //清理任务
                            clearCreateTask(taskId);
                            return;
                        }
                    }
                } finally {
                    //解锁
                    lock.unlock();
                }
                //物理线程
                PhysicalConnectionInfo physicalConnection = null;
    
                try {
                    //创建物理线程
                    physicalConnection = createPhysicalConnection();
                } catch (OutOfMemoryError e) {
                    LOG.error("create connection OutOfMemoryError, out memory. ", e);
                    //创建异常数自增
                    errorCount++;
                    //如果创建异常次数大于重试次数并且timeBetweenConnectErrorMillis>0
                    if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {
                        // fail over retry attempts
                        setFailContinuous(true);
                        //如果开启了快速失败
                        if (failFast) {
                            //加锁,唤醒
                            lock.lock();
                            try {
                                //发出信号,唤醒所有在等待的线程
                                notEmpty.signalAll();
                            } finally {
                                lock.unlock();
                            }
                        }
                        //如果开启了获取失败后中断
                        if (breakAfterAcquireFailure) {
                            //加锁,清除任务,终止执行
                            lock.lock();
                            try {
                                clearCreateTask(taskId);
                            } finally {
                                lock.unlock();
                            }
                            return;
                        }
                        //重置异常计数
                        this.errorCount = 0; // reset errorCount
                        //如果连接池关闭中已关闭,加锁清除任务,终止执行
                        if (closing || closed) {
                            lock.lock();
                            try {
                                clearCreateTask(taskId);
                            } finally {
                                lock.unlock();
                            }
                            return;
                        }
                        //设置调度任务,在timeBetweenConnectErrorMillis后重新创建
                        createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS);
                        return;
                    }
                } catch (SQLException e) {
                    LOG.error("create connection SQLException, url: " + jdbcUrl, e);
    
                    errorCount++;
                    if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {
                        // fail over retry attempts
                        setFailContinuous(true);
                        if (failFast) {
                            lock.lock();
                            try {
                                notEmpty.signalAll();
                            } finally {
                                lock.unlock();
                            }
                        }
    
                        if (breakAfterAcquireFailure) {
                            lock.lock();
                            try {
                                clearCreateTask(taskId);
                            } finally {
                                lock.unlock();
                            }
                            return;
                        }
    
                        this.errorCount = 0; // reset errorCount
                        if (closing || closed) {
                            lock.lock();
                            try {
                                clearCreateTask(taskId);
                            } finally {
                                lock.unlock();
                            }
                            return;
                        }
    
                        createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS);
                        return;
                    }
                } catch (RuntimeException e) {
                    LOG.error("create connection RuntimeException", e);
                    // unknow fatal exception
                    setFailContinuous(true);
                    continue;
                } catch (Error e) {
                    //发生Error时,清除任务,跳出循环中止执行
                    lock.lock();
                    try {
                        clearCreateTask(taskId);
                    } finally {
                        lock.unlock();
                    }
                    LOG.error("create connection Error", e);
                    // unknow fatal exception
                    setFailContinuous(true);
                    break;
                } catch (Throwable e) {
                    //发生Throwable时,清除任务,跳出循环中止执行
                    lock.lock();
                    try {
                        clearCreateTask(taskId);
                    } finally {
                        lock.unlock();
                    }
    
                    LOG.error("create connection unexecpted error.", e);
                    break;
                }
                //如果连接创建失败,重新创建
                if (physicalConnection == null) {
                    continue;
                }
                //设置物理连接的创建任务ID
                physicalConnection.createTaskId = taskId;
                //将连接置入连接池中
                boolean result = put(physicalConnection);
                //如果置入连接池失败,关闭当前连接
                if (!result) {
                    JdbcUtils.close(physicalConnection.getPhysicalConnection());
                    LOG.info("put physical connection to pool failed.");
                }
                //由于创建连接成功,跳出循环
                break;
            }
        }
    }
    
  • CreateConnectionThread#run

    CreateConnectionThread是Druid中用于创建连接的线程,在不设置线程池的情况下将以此种方式自动创建线程,通过此方式创建,需要接收到empty的唤醒。

    public class CreateConnectionThread extends Thread {
    
        public CreateConnectionThread(String name){
            super(name);
            this.setDaemon(true);
        }
    
        public void run() {
            //闭锁计数减少,连接池初始化时创建线程时用
            initedLatch.countDown();
            //最后丢弃计数
            long lastDiscardCount = 0;
            //异常数
            int errorCount = 0;
            for (;;) {
                // addLast
                try {
                    //可中断锁
                    lock.lockInterruptibly();
                } catch (InterruptedException e2) {
                    //如果当前锁被中断,跳出循环,放弃创建线程
                    break;
                }
                //获得连接池中的丢弃计数
                long discardCount = DruidDataSource.this.discardCount;
                //如果丢弃数被更改
                boolean discardChanged = discardCount - lastDiscardCount > 0;
                //记录当前连接池中的丢弃数
                lastDiscardCount = discardCount;
    
                try {
                    //空等待状态
                    boolean emptyWait = true;
                    // 如果存在创建异常、连接池中没有连接、丢弃数没有被更改
                    if (createError != null
                        && poolingCount == 0
                        && !discardChanged) {
                        //不进行等待
                        emptyWait = false;
                    }
                    //如果存在等待、异步初始化、累计创建数小于初始化线程大小
                    if (emptyWait
                        && asyncInit && createCount < initialSize) {
                        emptyWait = false;
                    }
    
                    if (emptyWait) {
                        // 必须存在线程等待,才创建连接
                        if (poolingCount >= notEmptyWaitThreadCount //
                            && (!(keepAlive && activeCount + poolingCount < minIdle))
                            && !isFailContinuous()
                           ) {
                            //发送信号量,使线程挂起,暂不创建
                            empty.await();
                        }
    
                        // 防止创建超过maxActive数量的连接
                        if (activeCount + poolingCount >= maxActive) {
                            //发送信号量,使线程挂起,暂不创建
                            empty.await();
                            //跳出循环,重新判断,避免连接溢出
                            continue;
                        }
                    }
    
                } catch (InterruptedException e) {
                    //如果线程被打断,记录创建异常,异常发生时间
                    lastCreateError = e;
                    lastErrorTimeMillis = System.currentTimeMillis();
                    //如果连接池不处于关闭中或被关闭,打印异常日志
                    if ((!closing) && (!closed)) {
                        LOG.error("create connection Thread Interrupted, url: " + jdbcUrl, e);
                    }
                    //跳出循环,终止执行
                    break;
                } finally {
                    //解锁
                    lock.unlock();
                }
    
                PhysicalConnectionInfo connection = null;
    
                try {
                    //创建线程
                    connection = createPhysicalConnection();
                } catch (SQLException e) {
                    LOG.error("create connection SQLException, url: " + jdbcUrl + ", errorCode " + e.getErrorCode()
                              + ", state " + e.getSQLState(), e);
    
                    errorCount++;
                    if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {
                        // fail over retry attempts
                        setFailContinuous(true);
                        if (failFast) {
                            lock.lock();
                            try {
                                notEmpty.signalAll();
                            } finally {
                                lock.unlock();
                            }
                        }
    
                        if (breakAfterAcquireFailure) {
                            break;
                        }
    
                        try {
                            Thread.sleep(timeBetweenConnectErrorMillis);
                        } catch (InterruptedException interruptEx) {
                            break;
                        }
                    }
                } catch (RuntimeException e) {
                    LOG.error("create connection RuntimeException", e);
                    setFailContinuous(true);
                    continue;
                } catch (Error e) {
                    LOG.error("create connection Error", e);
                    setFailContinuous(true);
                    break;
                }
                //如果连接创建失败,重新创建
                if (connection == null) {
                    continue;
                }
                //置入连接池,如果置入连接池失败,则关闭当前连接
                boolean result = put(connection);
                if (!result) {
                    JdbcUtils.close(connection.getPhysicalConnection());
                    LOG.info("put physical connection to pool failed.");
                }
                //重置异常计数
                errorCount = 0; // reset errorCount
                //如果正在关闭中或已关闭,跳出循环
                if (closing || closed) {
                    break;
                }
            }
        }
    }
    
  • DruidDataSource#fill(int)

    fill()顾名思义,将填充连接池中的连接数,该方法分为无参fill()和有参fill(int toCount),无参方法将会调用有参函数,将线程池填充至最大。

    @Override
    public int fill(int toCount) throws SQLException {
        //如果线程池被关闭,抛出异常
        if (closed) {
            throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis));
        }
        //参数校验,填充线程数不应该小于0
        if (toCount < 0) {
            throw new IllegalArgumentException("toCount can't not be less than zero");
        }
        //初始化连接池
        init();
        //如果填充数>最大活跃数,填充数=最大活跃数
        if (toCount > this.maxActive) {
            toCount = this.maxActive;
        }
        //记录填充数
        int fillCount = 0;
        for (;;) {
            try {
                //可被打断锁
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                //如果被打断,记录连接异常数
                connectErrorCountUpdater.incrementAndGet(this);
                throw new SQLException("interrupt", e);
            }
            //判断当前连接池中连接是否可以被填充至指定大小
            boolean fillable = this.isFillable(toCount);
            //解锁
            lock.unlock();
            //如果不能被填充,跳出循环
            if (!fillable) {
                break;
            }
    
            DruidConnectionHolder holder;
            try {
                //创建连接
                PhysicalConnectionInfo pyConnInfo = createPhysicalConnection();
                //创建连接持有者
                holder = new DruidConnectionHolder(this, pyConnInfo);
            } catch (SQLException e) {
                LOG.error("fill connection error, url: " + this.jdbcUrl, e);
                //记录连接异常数
                connectErrorCountUpdater.incrementAndGet(this);
                throw e;
            }
    
            try {
                //可被中断锁
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                //记录连接异常数
                connectErrorCountUpdater.incrementAndGet(this);
                throw new SQLException("interrupt", e);
            }
    
            try {
                //如果不能被填充到指定数
                if (!this.isFillable(toCount)) {
                    //关闭当前连接
                    JdbcUtils.close(holder.getConnection());
                    LOG.info("fill connections skip.");
                    //跳出循环
                    break;
                }
                //将连接置入连接池,上次活跃时间为当前
                this.putLast(holder, System.currentTimeMillis());
                //填充数自增
                fillCount++;
            } finally {
                //解锁
                lock.unlock();
            }
        }
    
        if (LOG.isInfoEnabled()) {
            LOG.info("fill " + fillCount + " connections");
        }
        //返回填充数
        return fillCount;
    }
    

总结

Druid连接创建方式

Druid连接有两种创建方式,一种为同步(即方法内部同步创建),一种为异步(通过线程创建或线程池提交任务)。

在异步创建时,采用线程创建还是线程池提交任务由连接池是否指定线程池确定,在使用线程池创建时,以下两种条件创建会被直接取消

  • createTaskCount >= maxCreateTaskCount,即当前创建任务数大于最大任务数
  • activeCount + poolingCount + createTaskCount >= maxActive,即活跃总数+池中总数+创建中总数>=最大活跃数

Druid连接的创建时点

  • initialSize>0时,将在init()时进行创建,此时创建连接由是否指定asyncInit参数来判断,需要注意的时,此时如果采用异步创建需要设置线程池,否则即使开启asyncInit也依旧会采用同步创建。

  • 开启keepAlive并且initialSize<minIdle,将在init()时进行创建,此时将采用异步方式创建连接

  • 抛弃连接(discardConnection)且连接池中活跃连接总数<最小数量,此时采用异步方式创建连接

  • 连接池取出的连接发生异常且fatalErrorCount - fatalErrorCountLastShrink > onFatalErrorMaxActive时,此时采用异步方式创建

  • 从连接池中取连接时,连接池中没有空闲连接,此时将采用异步方式创建连接

  • 向连接池中置入连接时(要求此时为异步线程池方式创建),且满足poolingCount + createTaskCount < notEmptyWaitThreadCount && activeCount + poolingCount + createTaskCount < maxActive

  • 连接池收缩(shrink)后,将keepAliveConnections归还至池子中时存在校验异常的连接,并且连接池中活跃连接总数<最小数量,此时采用异步创建

  • 连接池收缩后,开启keepAlive且连接池中活跃连接总数<最小数量,此时将采用异步方式将连接池中连接填充至最小连接数

  • 连接池收缩后,未开启keepAlive或连接池中活跃连接总数>=最小数量,并且满足onFatalError || fatalErrorIncrement > 0,此时将采用异步方式创建连接

  • 获得连接时,满足设置了创建线程池、池中没有连接、活跃连接数<最大活跃数、没有创建中的连接、线程池是ScheduledThreadPoolExecutor或它的子类,且此时线程池中存在排队,同步创建

  • 在手动调用fill()方法时,采用同步方式创建

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值