写点什么

数据库连接池 -Druid 源码学习(一)

作者:wjchenge
  • 2022 年 5 月 11 日
  • 本文字数:2838 字

    阅读完需:约 9 分钟

1、简介

在本文中,我们将了解 com.alibaba.druid.pool.DruidDataSource#init 方法的执行流程。

2、初始化流程

1、利用双重检查锁(Double-Check Locking)保证只初始化一次

				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; }
复制代码

2、获取当前代码执行的堆栈信息

initStackTrace = Utils.toString(Thread.currentThread().getStackTrace());
复制代码

3、解析包装代理的数据库连接,添加用户配置的过滤器

if (this.jdbcUrl != null) {  this.jdbcUrl = this.jdbcUrl.trim();  initFromWrapDriverUrl();}
复制代码

4、执行过滤器的初始化方法

for (Filter filter : filters) {  filter.init(this);}
复制代码

5、参数校验和赋值

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(); }
复制代码

6、初始化部分参数

// 通过spi机制加载用户自定义的过滤器initFromSPIServiceLoader();// 解析兼容部分特殊驱动resolveDriver();// oracle、db2、mysql特殊检查处理initCheck();// 初始化ExceptionSorter,initExceptionSorter();// 初始化ValidConnectionCheckerinitValidConnectionChecker();// 检查validationQuery参数配置是否完整validationQueryCheck();
复制代码

7、是否使用全局数据源统计

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);
复制代码

8、初始化连接池

// 保存正常的连接,获取的数据库连接就是从这个数组获取connections = new DruidConnectionHolder[maxActive];// 保存需要丢弃的连接evictConnections = new DruidConnectionHolder[maxActive];// 保存需要进行keepAlive检查的连接keepAliveConnections = new DruidConnectionHolder[maxActive];
复制代码

9、连接池初始化连接

// 异步初始化连接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);      }    }  }
复制代码

10、开启三个守护线程

// 打印线程池配置信息createAndLogThread();// 线程池扩容createAndStartCreatorThread();// 线程池缩容createAndStartDestroyThread();
复制代码

11、确保最小空闲连接

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

12、最后设置初始状态为完成,并释放锁,打印初始化情况日志

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);}
复制代码


用户头像

wjchenge

关注

还未添加个人签名 2018.07.27 加入

还未添加个人简介

评论

发布
暂无评论
数据库连接池-Druid 源码学习(一)_初始化_wjchenge_InfoQ写作社区