一、初识 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);
}
}
}
复制代码
评论