Druid源码阅读之连接池
概述
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源码阅读之连接池的更多相关文章
- 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分
这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...
- Mina源码阅读笔记(四)—Mina的连接IoConnector2
接着Mina源码阅读笔记(四)-Mina的连接IoConnector1,,我们继续: AbstractIoAcceptor: 001 package org.apache.mina.core.rewr ...
- Caffe源码阅读(1) 全连接层
Caffe源码阅读(1) 全连接层 发表于 2014-09-15 | 今天看全连接层的实现.主要看的是https://github.com/BVLC/caffe/blob/master/src ...
- Redis源码阅读(三)集群-连接初始化
Redis源码阅读(三)集群-连接建立 对于并发请求很高的生产环境,单个Redis满足不了性能要求,通常都会配置Redis集群来提高服务性能.3.0之后的Redis支持了集群模式. Redis官方提供 ...
- druid 源码分析与学习(含详细监控设计思路的彩蛋)(转)
原文路径:http://herman-liu76.iteye.com/blog/2308563 Druid是阿里巴巴公司的数据库连接池工具,昨天突然想学习一下阿里的druid源码,于是下载下来分析了 ...
- Spark源码阅读之存储体系--存储体系概述与shuffle服务
一.概述 根据<深入理解Spark:核心思想与源码分析>一书,结合最新的spark源代码master分支进行源码阅读,对新版本的代码加上自己的一些理解,如有错误,希望指出. 1.块管理器B ...
- Flume-NG源码阅读之AvroSink
org.apache.flume.sink.AvroSink是用来通过网络来传输数据的,可以将event发送到RPC服务器(比如AvroSource),使用AvroSink和AvroSource可以组 ...
- 【原】FMDB源码阅读(三)
[原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...
- 【原】FMDB源码阅读(二)
[原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...
随机推荐
- 定时调度框架:Quartz.net
Quartz.net相关概念 经常出现场景:定时轮询数据库同步,定时邮件通知,定时处理数据等 Scheduler (计划者或调度器) Job (工作对象):将要定时执行的任务代码写到实现Ijob接口的 ...
- css预处理器less和scss之less介绍(一)
第一次发的标题有误,重发一遍,抱歉了 一.less基础语法 1.声明变量:@变量名:变量值 使用变量:@变量名 例如 @color : #ff0000; @length : 100px; #div1{ ...
- 软工+C(2017第3期) 超链接
// 上一篇:分数和checklist // 下一篇:Alpha/Beta换人 注:平常看文章,总有能和构建之法,软件工程相关的链接,增量记录,也可以通过在其他人博客的交流中使用相关的超链接,在使用中 ...
- 【集美大学1411_助教博客】团队作业6——展示博客(Alpha版本)
写在前面的话 工作还真是应该抓紧做呢,以下评分是助教在出差前评的,但出差回来就忘记了大部分内容.同学们都在预期时间内完成了自己的alpha项目.由于助教的频繁出差,评分工作落下一大截,在此表示欠意,会 ...
- Java单元测试之覆盖率统计eclemma
安装 有两种安装方法 下载安装(推荐) 地址: http://sourceforge.net/projects/eclemma/ 将解压后的features和plugins目录下的文件分别拷贝到Ecl ...
- [2017BUAA软工助教]第0次个人作业
学习别人的经验和体会 零.前言 我认为人生就是一次次地从<存在>到<光明>. 一.软件工程师的成长 0.这是一个博客索引 同学们在上这门课的时候基本都是大三,觉得在大学里,到教 ...
- 个人作业2—英语学习APP案例分析
第一部分 调研, 评测 1.下载并使用,描述最简单直观的个人第一次上手体验. 一打开就受到暴击! 界面布局与大部分学习类APP类似,功能模块.搜索框跟一些日常推送.界面简单功能一目了然,方便操作. 2 ...
- Swing-setBounds()用法-入门
先看API: public void setBounds(Rectangle r) 移动组件并调整其大小,使其符合新的有界矩形 r.由 r.x 和 r.y 指定组件的新位置,由 r.width 和 r ...
- 201521123059 《Java程序设计》第五周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 1.Comparable与Arrays.sort,其功能是对指定对象数组按升序进 ...
- 201521123103 《Java学习笔记》 第四周学习总结
一.本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. (1)多态性:相同形态,不同行为(不同的定义): (2)多态绑定:运行时能够自动地选择调用哪个 ...