微信搜索 爪哇根据地,一起学习一起进步!
Druid简介:
连接池的出现解决了高并发场景下,为每次请求创建数据库连接时程序的性能消耗,以及请求响应时间的增加。之前使用过C3P0连接池,后来关注了Druid连接池,一个阿里巴巴数据库事业部出品,号称为监控而生的数据库连接池。与其他连接池相比,提供了丰富的,多维度的数据监控。
Druid git 源码地址:github.com/alibaba/dru…
Druid的初始化:
常用的属性名词说明:
url :数据库地址,通过前缀指定驱动类型。
username :数据库用户名
password :数据库密码
initialSize :连接池初始化大小
maxActive :连接池最大连接数
maxWait :获取连接最大等待时间
testOnBorrow :获取连接时是否检测连接是否有效
testOnReturn :回收连接时是否检测连接是否有效
testWhileIdle :获取连接时是否检测连接连接空闲时间超过
timeBetweenEvictionRunsMillis,超过则检测连接
timeBetweenEvictionRunsMillis :DestroyConnectionThread线程(定时检测连接有效性)
执行间隔(Sleep控制)
minEvictableIdleTimeMillis :连接再线程池中最小存活时间
removeAbandoned :是否开启线程活动时间超过removeAbandonedTimeout,进行丢弃
removeAbandonedTimeout :活动线程最大存活时间,超过直接丢弃(单位分钟)。
logAbandoned :关闭abandon连接是否打印日志。
filters :需要启用Druid的过滤器。
connectionInitSqls :连接时需要设置的mysql参数,例:set names utf8mb4;asyncInit :是否异步初始化连接池
createScheduler :初始化连接的线程池
validationQuery :创建连接校验连接是否有效执行的sql语句
keepAlive :是否创建空闲连接
minIdle :最小空闲连接数
在Spring中可以配置数据源,并在init-method属性直接调用init方法。
<bean name="readDataSource1" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
如果没有手动调用初始化,当第一次调用DataSource的getConnection方法时也会触发初始化逻辑完成初始化。
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
init();
if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(this);
return filterChain.dataSource_connect(this, maxWaitMillis);
} else {
return getConnectionDirect(maxWaitMillis);
}
}
DruidDataSource初始化大体做了几件事:
1.处理自定义的参数值。根据jdbcUrl判断数据库类型(Mysql或Oracle)、检查参数是否合法、初始化所配置的过滤器以及加载驱动。
2.初始化检测器,如用于连接是否有效的检测器,和用于查询语句是有有效的检测器。
3.实例化了一个数据源统计对象JdbcDataSourceStat。Druid连接池以监控闻名,
JdbcDataSourceStat则是数据的存储中心!
4.初始化三个数组,分别存放数据库连接,丢弃的连接,已经检测后仍然存活的连接,并初始化数据库连接。初始化三个线程,分别为日志记录线程,生成数据库连接,和检测连接是否超时。
DruidDataSource初始化几个关键点:
初始化连接池
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();
}
}
当property配置了createScheduler(线程池) 、asyncInit(boolean),并且asyncInit值为true,则进行异步初始化,反之阻塞主线程直至初始化完成。poolingCount为当前线池大小。
private void submitCreateTask(boolean initTask) {
createTaskCount++;
//创建生成连接的Runnable任务
CreateConnectionTask task = new CreateConnectionTask(initTask);
if (createTasks == null) {
//long类型数组用于存放Runnable任务
createTasks = new long[8];
}
boolean putted = false;
for (int i = 0; i < createTasks.length; ++i) {
if (createTasks[i] == 0) {
createTasks[i] = task.taskId;
putted = true;
break;
}
}
if (!putted) {
//当createTasks数组满时,扩容为之前数组大小的1.5倍。
long[] array = new long[createTasks.length * 3 / 2];
System.arraycopy(createTasks, 0, array, 0, createTasks.length);
array[createTasks.length] = task.taskId;
createTasks = array;
}
//丢进线程池执行
this.createSchedulerFuture = createScheduler.submit(task);
}
方法createPhysicalConnection()返回一个持有引用实际连接的
若asyncInit 配置为false时,通过一个while循环创建连接,核心代码如下
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);
}
}
}
逻辑与线程池模式一致。
到此完成了Druid线程池,常规参数配置及初始化规定数量连接数。剩下还有三个线程的创建,留到下章继续分析
(扫码关注公众号)
注:基于水平限制,文档只是对于自己的提升,有出入请指教。(未经授权禁转)