Bootstrap

Druid源码分析_08_DestroyThread扫描回收连接

销毁线程的创建

在DruidDataSource创建后,调用init()方法进行连接池初始化的时候.会创建一个销毁线程.

方法分析

createAndStartDestroyThread();

createAndStartDestroyThread

通过设置destroyScheduler来区分创建方式,一种是通过定时任务机制去执行配置的destroyTask,另外中是直接创建默认销毁线程任务

    // 间隔为 timeBetweenEvictionRunsMillis
    protected void createAndStartDestroyThread() {
        // 创建destory任务
        destroyTask = new DestroyTask();
    
        // 如果设置了destroyScheduler
        if (destroyScheduler != null) {
            //间隔时间 = timeBetweenEvictionRunsMillis = 默认60000ms = 60s
            long period = timeBetweenEvictionRunsMillis;
            if (period <= 0) {
                // 如果1000ms = 1s
                period = 1000;
            }
            // 创建一个间隔为period,并且延迟period启动的任务
            destroySchedulerFuture = destroyScheduler.scheduleAtFixedRate(destroyTask, period, period,
                    TimeUnit.MILLISECONDS);
            // 外层代码等待此处创建完成扣减栅栏数
            initedLatch.countDown();
            return;
        }
    
        String threadName = "Druid-ConnectionPool-Destroy-" + System.identityHashCode(this);
        // 启动线程
        destroyConnectionThread = new DestroyConnectionThread(threadName);
        destroyConnectionThread.start();
      }
    

DestroyConnectionThread

线程里睡眠60s,并且调用了DestoryTask

 public class DestroyConnectionThread extends Thread {
    
      public DestroyConnectionThread(String name) {
          super(name);
          this.setDaemon(true);
      }
    
      public void run() {
          initedLatch.countDown();
          System.out.println("DestroyConnectionThread initedLatch countDown " + initedLatch.getCount());
    
          for (; ; ) {
              // 从前面开始删除
              try {
                  if (closed || closing) {
                      break;
                  }
    
                  if (timeBetweenEvictionRunsMillis > 0) {
                      Thread.sleep(timeBetweenEvictionRunsMillis);
                  } else {
                      Thread.sleep(1000); //
                  }
    
                  if (Thread.interrupted()) {
                      break;
                  }
    
                  destroyTask.run();
              } catch (InterruptedException e) {
                  break;
              }
          }
      }
    
        }
    

DestroyTask

真正回收连接的任务.

 public class DestroyTask implements Runnable {
      public DestroyTask() {
    
      }
    
      @Override
      public void run() {
          shrink(true, keepAlive);
    
          // 是否开启了强制回收连接
          if (isRemoveAbandoned()) {
              removeAbandoned();
          }
      }
    
    }
    

shrink

判断连接池中对象是否存活,如果不满足则剔除.

/**
     *
     * @param checkTime
     * @param keepAlive
     * checkTime = true
     */
    public void shrink(boolean checkTime, boolean keepAlive) {
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            return;
        }
    
        // 是否要填充
        boolean needFill = false;
        // 踢出数量
        int evictCount = 0;
        // 还活着几个,每次机选poolingCount
        int keepAliveCount = 0;
        // 错误计数器
        int fatalErrorIncrement = fatalErrorCount - fatalErrorCountLastShrink;
        fatalErrorCountLastShrink = fatalErrorCount;
    
        try {
            // 初始化完成才做,没创建好的连接池有什么好销毁线程的呢
            if (!inited) {
                return;
            }
    
            // 检查的数量,池子里至少要保持有minIdle个连接,不能全干掉
            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;
    
                    // minEvictableIdleTimeMillis = 默认1800s 30分钟
                    if (idleMillis < minEvictableIdleTimeMillis
                            // keepAliveBetweenTimeMillis 默认2分钟
                            && idleMillis < keepAliveBetweenTimeMillis
                    ) {
                        break;
                    }
    
                    // 对空闲的连接进行回收 minEvictableIdleTimeMillis 默认30分钟
                    if (idleMillis >= minEvictableIdleTimeMillis) {
                        if (checkTime && i < checkCount) {
                            // 加入到剔除
                            evictConnections[evictCount++] = connection;
                            continue;
                            // 对空闲时间超过最大容忍的时间
                        } else if (idleMillis > maxEvictableIdleTimeMillis) {
                            // 加入到剔除
                            evictConnections[evictCount++] = connection;
                            continue;
                        }
                    }
    
                    // 开启keepAlive 并且 空闲时间 大于等于 2分钟
                    if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis) {
                        // keepAliveConnections也是要被剔除的连接 指的是开启keepAlive时要被剔除的数据结构
                        keepAliveConnections[keepAliveCount++] = connection;
                    }
                } else {
                    // 如果checkTime =false 一般为true,那么当前连接小于检查的总数,直接剔除
                    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);
            }
            // 赋值为null 就是删除了 等gc回收
            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);
        }
    
        // 如果剔除后的连接池里的链接数小于了minIdle 补上
        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();
            }
        }
    }
    

强制回收策略

/**
         * 强制回收
         * @return
    */
    public int removeAbandoned() {
        int removeCount = 0;
    
        long currrentNanos = System.nanoTime();
    
        List<DruidPooledConnection> abandonedList = new ArrayList<DruidPooledConnection>();
    
        // 锁住当前活跃的连接
        activeConnectionLock.lock();
        try {
            Iterator<DruidPooledConnection> iter = activeConnections.keySet().iterator();
    
            // 依次获取
            for (; iter.hasNext(); ) {
                DruidPooledConnection pooledConnection = iter.next();
    
                if (pooledConnection.isRunning()) {
                    continue;
                }
    
                // 当前时间 减去 连接时间
                long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);
    
                // 如果大于了5分钟
                if (timeMillis >= removeAbandonedTimeoutMillis) {
                    // 从activeConnections移除
                    iter.remove();
                    pooledConnection.setTraceEnable(false);
                    // 加入回收列表
                    abandonedList.add(pooledConnection);
                }
            }
        } finally {
            activeConnectionLock.unlock();
        }
    
        // 强制回收的列表里有数据
        if (abandonedList.size() > 0) {
            for (DruidPooledConnection pooledConnection : abandonedList) {
                final ReentrantLock lock = pooledConnection.lock;
                lock.lock();
                try {
                    if (pooledConnection.isDisable()) {
                        continue;
                    }
                } finally {
                    lock.unlock();
                }
    
                JdbcUtils.close(pooledConnection);
                pooledConnection.abandond();
                removeAbandonedCount++;
                removeCount++;
    
                // 如果配置了打印强制回收的日志,一般配置了强制回收肯定也要配置true
                if (isLogAbandoned()) {
                    StringBuilder buf = new StringBuilder();
                    buf.append("abandon connection, owner thread: ");
                    buf.append(pooledConnection.getOwnerThread().getName());
                    buf.append(", connected at : ");
                    buf.append(pooledConnection.getConnectedTimeMillis());
                    buf.append(", open stackTrace\n");
    
                    StackTraceElement[] trace = pooledConnection.getConnectStackTrace();
                    for (int i = 0; i < trace.length; i++) {
                        buf.append("\tat ");
                        buf.append(trace[i].toString());
                        buf.append("\n");
                    }
    
                    buf.append("ownerThread current state is " + pooledConnection.getOwnerThread().getState()
                            + ", current stackTrace\n");
                    trace = pooledConnection.getOwnerThread().getStackTrace();
                    for (int i = 0; i < trace.length; i++) {
                        buf.append("\tat ");
                        buf.append(trace[i].toString());
                        buf.append("\n");
                    }
    
                    LOG.error(buf.toString());
                }
            }
        }
    
        return removeCount;
    }
    
;