druid database connection pool keep alive mechanism source code

Posted by crob611 on Mon, 21 Feb 2022 13:13:29 +0100

How to protect the connection validity when the druid connection pool is not set with a keep alive mechanism?
  1. If a fatal exception occurs in the connection, the keep alive connection array will be added, and then the validity will be verified;
  2. 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;
  3. 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;
  4. 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;
  5. 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:
  1. com.alibaba.druid.pool.DruidDataSource#init
  2. com.alibaba.druid.pool.DruidDataSource#createAndStartDestroyThread
  3. com.alibaba.druid.pool.DruidDataSource.DestroyConnectionThread
  4. com.alibaba.druid.pool.DruidDataSource.DestroyTask
  5. 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

Topics: Druid