druid源码解读--druid的集成和源代码中关于配置文件的解读

拉取代码

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;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值