拉取代码
git clone https://github.com/alibaba/druid.git
编译代码(跳过test)
mvn clean install -Dmaven.test.skip=true
集成demo项目pom文件
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!--自启动Druid管理后台-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
druid的配置参数配置
# 数据库配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
url: jdbc:mysql://127.0.0.1:3306/jk_test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&nullCatalogMeansCurrent=true
username: root
password: 12345678
driverClassName: com.mysql.cj.jdbc.Driver
#初始化连接
initial-size: 10
#最大活动连接
max-active: 100
#最小连接池数量
min-idle: 10
#从池中取连接的最大等待时间,单位ms.
max-wait: 60000
#开启池的prepared(默认是false,未调整,经过测试,开启后的性能没有关闭的好。)
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
#每n秒运行一次空闲连接回收器
time-between-eviction-runs-millis: 60000
#池中的连接空闲..后被回收
min-evictable-idle-time-millis: 300000
#验证使用的SQL语句
validation-query: SELECT 1 FROM DUAL
# 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.
test-while-idle: true
#借出连接时不要测试,否则很影响性能
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
#login-username: admin
#login-password: admin
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
#连接泄漏回收参数,当可用连接数少于3个时才执行,超过时间限制,回收没有用(废弃)的连接(默认为 300秒,调整为180)
#getNumIdle() < 2) and (getNumActive() > getMaxActive() - 3)
remove-abandoned: true
# 连接泄漏回收参数,180秒,泄露的连接可以被删除的超时值
remove-abandoned-timeout: 3000
# abanded连接时输出错误日志
logAbandoned: true
demo代码地址
https://gitee.com/julyhhh/druid-demo
durid源码参数配置的作用和代码块分析
initial-size: 10
#初始化连接
//DruidDataSource中 448
{
String property = properties.getProperty("druid.initialSize");
if (property != null && property.length() > 0) {
try {
int value = Integer.parseInt(property);
this.setInitialSize(value);
} catch (NumberFormatException e) {
LOG.error("illegal property 'druid.initialSize'", e);
}
}
}
// 初始化链接需要小于最大活动连接数量
if (getInitialSize() > maxActive) {
throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActive " + maxActive);
}
max-active: 100
#最大活动连接
//DruidDataSource中 470
{
String property = properties.getProperty("druid.maxActive");
if (property != null && property.length() > 0) {
try {
int value = Integer.parseInt(property);
this.setMaxActive(value);
} catch (NumberFormatException e) {
LOG.error("illegal property 'druid.maxActive'", e);
}
}
}
// 设置最大活动连接的代码
lock.lock();
try {
// 获取总的活动数量
int allCount = this.poolingCount + this.activeCount;
// maxActive 和 allCount 取大的那个 TODO 疑问点,为什么还需要比较all count init的时候初始化直接获取最大的maxActive不就好了吗?是基于配置中心动态配置最大活动数量的考虑吗?
if (maxActive > allCount) {
this.connections = Arrays.copyOf(this.connections, maxActive);
evictConnections = new DruidConnectionHolder[maxActive];
keepAliveConnections = new DruidConnectionHolder[maxActive];
} else {
this.connections = Arrays.copyOf(this.connections, allCount);
evictConnections = new DruidConnectionHolder[allCount];
keepAliveConnections = new DruidConnectionHolder[allCount];
}
this.maxActive = maxActive;
} finally {
lock.unlock();
}
min-idle: 10
#最小连接池数量
// 最小连接池的数量
{
String property = properties.getProperty("druid.minIdle");
if (property != null && property.length() > 0) {
try {
int value = Integer.parseInt(property);
this.setMinIdle(value);
} catch (NumberFormatException e) {
LOG.error("illegal property 'druid.minIdle'", e);
}
}
}
// 最大活动连接比较
if (inited && value > this.maxActive) {
throw new IllegalArgumentException("minIdle greater than maxActive, " + maxActive + " < " + this.minIdle);
}
max-wait: 60000
#从池中取连接的最大等待时间,单位ms.
{
String property = properties.getProperty("druid.maxWait");
if (property != null && property.length() > 0) {
try {
int value = Integer.parseInt(property);
this.setMaxWait(value);
} catch (NumberFormatException e) {
LOG.error("illegal property 'druid.maxWait'", e);
}
}
}
@Override
public DruidPooledConnection getConnection() throws SQLException {
return getConnection(maxWait);
}
//创建连接的时候需要传入最大等待时长
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);
}
}
// 最大等待时间放与 waitNanosLocal
// final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait);
// waitNanosLocal.set(nanos - estimate);
pool-prepared-statements: true
#开启池的prepared(默认是false,未调整,经过测试,开启后的性能没有关闭的好。)
{
Boolean value = getBoolean(properties, "druid.poolPreparedStatements");
if (value != null) {
this.setPoolPreparedStatements0(value);
}
}
// 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
if (this.isPoolPreparedStatements()) {
buf.append("\n\n[");
for (int i = 0; i < poolingCount; ++i) {
DruidConnectionHolder conn = connections[i];
if (conn != null) {
if (i != 0) {
buf.append(",");
}
buf.append("\n\t{\n\tID:");
buf.append(System.identityHashCode(conn.getConnection()));
PreparedStatementPool pool = conn.getStatementPool();
buf.append(", \n\tpoolStatements:[");
int entryIndex = 0;
try {
for (Map.Entry<PreparedStatementKey, PreparedStatementHolder> entry : pool.getMap().entrySet()) {
if (entryIndex != 0) {
buf.append(",");
}
buf.append("\n\t\t{hitCount:");
buf.append(entry.getValue().getHitCount());
buf.append(",sql:\"");
buf.append(entry.getKey().getSql());
buf.append("\"");
buf.append("\t}");
entryIndex++;
}
} catch (ConcurrentModificationException e) {
// skip ..
}
buf.append("\n\t\t]");
buf.append("\n\t}");
}
}
buf.append("\n]");
}
max-pool-prepared-statement-per-connection-size: 20
#要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
//为什么要设置大于0的时候开启poolPreparedStatements呢 直接开个参数或者直接true false不可以吗
if (maxPoolPreparedStatementPerConnectionSize > 0) {
this.poolPreparedStatements = true;
} else {
this.poolPreparedStatements = false;
}
this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
}
// PreparedStatementPool 使用到这个参数,如果 <0 默认pool的 LRUCache map 为16 避免扩容??
public PreparedStatementPool(DruidConnectionHolder holder){
this.dataSource = holder.getDataSource();
int initCapacity = holder.getDataSource().getMaxPoolPreparedStatementPerConnectionSize();
if (initCapacity <= 0) {
initCapacity = 16;
}
map = new LRUCache(initCapacity);
}
time-between-eviction-runs-millis: 60000
#每n秒运行一次空闲连接回收器
{
String property = properties.getProperty("druid.timeBetweenEvictionRunsMillis");
if (property != null && property.length() > 0) {
try {
long value = Long.parseLong(property);
this.setTimeBetweenEvictionRunsMillis(value);
} catch (NumberFormatException e) {
LOG.error("illegal property 'druid.timeBetweenEvictionRunsMillis'", e);
}
}
}
// DuridDataSource#init判断保持连接的的时间小于最小的空闲时间 异常
if (keepAlive && keepAliveBetweenTimeMillis <= timeBetweenEvictionRunsMillis) {
throw new SQLException("keepAliveBetweenTimeMillis must be grater than timeBetweenEvictionRunsMillis");
}
//当连接池初始化时,会初始化一个定时清除空闲连接的任务DestroyTask,该任务默认是1分钟执行一次(使用timeBetweenEvictionRunsMillis参数设置)
protected void createAndStartDestroyThread() {
destroyTask = new DestroyTask();
if (destroyScheduler != null) {
//轮训销毁时间 定时清除空闲连接的任务
long period = timeBetweenEvictionRunsMillis;
if (period <= 0) {
period = 1000;
}
destroySchedulerFuture = destroyScheduler.scheduleAtFixedRate(destroyTask, period, period,
TimeUnit.MILLISECONDS);
initedLatch.countDown();
return;
}
String threadName = "Druid-ConnectionPool-Destroy-" + System.identityHashCode(this);
destroyConnectionThread = new DestroyConnectionThread(threadName);
destroyConnectionThread.start();
}
min-evictable-idle-time-millis: 300000
#池中的连接空闲..后被回收
{
String property = properties.getProperty("druid.minEvictableIdleTimeMillis");
if (property != null && property.length() > 0) {
try {
long value = Long.parseLong(property);
this.setMinEvictableIdleTimeMillis(value);
} catch (NumberFormatException e) {
LOG.error("illegal property 'druid.minEvictableIdleTimeMillis'", e);
}
}
}
//定时清除空闲连接的任务, destroySchedulerFuture = destroyScheduler.scheduleAtFixedRate(destroyTask, period, period, TimeUnit.MILLISECONDS);定时任务中会判断连接池的连接是否满足关闭的条件,如果满足则关闭,满足的条件如下:
//关闭条件,空闲时间大于minEvictableIdleTimeMillis,并且空闲连接大于minIdle,
// 其中checkCount为poolingCount - minIdle,即可能被关闭的连接数量
// 或者空闲时间大于maxEvictableIdleTimeMillis
if (idleMillis >= minEvictableIdleTimeMillis) {
if (checkTime && i < checkCount) {
evictConnections[evictCount++] = connection;
continue;
} else if (idleMillis > maxEvictableIdleTimeMillis) {
evictConnections[evictCount++] = connection;
continue;
}
}