druid 连接池源码分析(2)

1,032 阅读2分钟

首先我们直接看位于pool里面的类getConnection,这是连接池的最初起点

public DruidPooledConnection getConnection() throws SQLException {
    return getConnection(maxWait);
}

public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
    init();

    if (filters.size() > 0) {
        FilterChainImpl filterChain = new FilterChainImpl(this);
        return filterChain.dataSource_connect(this, maxWaitMillis);
    } else {
        return getConnectionDirect(maxWaitMillis);
    }
}

获取maxWait这个等待时间的参数以后调用了init方法启动了连接池:

首先我们看到init方法使用了ReentrantLock 可重入锁来控制整个初始化方法 在初始化可重入锁的前后都进行了初始化inited的判断 然后init方法获取了 initStackTrace的信息并初始化了db url,jdbc 的类别等等其他参数

public void init() throws SQLException {
    if (inited) {
        return;
    }

    // bug fixed for dead lock, for issue #2980
    DruidDriver.getInstance();

    final ReentrantLock lock = this.lock;
    try {
        lock.lockInterruptibly();
    } catch (InterruptedException e) {
        throw new SQLException("interrupt", e);
    }

    boolean init = false;
    try {
        if (inited) {
            return;
        }

        initStackTrace = Utils.toString(Thread.currentThread().getStackTrace());

        this.id = DruidDriver.createDataSourceId();
        if (this.id > 1) {
            long delta = (this.id - 1) * 100000;
            this.connectionIdSeedUpdater.addAndGet(this, delta);
            this.statementIdSeedUpdater.addAndGet(this, delta);
            this.resultSetIdSeedUpdater.addAndGet(this, delta);
            this.transactionIdSeedUpdater.addAndGet(this, delta);
        }

        if (this.jdbcUrl != null) {
            this.jdbcUrl = this.jdbcUrl.trim();
            initFromWrapDriverUrl();
        }

        for (Filter filter : filters) {
            filter.init(this);
        }

        if (this.dbTypeName == null || this.dbTypeName.length() == 0) {
            this.dbTypeName = JdbcUtils.getDbType(jdbcUrl, null);
        }

        DbType dbType = DbType.of(this.dbTypeName);
        if (dbType == DbType.mysql
                || dbType == DbType.mariadb
                || dbType == DbType.oceanbase
                || dbType == DbType.ads) {
            boolean cacheServerConfigurationSet = false;
            if (this.connectProperties.containsKey("cacheServerConfiguration")) {
                cacheServerConfigurationSet = true;
            } else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) {
                cacheServerConfigurationSet = true;
            }
            if (cacheServerConfigurationSet) {
                this.connectProperties.put("cacheServerConfiguration", "true");
            }
        }

        if (maxActive <= 0) {
            throw new IllegalArgumentException("illegal maxActive " + maxActive);
        }

        if (maxActive < minIdle) {
            throw new IllegalArgumentException("illegal maxActive " + maxActive);
        }

        if (getInitialSize() > maxActive) {
            throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActive " + maxActive);
        }

        if (timeBetweenLogStatsMillis > 0 && useGlobalDataSourceStat) {
            throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGlobalDataSourceStat=true");
        }

        if (maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis) {
            throw new SQLException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis");
        }

        if (keepAlive && keepAliveBetweenTimeMillis <= timeBetweenEvictionRunsMillis) {
            throw new SQLException("keepAliveBetweenTimeMillis must be grater than timeBetweenEvictionRunsMillis");
        }

        if (this.driverClass != null) {
            this.driverClass = driverClass.trim();
        }

        initFromSPIServiceLoader();

        resolveDriver();

        initCheck();

        initExceptionSorter();
        initValidConnectionChecker();
        validationQueryCheck();

        if (isUseGlobalDataSourceStat()) {
            dataSourceStat = JdbcDataSourceStat.getGlobal();
            if (dataSourceStat == null) {
                dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbTypeName);
                JdbcDataSourceStat.setGlobal(dataSourceStat);
            }
            if (dataSourceStat.getDbType() == null) {
                dataSourceStat.setDbType(this.dbTypeName);
            }
        } else {
            dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbTypeName, this.connectProperties);
        }
        dataSourceStat.setResetStatEnable(this.resetStatEnable);

        connections = new DruidConnectionHolder[maxActive];
        evictConnections = new DruidConnectionHolder[maxActive];
        keepAliveConnections = new DruidConnectionHolder[maxActive];

        SQLException connectError = null;

        if (createScheduler != null && asyncInit) {
            for (int i = 0; i < initialSize; ++i) {
                submitCreateTask(true);
            }
        } else if (!asyncInit) {
            // init connections
            while (poolingCount < initialSize) {
                try {
                    PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
                    DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
                    connections[poolingCount++] = holder;
                } catch (SQLException ex) {
                    LOG.error("init datasource error, url: " + this.getUrl(), ex);
                    if (initExceptionThrow) {
                        connectError = ex;
                        break;
                    } else {
                        Thread.sleep(3000);
                    }
                }
            }

            if (poolingCount > 0) {
                poolingPeak = poolingCount;
                poolingPeakTime = System.currentTimeMillis();
            }
        }

        createAndLogThread();
        createAndStartCreatorThread();
        createAndStartDestroyThread();

        initedLatch.await();
        init = true;

        initedTime = new Date();
        registerMbean();

        if (connectError != null && poolingCount == 0) {
            throw connectError;
        }

        if (keepAlive) {
            // async fill to minIdle
            if (createScheduler != null) {
                for (int i = 0; i < minIdle; ++i) {
                    submitCreateTask(true);
                }
            } else {
                this.emptySignal();
            }
        }

    } catch (SQLException e) {
        LOG.error("{dataSource-" + this.getID() + "} init error", e);
        throw e;
    } catch (InterruptedException e) {
        throw new SQLException(e.getMessage(), e);
    } catch (RuntimeException e){
        LOG.error("{dataSource-" + this.getID() + "} init error", e);
        throw e;
    } catch (Error e){
        LOG.error("{dataSource-" + this.getID() + "} init error", e);
        throw e;

    } finally {
        inited = true;
        lock.unlock();

        if (init && LOG.isInfoEnabled()) {
            String msg = "{dataSource-" + this.getID();

            if (this.name != null && !this.name.isEmpty()) {
                msg += ",";
                msg += this.name;
            }

            msg += "} inited";

            LOG.info(msg);
        }
    }
}

其中还通过java spiServiceLoader(服务发现)初始化了Filter 类,这个filter类继承了基础的wraper 实现了整整600多个接口,这里就不一一展开分析这些接口了,这个spi方法非常好的解耦了整个 初始化方法和jdbc filter之前的关系,不需要关心具体的filter内部的实现。

/**
 * load filters from SPI ServiceLoader
 * 
 * @see ServiceLoader
 */
private void initFromSPIServiceLoader() {
    if (loadSpifilterSkip) {
        return;
    }

    if (autoFilters == null) {
        List<Filter> filters = new ArrayList<Filter>();
        ServiceLoader<Filter> autoFilterLoader = ServiceLoader.load(Filter.class);

        for (Filter filter : autoFilterLoader) {
            AutoLoad autoLoad = filter.getClass().getAnnotation(AutoLoad.class);
            if (autoLoad != null && autoLoad.value()) {
                filters.add(filter);
            }
        }
        autoFilters = filters;
    }

    for (Filter filter : autoFilters) {
        if (LOG.isInfoEnabled()) {
            LOG.info("load filter from spi :" + filter.getClass().getName());
        }
        addFilter(filter);
    }
}

之后实现了resolveDriver类,这个类里统一处理了各种数据库的驱动类,这个代码写的非常好,可以在平时复用他的处理方法。

protected void resolveDriver() throws SQLException {
    if (this.driver == null) {
        if (this.driverClass == null || this.driverClass.isEmpty()) {
            this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl);
        }

        if (MockDriver.class.getName().equals(driverClass)) {
            driver = MockDriver.instance;
        } else if ("com.alibaba.druid.support.clickhouse.BalancedClickhouseDriver".equals(driverClass)) {
            Properties info = new Properties();
            info.put("user", username);
            info.put("password", password);
            info.putAll(connectProperties);
            driver = new BalancedClickhouseDriver(jdbcUrl, info);
        } else {
            if (jdbcUrl == null && (driverClass == null || driverClass.length() == 0)) {
                throw new SQLException("url not set");
            }
            driver = JdbcUtils.createDriver(driverClassLoader, driverClass);
        }
    } else {
        if (this.driverClass == null) {
            this.driverClass = driver.getClass().getName();
        }
    }
}

然后是检查类,在检查类中根据不同的db类型分别做了初步的检查和处理

protected void initCheck() throws SQLException {
    DbType dbType = DbType.of(this.dbTypeName);

    if (dbType == DbType.oracle) {
        isOracle = true;

        if (driver.getMajorVersion() < 10) {
            throw new SQLException("not support oracle driver " + driver.getMajorVersion() + "."
                                   + driver.getMinorVersion());
        }

        if (driver.getMajorVersion() == 10 && isUseOracleImplicitCache()) {
            this.getConnectProperties().setProperty("oracle.jdbc.FreeMemoryOnEnterImplicitCache", "true");
        }

        oracleValidationQueryCheck();
    } else if (dbType == DbType.db2) {
        db2ValidationQueryCheck();
    } else if (dbType == DbType.mysql
            || JdbcUtils.MYSQL_DRIVER_6.equals(this.driverClass)) {
        isMySql = true;
    }

    if (removeAbandoned) {
        LOG.warn("removeAbandoned is true, not use in production.");
    }
}

之中很多我们略下不看 最后这里启动了创建的线程,并使用了一个计时器来计算线程的创建情况

CountDownLatch             initedLatch               = new CountDownLatch(2);

protected void createAndStartCreatorThread() {
    if (createScheduler == null) {
        String threadName = "Druid-ConnectionPool-Create-" + System.identityHashCode(this);
        createConnectionThread = new CreateConnectionThread(threadName);
        createConnectionThread.start();
        return;
    }

    initedLatch.countDown();
}