写点什么

druid 源码学习一

作者:Nick
  • 2022 年 5 月 11 日
  • 本文字数:4433 字

    阅读完需:约 15 分钟

druid源码学习一

一、初识 druid

1.1 Druid 的简单认知

Druid 是阿里巴巴开发的号称为监控而生的数据库连接池,

是 Java 语言中最好的数据库连接池。Druid 能够提供强大的监控和扩展功能。

在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,

可以很好的监控 DB 池连接和 SQL 的执行情况。

1.2 Druid 可以做什么?

1) 可以监控数据库访问性能,Druid 内置提供了一个功能强大的 StatFilter 插件,

能够详细统计 SQL 的执行性能,这对于线上分析数据库访问性能有帮助。

2) 替换 DBCP 和 C3P0。Druid 提供了一个高效、功能强大、可扩展性好的数据库连接池。


3) 数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver 和 DruidDataSource 都支持 PasswordCallback。


4) SQL 执行日志,Druid 提供了不同的 LogFilter,能够支持 Common-Logging、Log4j 和 JdkLog,你可以按需要选择相应的 LogFilter,监控你应用的数据库访问情况。


扩展 JDBC,如果你要对 JDBC 层有编程的需求,可以通过 Druid 提供的 Filter-Chain 机制,很方便编写 JDBC 层的扩展插件。

二、DruidDataSource 核心类学习

2.1 DruidDataSource 类总体情况

DruidDataSource 是 druid 最核心的类之一,承载了连接池的启动、关闭、以及连接的获取和管理等功能。

看图可得知 DruidDataSource 继承自 DruidAbstractDataSource 和 CommonDataSource。

说明 DruidDataSrouce 是一个 DataSource,可以直接 getConnection 获取连接。

且拥有两个 task 实现 runnable 接口,三个线程继承 thread,分别处理销毁任务,创建连接任务,

记录统计信息等信息。


2.2 DruidDataSource 类如何初始化

初始化的过程分为两种情况,一种是在加载 druid 时初始化,还有就是在获取连接时也会初始话,

DruidDataSource 的这个初始化时机是可选的,当我们设置 init=true 时,在 createDataSource 时就会调用 DataSource.init()方法进行初始化,

否则,只会在 getConnection 时再进行初始化,当然这个初始化肯定时只会进行一次的。


1,第一步就是判断是否已经初始化了,如果已经初始化,那么不再进行,保证只初始化一次,inited 这个字段是个 volatile 修饰的布尔类型

2,然后是 DruidDriver.getInstance();这一步是为了提前初始化 DruidDriver,因为之前有个 issue 提到这里在多线程下初始化可能存在死锁,所以就给提前显式初始化了

3,加锁,使用一个可重入锁,并使用可处理中断异常的方式获取锁,保证下面的处理只有一个线程能处理


初始化具体流程

初始化驱动实例 -> 加锁 -> 初始化属性 -> 初始化过滤器 -> 校验参数 -> 创建初始化连接并校验后加入池中 -> 创建 logStatsThread、createConnectionThread 和 destroyConnectionThread

-> 注册 MBean,用于支持 JMX -> 如果设置了 keepAlive,通知 createConnectionThread 创建连接对象 -> 解锁


初始化源码如下

public void init() throws SQLException {    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; }
initStackTrace = Utils.toString(Thread.currentThread().getStackTrace());
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); }
if (this.jdbcUrl != null) { this.jdbcUrl = this.jdbcUrl.trim(); initFromWrapDriverUrl(); }
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 (JdbcUtils.isMysqlDbType(dbType)) { 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(); }
initFromSPIServiceLoader();
resolveDriver();
initCheck();
initExceptionSorter(); initValidConnectionChecker(); validationQueryCheck();
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 { 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(); } }
createAndLogThread(); createAndStartCreatorThread(); createAndStartDestroyThread();
initedLatch.await(); init = true;
initedTime = new Date(); registerMbean();
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); } }}
复制代码


发布于: 刚刚阅读数: 2
用户头像

Nick

关注

终身学习,向死而生 2020.03.18 加入

得到、极客时间重度学习者,来infoQ 是为了输出倒逼输入

评论

发布
暂无评论
druid源码学习一_源码_Nick_InfoQ写作社区