How to protect the connection validity when the druid connection pool is not set with a keep alive mechanism?
- If a fatal exception occurs in the connection, the keep alive connection array will be added, and then the validity will be verified;
- If the physical connection timeout is set and the idle time of the connection is greater than the set physical connection timeout, it will be added to the expulsion connection array;
- If the idle time of the connection is less than the minimum idle time and less than the keep alive inspection interval, continue to wait for the next round of inspection;
- If the idle time of the connection is greater than the minimum eviction idle time and the polling index is less than the merge counter, put the connection into the eviction connection array;
- If the idle time of the connection is greater than the maximum eviction idle time, put the connection into the eviction connection array;
If the keep alive mechanism is not enabled, the connection in the connection pool may not be true and effective. If the firewall closes the connection or other unknown scenarios close the connection, the connection may be invalid; That is, the validity of the connection will not be checked;
druid turns on the survival mechanism
If the keep alive mechanism is set and the idle time is greater than the keep alive check interval, put the connection into the keep alive connection array; Next, the validity of the connections in the keep alive connection array will be checked
After the keep alive mechanism is enabled, the connection validity of qualified connections in the connection pool will be checked to ensure the validity of the connection;
The startup sequence of druid keep alive mechanism daemon thread is as follows:
- com.alibaba.druid.pool.DruidDataSource#init
- com.alibaba.druid.pool.DruidDataSource#createAndStartDestroyThread
- com.alibaba.druid.pool.DruidDataSource.DestroyConnectionThread
- com.alibaba.druid.pool.DruidDataSource.DestroyTask
- com.alibaba.druid.pool.DruidDataSource#shrink(boolean, boolean)
DestroyConnectionThread daemon:
public class DestroyConnectionThread extends Thread { public DestroyConnectionThread(String name){ super(name); this.setDaemon(true); } public void run() { initedLatch.countDown(); for (;;) { // Delete from front try { if (closed || closing) { break; } //Timebetween evictionrunsmillis is the interval between triggering heartbeat. If it is greater than 0, it will enter sleep time, //The default is 1 minute, and a heartbeat check is performed once a minute if (timeBetweenEvictionRunsMillis > 0) { Thread.sleep(timeBetweenEvictionRunsMillis); } else { Thread.sleep(1000); // } if (Thread.interrupted()) { break; } destroyTask.run(); } catch (InterruptedException e) { break; } } } }
After the program is started, the daemon thread will enter the wireless loop and call the DestoryTask thread according to the time between eviction runs millis loop;
DestoryTask thread source code:
public class DestroyTask implements Runnable { public DestroyTask() { } @Override public void run() { shrink(true, keepAlive); if (isRemoveAbandoned()) { removeAbandoned(); } } }
shirnk method core code
public void shrink(boolean checkTime, boolean keepAlive) { try { lock.lockInterruptibly(); } catch (InterruptedException e) { return; } boolean needFill = false; //Expulsion counter int evictCount = 0; //Keep alive counter int keepAliveCount = 0; int fatalErrorIncrement = fatalErrorCount - fatalErrorCountLastShrink; fatalErrorCountLastShrink = fatalErrorCount; try { if (!inited) { return; } //Merge counter = number of connections stored in the database connection pool - minimum number of free connections () final int checkCount = poolingCount - minIdle; final long currentTimeMillis = System.currentTimeMillis(); for (int i = 0; i < poolingCount; ++i) { //Remove the connection handle from the database connection pool DruidConnectionHolder connection = connections[i]; //(if fatal errors occur or the number of fatal errors is greater than 0) & & the last fatal error time is greater than the connection time of the current connection //Then add the connection handle to the keepAliveConnections connection handle array and exit this cycle if ((onFatalError || fatalErrorIncrement > 0) && (lastFatalErrorTimeMillis > connection.connectTimeMillis)) { //Logically, it is determined as a live connection keepAliveConnections[keepAliveCount++] = connection; continue; } //Upper access true if (checkTime) { //If the physical connection timeout parameter is set and greater than 0 (default: - 1) if (phyTimeoutMillis > 0) { //Gets the length of time the connection was established long phyConnectTimeMillis = currentTimeMillis - connection.connectTimeMillis; //If the physical connection duration established by the current connection is longer than the physical connection duration set by the parameter, the connection will be added to the evictConnections array if (phyConnectTimeMillis > phyTimeoutMillis) { evictConnections[evictCount++] = connection; continue; } } //Current connection idle time long idleMillis = currentTimeMillis - connection.lastActiveTimeMillis; //The idle time is less than the minimum idle time and the idle time is less than the live check interval //Keep alive verification is not performed if (idleMillis < minEvictableIdleTimeMillis && idleMillis < keepAliveBetweenTimeMillis ) { break; } //Idle time is greater than or equal to the minimum idle time //If the current index is less than the counter, it is added to the eviction connection array if (idleMillis >= minEvictableIdleTimeMillis) { //If the current index is smaller than the merge counter, it is added to the eviction array if (checkTime && i < checkCount) { evictConnections[evictCount++] = connection; continue; //If the idle time is greater than the maximum eviction idle time, the eviction connection array is added } else if (idleMillis > maxEvictableIdleTimeMillis) { evictConnections[evictCount++] = connection; continue; } } //This is the switch to turn on the keep alive mechanism //If the keep alive mechanism is enabled and the idle time is greater than or equal to the keep alive interval, add the live connection array if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis) { keepAliveConnections[keepAliveCount++] = connection; } } else { //If the index is less than the minimum number of discards, it is added to the eviction connection array if (i < checkCount) { evictConnections[evictCount++] = connection; } else { break; } } } //Merge counter = expulsion counter + keep alive counter int removeCount = evictCount + keepAliveCount; //If the merge counter is greater than 0 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(); } //Expulsion counter is greater than 0 //Close the connection in the array 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); } //Keep alive counter greater than 0 //Check the availability of the connection. If it is valid, rejoin the queue. Otherwise, close the connection 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 { //Verify the validity of the connection this.validateConnection(connection); validate = true; } catch (Throwable error) { if (LOG.isDebugEnabled()) { LOG.debug("keepAliveErr", error); } // skip } //Join connection pool if valid boolean discard = !validate; if (validate) { holer.lastKeepTimeMillis = System.currentTimeMillis(); boolean putOk = put(holer, 0L, true); if (!putOk) { discard = true; } } //If not, close the connection 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(); } } }
GitHub address: https://github.com/mingyang66/spring-parent