druid的keepalive机制源码解析
作者:codecraft
这篇文章主要为大家介绍了druid的keepalive机制源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
DruidDataSource
public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource, MBeanRegistration { private int keepAliveCheckCount = 0; private DruidConnectionHolder[] keepAliveConnections; private volatile boolean keepAlive = false; // from DruidAbstractDataSource protected volatile long keepAliveBetweenTimeMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS * 2; public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = 60 * 1000L; public void init() throws SQLException { //...... if (keepAlive) { // async fill to minIdle if (createScheduler != null) { for (int i = 0; i < minIdle; ++i) { submitCreateTask(true); } } else { this.emptySignal(); } } //...... } }
DruidDataSource的init方法在keepAlive的时候触发创建连接,当createScheduler不为null时(默认为null
)执行submitCreateTask,否则执行emptySignal
submitCreateTask
com/alibaba/druid/pool/DruidDataSource.java
private void submitCreateTask(boolean initTask) { createTaskCount++; CreateConnectionTask task = new CreateConnectionTask(initTask); if (createTasks == null) { createTasks = new long[8]; } boolean putted = false; for (int i = 0; i < createTasks.length; ++i) { if (createTasks[i] == 0) { createTasks[i] = task.taskId; putted = true; break; } } if (!putted) { long[] array = new long[createTasks.length * 3 / 2]; System.arraycopy(createTasks, 0, array, 0, createTasks.length); array[createTasks.length] = task.taskId; createTasks = array; } this.createSchedulerFuture = createScheduler.submit(task); }
submitCreateTask创建CreateConnectionTask,然后提交到createScheduler
CreateConnectionTask
com/alibaba/druid/pool/DruidDataSource.java
public class CreateConnectionTask implements Runnable { private int errorCount; private boolean initTask; 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; 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++; 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 (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) { lock.lock(); try { clearCreateTask(taskId); } finally { lock.unlock(); } LOG.error("create connection Error", e); // unknow fatal exception setFailContinuous(true); break; } catch (Throwable e) { lock.lock(); try { clearCreateTask(taskId); } finally { lock.unlock(); } LOG.error("create connection unexecpted error.", e); break; } if (physicalConnection == null) { continue; } physicalConnection.createTaskId = taskId; boolean result = put(physicalConnection); if (!result) { JdbcUtils.close(physicalConnection.getPhysicalConnection()); LOG.info("put physical connection to pool failed."); } break; } } }
CreateConnectionTask主要是创建physicalConnection,然后放到connections中。在emptyWait为true的时候会根据条件执行empty.await()
CreateConnectionThread
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; } } } }
CreateConnectionThread的逻辑与CreateConnectionTask有点类似,有不少重复的代码,不像是同一个人写的;CreateConnectionThread是在DruidDataSource的init方法中触发createAndStartCreatorThread执行的,看只执行一次
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 { 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; } 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(); } 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(); 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(); } } }
DestroyConnectionThread就是每隔timeBetweenEvictionRunsMillis执行一下destroyTask,而DestroyTask的run方法主要是执行shrink(true, keepAlive)
shrink方法会根据poolingCount遍历connections,在checkTime为true时会根据idleMillis判断是否需要evict,否则判断是否需要keepalive(keepAlive && idleMillis >= keepAliveBetweenTimeMillis
),需要的话放入keepAliveConnections中,然后遍历进行validateConnection,如果成功则更新lastKeepTimeMillis,否则执行connection.close(),最后清空keepAliveConnections数组
小结
DestroyConnectionThread就是每隔timeBetweenEvictionRunsMillis执行一下destroyTask,而DestroyTask的run方法主要是执行shrink(true, keepAlive);该方法处理了evict及keepalive的逻辑,根据poolingCount遍历connections,在checkTime为true时会根据idleMillis判断是否需要evict,否则判断是否需要keepalive(keepAlive && idleMillis >= keepAliveBetweenTimeMillis
),需要的话放入keepAliveConnections中,然后遍历进行validateConnection,如果成功则更新lastKeepTimeMillis,否则执行connection.close(),最后清空keepAliveConnections数组。
jedis的keepalive是直接设置socket.setKeepAlive(true),而common-pools则没有所谓的keepalive,本质上druid的keepalive与common-pools的testWhileIdle类似;只不过druid直接在getConnection的时候执行testWhileIdle,这个逻辑有点奇怪,如果移除掉,而在shrink方法里头的keepAlive逻辑删除keepAliveBetweenTimeMillis判断,那么就跟common-pools的testWhileIdle的逻辑一致了。druid的keepalive相当于带了keepAliveBetweenTimeMillis的testWhileIdle。
以上就是druid的keepalive机制源码解析的详细内容,更多关于druid keepalive机制的资料请关注脚本之家其它相关文章!