前言

TomcatJdbc数据库衔接池开始是设计来用于替代Apache Commons DBCP,但现在TomcatJdbc数据库衔接池更多是与DruidHikariCP数据库衔接池进行比较,尽管扩展性不及Druid,功能不及HikariCP,但TomcatJdbc数据库衔接池仍旧有较为广泛的运用,这首要得益于其简略的装备和简洁的代码完成。

本文将对TomcatJdbc数据库衔接池的初始化源码进行分析,Tomcat版别为9.0.82

正文

TomcatJdbc数据库衔接池的初始化,发生在第一次获取数据库衔接时,即第一次调用DataSourceProxy#getConnection办法的时分,如下所示。

public Connection getConnection() throws SQLException {
    // 一开始pool为null
    if (pool == null) {
        // 调用createPool()办法创立衔接池
        return createPool().getConnection();
    }
    return pool.getConnection();
}
public ConnectionPool createPool() throws SQLException {
    if (pool != null) {
        return pool;
    } else {
        // 调用pCreatePool()办法创立衔接池
        return pCreatePool();
    }
}
private synchronized ConnectionPool pCreatePool() throws SQLException {
    if (pool != null) {
        return pool;
    } else {
        // 调用数据库衔接池的结构函数
        // 初始化逻辑在结构函数中
        pool = new ConnectionPool(poolProperties);
        return pool;
    }
}
public ConnectionPool(PoolConfiguration prop) throws SQLException {
    // 调用init()办法完成初始化
    init(prop);
}

在初次获取数据库衔接时,因为ConnectionPoolnull,此时会new一个ConnectionPool作为数据库衔接池并调用其init() 办法完成初始化,下面看一下init() 办法的完成。

protected void init(PoolConfiguration properties) throws SQLException {
    // 这儿的properties实践是PoolProperties
    // 用户的数据库衔接池装备会加载到properties中
    // 没有装备的项就运用PoolProperties中的默许值
    poolProperties = properties;
    // 做一下数据库衔接池装备的惯例校验
    // 例如maxIdle不能小于minIdle
    checkPoolConfiguration(properties);
    // busy行列保存借出的正在运用的衔接
    busy = new LinkedBlockingQueue<>();
    if (properties.isFairQueue()) {
        // idle行列保存可用的空闲衔接
        // 默许装备下会运用公正行列
        // 越先等候的线程越先获取到衔接
        idle = new FairBlockingQueue<>();
    } else {
        idle = new LinkedBlockingQueue<>();
    }
    // 初始化衔接池整理器,实践是启动一个守时使命
    // 每隔TimeBetweenEvictionRunsMillis运转一次
    initializePoolCleaner(properties);
    if (this.getPoolProperties().isJmxEnabled()) {
        createMBean();
    }
    // 解析得到并初始化拦截器
    ......
    // 依据装备的初始衔接数initialSize来预热衔接池
    // 若未装备initialSize则默许取值为10
    PooledConnection[] initialPool = new PooledConnection[poolProperties.getInitialSize()];
    try {
        for (int i = 0; i < initialPool.length; i++) {
            // 这儿创立衔接时,传入的用户名和暗码为null不影响衔接创立
            // 创立出来的衔接会先暂时存放在initialPool数组
            initialPool[i] = this.borrowConnection(0, null, null);
        }
    } catch (SQLException x) {
        log.error("Unable to create initial connections of pool.", x);
        if (!poolProperties.isIgnoreExceptionOnPreLoad()) {
            if (jmxPool!=null) {
                jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_INIT, getStackTrace(x));
            }
            close(true);
            throw x;
        }
    } finally {
        for (int i = 0; i < initialPool.length; i++) {
            if (initialPool[i] != null) {
                try {
                    // 将预热出来的衔接放到idle行列中
                    this.returnConnection(initialPool[i]);
                } catch(Exception x) {
                }
            }
        }
    }
    // 将衔接池关闭标识置为false
    closed = false;
}

总结

通过init() 办法,咱们可以总结如下几点。

  1. 衔接池的默许装备在PoolProperties中。TomcatJdbc数据库衔接池会读取用户的装备到PoolProperties中,如果有用户没有装备的项,则会运用PoolProperties预置的默许值,所以检查TomcatJdbc数据库衔接池装备的默许值,可以在PoolProperties中检查;
  2. 衔接池的衔接存放在busyidle行列中。busy行列保存借出的正在运用的衔接,idle行列保存可用的空闲衔接,默许情况下,idle行列运用的是公正行列FairBlockingQueue,以保证最先等候获取衔接的线程能最先获取到衔接;
  3. 衔接池在初始化时会创立衔接池整理器PoolCleaner并启动。PoolCleaner本质是一个守时使命,每距离timeBetweenEvictionRunsMillis运转一次,每次运转会执行衔接走漏检测可用衔接检测保活idle行列巨细调整
  4. 衔接池会在初始化时预热。预热即预先创立指定数量的衔接出来并放在idle行列中,预热衔接数量通过initialSize指定,不指守时默许为10。