Druid源码阅读系列(十一)

以下内容是对这两周源码查看的一个梳理,以前没有看过Druid的源码,算是从零开始,研究的不那么深入。希望我的整个研究思路能对你有所帮助。

DruidDataSource

Druid的数据源封装类,通过设置需要的参数之后实例化然后就可以愉快的使用Druid了。

DruidDataSource里最核心的方法是init(),这个方法里面有很多校验和一些设置参数的初始化,驱动加载,filter的加载等。然后init()这个方法调用不是必须的,因为在调用getConnect()时会自动调用init().

我认为里面比较核心的几块逻辑是:

初始连接的创建

初始连接数通过initialSize设定,分同步和异步两种情况:

同步就是一个while循环依次创建,通过底层的Driver.connect(url, info)拿到实际连接,在其前后进行各种各样的监控计数和校验逻辑以及前置的filter逻辑。

异步是根据传进来的createScheduler周期性的执行创建任务,创建逻辑都是调用的createPhysicalConnection这个方法。

public PhysicalConnectionInfo createPhysicalConnection() throws SQLException {
   、、、、、、、、、、、、、、、、、、、、、、、、、、

    String password = getPassword();
    PasswordCallback passwordCallback = getPasswordCallback();

    if (passwordCallback != null) {
        if (passwordCallback instanceof DruidPasswordCallback) {
            DruidPasswordCallback druidPasswordCallback = (DruidPasswordCallback) passwordCallback;

            druidPasswordCallback.setUrl(url);
            druidPasswordCallback.setProperties(connectProperties);
        }

        char[] chars = passwordCallback.getPassword();
        if (chars != null) {
            password = new String(chars);
        }
    }

    Properties physicalConnectProperties = new Properties();
    if (connectProperties != null) {
        physicalConnectProperties.putAll(connectProperties);
    }

    if (user != null && user.length() != 0) {
        physicalConnectProperties.put("user", user);
    }

    if (password != null && password.length() != 0) {
        physicalConnectProperties.put("password", password);
    }

  、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
    try {
        conn = createPhysicalConnection(url, physicalConnectProperties);
        connectedNanos = System.nanoTime();

        if (conn == null) {
            throw new SQLException("connect error, url " + url + ", driverClass " + this.driverClass);
        }

        initPhysicalConnection(conn, variables, globalVariables);
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

    return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos, variables, globalVariables);
}

从代码里可以看出,数据库密码的加密解析也是在这块做的。

创建连接守护线程的创建

创建连接的线程,如果有设置createScheduler则直接采用设置的定时器,否则会创建一个守护线程执行创建的工作。创建的核心逻辑上面已经列了,就不赘述。

createScheduler主要是为了优化一个数据源创立两个线程分别用于创建连接和销毁或检测连接,当分库分表情况下,可能有成百上千个数据库,因此创立大量链接。利用createScheduler可以让多个数据源共享同一个连接池。destroyScheduler同理。

摧毁检测连接守护线程的创建

根据设定的timeBetweenEvictionRunsMillis摧毁连接,摧毁和检测线程的核心逻辑有两个方法shrink()removeAbandoned()

shrink()会做以下几件事:

  1. 将连接池的连接限制为最小连接数,多余的关闭
  2. 对于一些发生过异常的连接由于不知道是否可用,拿去做保活检测
  3. 超过设定的物理超时时间关闭该连接,主要用于跳过mysql 8小时自动断开连接
  4. 关闭大于最大存活时间的连接
  5. 开启保活且存活时间超过保活检测周期的拿去做保活检测
  6. 开启保活且连接池连接加活跃连接数小于最小连接填充连接至最小连接
  7. 保活连接不是一直存在连接池里的,而是一开始和丢弃连接一样从连接池拿掉,当检测有效之后,再将连接放进连接池尾部,如果连接池已经有了对应的holder会将连接丢弃。

removeAbandoned()只有开启了removeAbandoned之后会调用,它的作用是遍历活跃连接,跳过正在执行的,拿到里面存活时间大于removeAbandonedTimeoutMillis的连接,直接关闭并丢掉。

连接获取

获取连接的时候如果有设置filter,会先递归的调用filter逻辑之后(主要是监控和计数),才会调用对应的getConnectionDirect()从连接池尾部拿到连接,里面会有一些很复杂的逻辑判断,这块应该和各种状态值有关,就没细究了。

连接关闭

关闭连接入口在DruidPooledConnectionrecycle()方法里,对于在removeAbandoned()里已经被丢弃的连接是不需要回收的,主要是针对没有被丢弃的连接,具体实现是DruidDataSourcerecycle(DruidPooledConnection pooledConnection)方法里,recycle()主要做了一下几件事:

  1. removeAbandoned相关调试,获取连接时当开启removeAbandoned之后,在拿到连接的时候这个状态设置为true,关闭时会进一步检测。
  2. 对于需要回滚的连接进行回滚,如果回滚异常可能导致连接关闭,刷新相应数值。(activeCount,closeCount等)
  3. 当连接使用次数达到phyMaxUseCount这个设置的连接最大使用次数时,关闭连接,这个主要是针对分布式数据库优化的,通过达到一定使用次数后断开重连,使得多个服务器间负载更均衡。
  4. 测试testOnReturn逻辑,如果设置会验证连接是否可用,不可用会关闭连接。
  5. 数据源可用性验证,不可用关闭连接。
  6. 超过设定的物理超时时间关闭该连接。
  7. 所有检验通过后将连接放进连接池,更新相应数值。
PreparedStatementPool

当一个PreparedStatementPool会多次用到,频繁的创建其实引来不必要的消耗,这时候可以引入缓存,即PC CachePS Cache的实现其实很简单,底层就是一个LRU Cache。当设置maxPoolPreparedStatementPerConnectionSize>0则会开启。

DruidPooledPreparedStatement调用close方法时会调用conn.closePoolableStatement(this)方法,如果开启了缓存会调用holder.getStatementPool().put(stmtHolder);stmtHolder放进缓存里。

执行prepareStatement方法时如果开启缓存会通过stmtHolder = holder.getStatementPool().get(key);拿到对应的PreparedStatement对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值