前两天发现严重跑偏,发现大家都在梳理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); } } }
再看下流程图消化一下