前两天发现严重跑偏,发现大家都在梳理druid的pool的关键类,druiddatasource,init流程啊,getconnection里是如何执行的呢。
感觉模模糊糊懂个大概,今天我也来研究下
DruidDataSource连接池的初始化//线程池初始化,在starter方法或者其他初始化方式
public void init() throws SQLException {
//已经在初始化就不在进行初始化了。
if (inited) {
return;
}
//驱动加载,可以看看githug上面的bug说明。
// 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());
//定义了几个原子数据,具体作用未知,每个dataSource都会增加一个
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);
}
//spring环境下properties参数在加载DruidDataSourceAutoConfigure的时候加载DataSourceProperties ,DruidStatProperties的时候加载进去的。
if (this.jdbcUrl != null) {
this.jdbcUrl = this.jdbcUrl.trim();
//初始化包装方式的url,比如:jdbc:wrap-jdbc:filters=:name=driverWrapperTest:jdbc:derby:memory:driverWrapperTestDB;create=true
initFromWrapDriverUrl();
}
//为配置的filter初始化
//这里面主要是做配置的初始化(合并SQL、慢查询相关)
//如果我们没有做相关配置相当于这里面什么都没做
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;
//spring环境下connectProperties由DruidDataSourceAutoConfigure加载完成
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);
}
//timeBetweenLogStatsMillis 将监控数据发送到文件中的间隔时间
if (timeBetweenLogStatsMillis > 0 && useGlobalDataSourceStat) {
throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGlobalDataSourceStat=true");
}
if (maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis) {
throw new SQLException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis");
}
//keepAlive 是否保活
//keepAliveBetweenTimeMillis 保活检查间隔时间
//timeBetweenEvictionRunsMillis 检测需要关闭的空闲连接的间隔时间,单位是毫秒
if (keepAlive && keepAliveBetweenTimeMillis <= timeBetweenEvictionRunsMillis) {
throw new SQLException("keepAliveBetweenTimeMillis must be grater than timeBetweenEvictionRunsMillis");
}
if (this.driverClass != null) {
this.driverClass = driverClass.trim();
}
//从spi加载filter,会和druid的初始化filters进行去重
initFromSPIServiceLoader();
//加载驱动类(里面有四种不同的创建驱动的类加载方式,可以参考下),支持mock类型的驱动名称
resolveDriver();
//oracle和db2的连接检查
initCheck();
//当数据库抛出一些不可恢复的异常时,抛弃连接
initExceptionSorter();
//初始化检测数据库连接是否有效的方法 (设置 usePingMethod, 默认是true)
//1. usePingMethod=true 使用ping方式检测连接有效性 (validationQuery 这个值在true的情况下就没有用了),
//2. usePingMethod=false, 使用 validationQuery 检测有效性,如果配置select 1 )
initValidConnectionChecker();
//检测有无验证连接的参数,仅仅会记录日志,不会报错
validationQueryCheck();
//暂不清楚这个作用是什么,druid.globalDbType的功能是什么?
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 {
//创建真正的连接 Connect
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
connections[poolingCount++] = holder; //生成好的连接直接往后排
} catch (SQLException ex) {
LOG.error("init datasource error, url: " + this.getUrl(), ex);
// initExceptionThrow
if (initExceptionThrow) {
connectError = ex;
break;
} else {
Thread.sleep(3000);
}
}
}
if (poolingCount > 0) {
poolingPeak = poolingCount;
poolingPeakTime = System.currentTimeMillis();
}
} createAndLogThread();//创建并记录连接池信息到日志 createAndStartCreatorThread();//开启创建连接的守护线程 createAndStartDestroyThread();//开启负责丢弃连接的守护线程 //阻塞上面线程的执行,在createAndStartCreatorThread 和 createAndStartDestroyThread 分别调用了 initedLatch.countDown(),计数器正好为0 initedLatch.await(); init = true;
initedTime = new Date();
registerMbean();//把dataSource加入到stat中
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);
}
}
}
再看下流程图消化一下



