druid 连接池源码分析(3)

778 阅读2分钟

继续昨天的分析 init()方法初始化的进程: 接下来进行的是可用性连接检查

这个接口帮助druid应用程序轻松快速地确定java.sql.Connection对象的可用性,并识别不应再用于连接池的连接对象。 类似


public class OracleValidConnectionChecker extends ValidConnectionCheckerAdapter implements ValidConnectionChecker, Serializable 

继承了公用的ValidConnectionChecker方法, isValid()方法可用于检查连接,也可与重试机制结合使用,例如响应快速连接故障转移处理的重试机制。

private void initValidConnectionChecker() {
    if (this.validConnectionChecker != null) {
        return;
    }

    String realDriverClassName = driver.getClass().getName();
    if (JdbcUtils.isMySqlDriver(realDriverClassName)) {
        this.validConnectionChecker = new MySqlValidConnectionChecker();

    } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER)
            || realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER2)) {
        this.validConnectionChecker = new OracleValidConnectionChecker();

    } else if (realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER)
               || realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_SQLJDBC4)
               || realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_JTDS)) {
        this.validConnectionChecker = new MSSQLValidConnectionChecker();

    } else if (realDriverClassName.equals(JdbcConstants.POSTGRESQL_DRIVER)
            || realDriverClassName.equals(JdbcConstants.ENTERPRISEDB_DRIVER)
            || realDriverClassName.equals(JdbcConstants.POLARDB_DRIVER)) {
        this.validConnectionChecker = new PGValidConnectionChecker();
    }
}

最后是validationQueryCheck方法, validationQuery是用来验证数据库连接的查询语句,这个查询语句必须是至少返回一条数据的SELECT语句。每种数据库都有各自的验证语句。这也是用来校验数据库链接的

private void validationQueryCheck() {
    if (!(testOnBorrow || testOnReturn || testWhileIdle)) {
        return;
    }

    if (this.validConnectionChecker != null) {
        return;
    }

    if (this.validationQuery != null && this.validationQuery.length() > 0) {
        return;
    }

    String errorMessage = "";

    if (testOnBorrow) {
        errorMessage += "testOnBorrow is true, ";
    }

    if (testOnReturn) {
        errorMessage += "testOnReturn is true, ";
    }

    if (testWhileIdle) {
        errorMessage += "testWhileIdle is true, ";
    }

    LOG.error(errorMessage + "validationQuery not set");
}

接下来是支持配置公用监控数据的配置,配置参数为useGloalDataSourceStat,通过参数来初始化公共监控的部分

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);
}

接下来就是具体的初始化部分了 :

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);
    }
}

druid支持两种方式新增连接,一种是通过开启不同的守护线程通过await、signal通信实现(默认的方式),另一种是直接通过线程池异步新增,这个方式通过在初始化druid时传入asyncInit=true,再把一个线程池对象赋值给createScheduler,就成功启用了这种模式, 首先我们看这里createScheduler传入,asyncInit参数为true,直接调用submitCreaterTask方法创建了连接。

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();
    }
}

上面的方法首先计算了poolingCount大小还没有到达initialSize-初始化的最大线程数 然后就调用了createPhysicalConnection方法

public PhysicalConnectionInfo createPhysicalConnection() throws SQLException {
    String url = this.getUrl();
    Properties connectProperties = getConnectProperties();

    String user;
    if (getUserCallback() != null) {
        user = getUserCallback().getName();
    } else {
        user = getUsername();
    }

    String password = getPassword();
    PasswordCallback passwordCallback = getPasswordCallback();

    if (passwordCallback != null) {
        if (passwordCallback instanceof DruidPasswordCallback) {
            DruidPasswordCallback druidPasswordCallback = (DruidPasswordCallback) passwordCallback;

            druidPasswordCallback.setUrl(url);
            druidPasswordCallback.setProperties(connectProperties);
        }

        char[] chars = passwordCallback.getPassword();
        if (chars != null) {
            password = new String(chars);
        }
    }

    Properties physicalConnectProperties = new Properties();
    if (connectProperties != null) {
        physicalConnectProperties.putAll(connectProperties);
    }

createPhysicalConnection方法中就是真实创建连接的部分方法,该方法太长这里只贴入部分,方法会返回一个

return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos, variables, globalVariables);

info类中含有刚刚初始化的大部分参数。

总结: 今天也分析了getconnection 方法中的部分方法实现,连接的处理,这几天的讨论过程非常的没有条例,属于看到哪算哪,明天会改变方式,先做图理清楚流程再重新从图出发分析代码