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()
方法时,采用同步方式创建