druid 源码分析(6)

408 阅读2分钟

周末整理了一下init方法的整个流程,这里顺便贴一下在文章中,以免之后遗漏了。

未命名文件.png

然后我们继续来讨论源码:之前讲到过,创建连接是采用异步方式,进入到DruidDataSource.CreateConnectionThread.run()。当不需要创建连接时,该线程进入empty.await()状态,此时需要用户线程调用empty.signal()来唤醒。

    public void run() {
        // 用于唤醒初始化数据源的线程
        initedLatch.countDown();

        long lastDiscardCount = 0;
        
        // 注意,这里是死循环,当需要创建连接对象时,这个线程会受到signal,否则会一直await
        for (;;) {
            // 加锁
            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e2) {
                break;
            }
            // 丢弃数量discardCount
            long discardCount = DruidDataSource.this.discardCount;
            boolean discardChanged = discardCount - lastDiscardCount > 0;
            lastDiscardCount = discardCount;

            try {
                // 这个变量代表了是否有必要新增连接,true代表没必要
                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()
                    ) {
                        // 等待signal,前面已经讲到,当某线程需要创建连接时,会发送signal给它
                        empty.await();
                    }

                    // 防止创建超过maxActive数量的连接
                    if (activeCount + poolingCount >= maxActive) {
                        empty.await();
                        continue;
                    }
                }

            } catch (InterruptedException e) {
                lastCreateError = e;
                lastErrorTimeMillis = System.currentTimeMillis();
                break;
            } finally {
                // 解锁
                lock.unlock();
            }

            PhysicalConnectionInfo connection = null;

            try {
                // 创建原生的连接对象,并包装
                connection = createPhysicalConnection();
            } catch (SQLException e) {
                //出现SQLException会继续往下走
                //······
            } catch (RuntimeException e) {
                // 出现RuntimeException则重新进入循环体
                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;
            }
            // 将连接对象包装为DruidConnectionHolder,并放入connections数组中
            // 注意,该方法会去调用notEmpty.signal(),即会去唤醒正在等待获取连接的线程
            boolean result = put(connection);

        }
    }

testOnBorrow 和testWhileIdle参数的应用

        // 如果开启了testOnBorrow
        if (testOnBorrow) {
            // 这里会去调用validConnectionChecker的isValidConnection方法来校验,validConnectionChecker不存在的话,则以普通JDBC方式校验
            boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
            if (!validate) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("skip not validate connection.");
                }

                Connection realConnection = poolableConnection.conn;
                // 丢弃连接,丢弃完会发送signal给CreateConnectionThread来创建连接
                discardConnection(realConnection);
                continue;
            }
        } else {
            Connection realConnection = poolableConnection.conn;
            if (poolableConnection.conn.isClosed()) {
                discardConnection(null); // 传入null,避免重复关闭
                continue;
            }

            if (testWhileIdle) {
                final DruidConnectionHolder holder = poolableConnection.holder;
                // 当前时间
                long currentTimeMillis             = System.currentTimeMillis();
                // 最后活跃时间
                long lastActiveTimeMillis          = holder.lastActiveTimeMillis;
                long lastKeepTimeMillis            = holder.lastKeepTimeMillis;

                if (lastKeepTimeMillis > lastActiveTimeMillis) {
                    lastActiveTimeMillis = lastKeepTimeMillis;
                }
                // 计算连接对象空闲时长
                long idleMillis = currentTimeMillis - lastActiveTimeMillis;

                long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
                // 空闲检测周期
                if (timeBetweenEvictionRunsMillis <= 0) {
                    timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
                }
                // 当前连接空闲时长大于空间检测周期时,进入检测
                if (idleMillis >= timeBetweenEvictionRunsMillis
                        || idleMillis < 0 // unexcepted branch
                        ) {
                    // 接下来的逻辑和前面testOnBorrow一样的
                    boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                    if (!validate) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("skip not validate connection.");
                        }

                        discardConnection(realConnection);
                         continue;
                    }
                }
            }
        }

进入DruidDataSource.getConnectionDirect(),这里不会进行检测,只是将连接对象放入activeConnections,具体泄露连接的检测工作是在DestroyConnectionThread线程中进行

        if (removeAbandoned) {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            poolableConnection.connectStackTrace = stackTrace;
            // 记录连接借出时间
            poolableConnection.setConnectedTimeNano();
            poolableConnection.traceEnable = true;

            activeConnectionLock.lock();
            try {
                // 放入activeConnections
                activeConnections.put(poolableConnection, PRESENT);
            } finally {
                activeConnectionLock.unlock();
            }
        }

进入DruidDataSource.removeAbandoned(),当连接对象使用时间超过removeAbandonedTimeoutMillis,则会被丢弃掉。

    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);
                // 如果超过设置的丢弃超时时间,则加入abandonedList
                if (timeMillis >= removeAbandonedTimeoutMillis) {
                    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++;
            }
        }

        return removeCount;
    }