概述

Druid是阿里巴巴开源的一个数据库连接池 源码地址。下面简单分析一下连接池是怎么实现的

怎么开始阅读

如果使用过Druid连接池的都只要在Spring配置中配置jdbc的时候配置Driver是使用的DruidDataSource。因此,在读源码的时候也可以从这个类入口。

Datasouce

什么是Datasouce呢,其实就是用于管理数据库连接的工厂类。接口就2个方法

public interface DataSource  extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password)
throws SQLException;
}

DruidDataSource

DruidDataSource就是实现了这个接口,利用池化思想来管理数据库连接。池化的思想我理解的主要有2个目的:

  • 一个目的是可以重复利用一些资源,特别是那些创建和销毁的开销都比较大的资源
  • 一个是可以控制资源的数量,防止大规模的创建导致系统问题 因此,DruidDataSource的关键就是在调用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);
}
}

init()就是初始化连接池,其中核心代码:

public void init() throws SQLException {
...
//line : 845
for (int i = 0, size = getInitialSize(); i < size; ++i) {
//看名字就是知道是保存物理连接的类。并且在这里会实际创建物理连接(JDBC的Connection),
//ps.准确的是说是ConnectionProxyImpl类,这个类是实现监控的关键,后面会再写一篇文章介绍
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
//这种构造的时候传入 this和另外一个对象一般情况都是包装类,
//这样在DruidConnectionHolder就可以获取DruidDataSource的一些状态字段和成员对象(连接归还的时候就会用到)
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
//connections 保存连接的数组
connections[poolingCount] = holder;
//方法就一行代码:poolingCount++; 从属性的名字推断就是对连接池中的连接计数
//初始化完成首poolingCount的值就等于初始化连接的数量
incrementPoolingCount();
}
...

初始化完成后就看怎么获取连接,回到上面getConnection()的方法中,直接看getConnectionDirect()方法吧。(我相信有过滤器的创建连接最终肯定还是调用这个方法,只不过这里会用到责任链模式来处理过滤器,可以参考之前的文章介绍责任链实现方式)。下面看看

public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
int notFullTimeoutRetryCnt = 0;
for (;;) {
DruidPooledConnection poolableConnection;
try {
poolableConnection = getConnectionInternal(maxWaitMillis);
} catch (GetConnectionTimeoutException ex) {
...
} //这里都是做一些配置的校验,比如配置了testOnBorrow,那么在这里会对连接进行测试
... return poolableConnection;
}
} private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
DruidConnectionHolder holder;
...
if (maxWait > 0) {
holder = pollLast(nanos);
} else {
holder = takeLast();
} if (holder != null) {
activeCount++;
if (activeCount > activePeak) {
activePeak = activeCount;
activePeakTime = System.currentTimeMillis();
}
}
... DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);
return poolalbeConnection;
}

这里看到DruidConnectionHolder,也就是再初始化的时候生成的包含了物理连接的保证类,那么pollLast(nanos)肯定就是有超时时间的获取,takeLast()肯定就是无超时时间的获取,那么直接看takeLast()

DruidConnectionHolder takeLast() throws InterruptedException, SQLException {
try {
while (poolingCount == 0) {
emptySignal(); // send signal to CreateThread create connection ...
notEmpty.await(); // signal by recycle or creator
...
}
} catch (InterruptedException ie) {
...
}
//poolingCount--
decrementPoolingCount();
DruidConnectionHolder last = connections[poolingCount];
connections[poolingCount] = null; return last;
}

这里就是如何从连接池获取连接的核心代码了,这里poolingCount为0的情况下就会发送empty信号(回想一下自己刚开始写生产者消费者的代码吧,是不是用的到了一个empty和full来控制消费队列为空和满的情况),这里也是这样的,当poolingCount==0的时候就表示没有可用的连接。

  • 如果达到最大连接数,阻塞
  • 如果没有达到,创建新的连接,这里创建新的连接是通过一个线程去执行的,详情参考CreateConnectionTask。

当然如果当poolingCount不为0的时候,那么直接从连接数组中获取下表为当poolingCount-1的连接返回就可以啦。

连接关闭

看了上面如何从连接池中获取连接,那么很自然都可以知道如何把连接放回连接池中,肯定就是 connections[poolingCount] = 待返回的连接,然后poolingCount+1。

/**
* 回收连接
* DruidDataSouce.java
*/
protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); result = putLast(holder, lastActiveTimeMillis);
....
} boolean putLast(DruidConnectionHolder e, long lastActiveTimeMillis) {
if (poolingCount >= maxActive) {
return false;
} e.setLastActiveTimeMillis(lastActiveTimeMillis);
connections[poolingCount] = e;
incrementPoolingCount(); notEmpty.signal();
return true;
}

果然和我们想的一样,不过这里还有一个很重要的一部,调用notEmpty.signal();

小结

从复杂逻辑中把连接池的相关逻辑抽取出来,其实就很简单,类似于一个生产者消费者模型。希望的这篇文章对你有帮助。

Druid源码阅读之连接池的更多相关文章

  1. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

  2. Mina源码阅读笔记(四)—Mina的连接IoConnector2

    接着Mina源码阅读笔记(四)-Mina的连接IoConnector1,,我们继续: AbstractIoAcceptor: 001 package org.apache.mina.core.rewr ...

  3. Caffe源码阅读(1) 全连接层

    Caffe源码阅读(1) 全连接层 发表于 2014-09-15   |   今天看全连接层的实现.主要看的是https://github.com/BVLC/caffe/blob/master/src ...

  4. Redis源码阅读(三)集群-连接初始化

    Redis源码阅读(三)集群-连接建立 对于并发请求很高的生产环境,单个Redis满足不了性能要求,通常都会配置Redis集群来提高服务性能.3.0之后的Redis支持了集群模式. Redis官方提供 ...

  5. druid 源码分析与学习(含详细监控设计思路的彩蛋)(转)

    原文路径:http://herman-liu76.iteye.com/blog/2308563  Druid是阿里巴巴公司的数据库连接池工具,昨天突然想学习一下阿里的druid源码,于是下载下来分析了 ...

  6. Spark源码阅读之存储体系--存储体系概述与shuffle服务

    一.概述 根据<深入理解Spark:核心思想与源码分析>一书,结合最新的spark源代码master分支进行源码阅读,对新版本的代码加上自己的一些理解,如有错误,希望指出. 1.块管理器B ...

  7. Flume-NG源码阅读之AvroSink

    org.apache.flume.sink.AvroSink是用来通过网络来传输数据的,可以将event发送到RPC服务器(比如AvroSource),使用AvroSink和AvroSource可以组 ...

  8. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  9. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

随机推荐

  1. 设置input的placeholder样式

    自定义input默认placeholder样式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 inpu ...

  2. Spring+mybatis 实现aop数据库读写分离,多数据库源配置

    在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应用中,数据库都是读多写少 ...

  3. Struts2中Action接收参数的方法

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt112 Struts2中Action接收参数的方法主要有以下三种: 1.使用A ...

  4. Java之JMX 详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt194 一.JMX简介 JMX是一种JAVA的正式规范,它主要目的是让程序有被 ...

  5. ★浅谈Spanking情节

  6. 201521123082 《Java程序设计》第12周学习总结

    201521123082 <Java程序设计>第12周学习总结 标签(空格分隔): java 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. An ...

  7. 201521123098 《Java程序设计》第7周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 1. ArrayList代码分析 1.1 解释ArrayList的contains源代码 该方法调用了ind ...

  8. 201521123065《Java程序设计》第2周学习总结

    1.本周学习总结 字符串创建后是不可变的,可以使用加号进行字符串之间的拼接. 使用for循环对字符串进行修正会不断产生新的字符串,应使用StringBuilder. 字符串内容的比较要用equal. ...

  9. 201521123104《java程序设计》第13周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu ...

  10. Servlet一些基础

    Servlet 是一套规范,规定了如何通过Java代码来开发动态网站,并由 javax.servlet 和 javax.servlet.http 两个包中的类来实现. servlet是一个服务器端组建 ...