【SQL开源代码栏目提醒】:文章导读:在新的一年中,各位网友都进入紧张的学习或是工作阶段。
网学会员整理了SQL开源代码-数据库连接池BoneCP源码分析报告 - 电子电信的相关内容供大家参考,祝大家在新的一年里工作和学习顺利!
数据库连接池 BONECP 源码分析报告..............................................................................................21. 简述 ..............................................................................................................................................2 1.1 官方主页 ............................................................................................................................................ 2 1.2 API 文档 .............................................................................................................................................. 2 1.3 BoneCP 简介(译自官方) ............................................................................................................... 2 1.4 BoneCP 特点(译自官方) ............................................................................................................... 2 1.5 本次分析使用的版本......................................................................................................................... 3 1.6 依赖库 ................................................................................................................................................ 3 1.7 包结构说明......................................................................................................................................... 3 1.8 主要类型 ............................................................................................................................................ 4 1.9 连接池创建过程及连接获取过程简述 ............................................................................................. 42. BONECP 生命周期过程 ...............................................................................................................4 2.1 BoneCPConfig 初始化及配置分析 ..................................................................................................... 5 2.2 BoneCPDataSource 分析..................................................................................................................... 5 2.3 BoneCP 初始化过程分析 ................................................................................................................... 5 2.4 BoneCP 的 getConnection过程分析 ................................................................................................ 6 2.5 BoneCP 的 shutdown .......................................................................................................................... 73. BONECP 使用的队列数据结构分析 ............................................................................................7 3.1 LIFOQueue........................................................................................................................................... 7 3.2 LinkedBlockingDeque(JDK1.6) ....................................................................................................... 7 3.3 BoundedLinkedTransferQueue ............................................................................................................ 8 3.4 LinkedTransferQueue(将纳入 JDK1.7)........................................................................................... 84.BONECP 线程池 ............................................................................................................................ 105.发现 BONECP‐0.7.0 中的 BUG....................................................................................................... 12 数据库连接池 BoneCP 源码分析报告 作者:葛一帆 时间:2011‐03‐131. 简述1.1 官方主页 http://jolbox.com/1.2 API 文档 http://jolbox.com/bonecp/downloads/site/apidocs/index.html1.3 BoneCP 简介(译自官方) BoneCP 是一个快速,免费,
开源的 Java 数据库连接池(即,JDBCPool)。
如果熟悉 C3P0或者 DBCP,那么你也就知道它是用来干什么的。
简单地说,这个
代码库将为你管理数据库连接,让你的应用具有更快的数据库访问能力。
1.4 BoneCP 特点(译自官方) 具有高可扩展性的快速连接池 在 connection 状态改变时,可配置回调机制(钩式拦截器) 通过分区(Partitioning)来提升性能 允许你直接访问 connection 或 statement 自动地扩展 pool 容量 支持 statementcaching 支持异步地获取 connection(通过返回一个 FutureltConnectiongt) 以异步的方式,施放辅助线程(helper threads)来关闭 connection 和 statement,以获 得高性能。
在每个新获取的 connection 上,通过简单的机制,执行自定义的 statement,(即通过简 单的
SQL 语句来测试 connection 是否有效,对应的配置属性为 initSQL) 支持运行时切换数据库,而不需要停止(shutdown)应用 能够自动地回放(replay)任何失败的事务(例如,数据库或网络出现故障等等) 支持 JMX 可以延迟初始化(lazyinitialization) 支持使用 XML 或 property 文件的配置方式 支持 idleconnectiontimeouts 和 maxconnectionage 自动检验 connection(是否活跃等等) 允许直接从数据库获取连接,而不通过 Driver 支持 Datasouce 和 Hibernate 支持通过 debugginghooks 来定位获取后未关闭的 connection 支持通过 debugging 来显示被关闭了两次的 connection 的堆栈轨迹(stacklocations) 支持自定义 poolname 整洁有序的
代码 免费,
开源,纯 Java 编写,具有完整的文档。
1.5 本次分析使用的版本bonecp‐0.7.0.jar1.6 依赖库slf4j‐api‐1.5.8.jarslf4j‐nop‐1.5.8.jarguava‐r08.jar (googlelib)1.7 包结构说明主要包com.jolbox.bonecp 连接池核心包com.jolbox.bonecp.hooks 支持 connection 状态改变时的事件通知com.jolbox.bonecp.proxy 代理 java.
sql.下的接口,仅供内部使用jsr166y 将 jsr166y 的部分类型直接打包进来其它包,提供外部支持和测试com.jolbox.bonecp.providercom.jolbox.bonecp.springcom.jolbox.benchmark1.8 主要类型com.jolbox.bonecp.BoneCPcom.jolbox.bonecp.BoneCPConfigcom.jolbox.bonecp.BoneCPDataSourcecom.jolbox.bonecp 包下的多种线程及包装类1.9 连接池创建过程及连接获取过程简述
代码: //加载驱动 Class.forNamequotcom.mysql.jdbc.Driverquot //实例化配置类 BoneCPConfigconfignewBoneCPConfig //设置配置 config.setJdbcUrlquotjdbc:mysql://localhost:3306/sailing_dbquot config.setUsernamequotrootquot config.setPasswordquotrootquot config.setXxx… //创建连接池 BoneCPpoolnewBoneCPconfig //从池中获取连接 Connectionconnectionpool.getConnection System.out.printlnconnection.getClass.getName System.out.printlnquot‐‐‐gtquotconnection.getAutoCommit connection.close pool.shutdown注:以上是使用 BoneCP 的基本方式,其它方式,比如数据源支持,读取配置文件等,都是对该过程的进一步封装,以“适配”不同的使用需求。
下面开始进行源码分析,对该连接池的运行过程进行详细的阐述。
2. BoneCP 生命周期过程2.1 BoneCPConfig 初始化及配置分析BoneCPConfig 提供了以 XML 和 Properties 两种配置方式。
它的无参构造方法将会加载类路径下的 XML 配置文件,然后调用 JDK 的 XML 解析库对 XML 进行解析(即 javax.xml..下) 。
XML文件中的配置信息最终会被转化成 java.util.Properties 对象,然后调用设置 setProperties 的方法进行属性设置。
setProperties 方法接收 Properties 对象作为参数,并借助 Java 反射机制,用一次 for 循环将属性注入到字段中。
BoneCPConfig 将属性字段的类型限定为 4 种, 分别为:intlongboolean 和 String。
除了常规的连接池设置,配置属性中需要特别说明字段如下:connectionHookinitSQL2.2 BoneCPDataSource 分析BoneCP 提供的数据源 BoneCPDataSource 继承了 BoneCPConfig,它实现了 DataSource 接口,还实现了 ObjectFactory 接口,支持 JNDI 的支持。
该数据源持有一个 BoneCP 对象(即 pool字段),该字段使用了 volatile 关键字进行修饰,privatetransientvolatileBoneCPpool这里使用 volatile 是没有必要的,应该做成 final 的,并且不使用 Lazy 加载,因为每个数据源对应一个唯一的 pool,只要使用相同的数据源对象,这个 pool 句柄就不会改变。
即使在多线程环境下,只要使用的数据源对象没有改变,那么使用的就是相同的 pool 对象。
唯一的风险在于初期调用 getConnection 时存在竞争。
2.3 BoneCP 初始化过程分析执行 newBoneCPconfig,在构造方法中,将主要进行以下操作:1. 调 用 config.sanitize 方 法 , 纠 正 config 中 的 错 误 配 置 。
例 如 , 如 果 属 性 maxConnectionsPerPartition(每个分区的最大)小于 2,那么就将该变量设置为 50。
2. 根据 config 中的属性来设置 BoneCP 的一些常规属性。
3. 如果不进行 connection 的延迟测试(即 config.isLazyInit 方法返回 false),那么就获取一 个 connection,然后立即 close。
完成获取 connection 的测试。
一旦发生异常,就将异常 对象及信息对象传递给 Hook 对象(如果 config 中配置了 Hook)4. 判断 config 是否启用了 connection 追踪功能,如果启用,就实例化一个队列,并赋给对 应的字段。
该队列用来观察是否有应该安全关闭却没有关闭的 connection。
5. 调用 java.util.concurrent.Executors 的 newCachedThreadPool 方法,创建一个线程池,并 赋给对应的字段。
该线程池用于异步地创建 connection。
6. 根据 config 中配置的分区数量,创建 ConnectionPartition 数组。
7. 如 果 config 中 设 置 了 使 用 独 立 的 线 程 来 关 闭 connection , 那 么 就 调 用 java.util.concurrent.Executors 的 newFixedThreadPool 方法,创建一个线程池,并赋给对应 的字段。
8. 调用 java.util.concurrent.Executors 的 newScheduledThreadPool 方法,创建一个线程池, 并赋给相应字段。
该线程池用于定期地测试 connection 的活性(即用它发送一条简单的
SQL),并关闭故障的 connection。
每个分区一个线程。
9. 再调用 java.util.concurrent.Executors 的 newScheduledThreadPool 方法,创建一个线程池, 并赋给相应字段。
该线程池用于定期地检查 connection 是否过期。
每个分区一个线程。
10. 调用 java.util.concurrent.Executors 的 newFixedThreadPool 方法,创建一个线程池,并赋 给相应字段。
该线程池用于观察每个分区,根据需要动态地创建新的 connection 或清理 过剩的。
11. 如果开启了对 close 操作的监控,就会执行 Executors.newCachedThreadPool 来创建一个 线程池,监控那些失败的 close 操作。
12. 创建各个分区(for 循环)及相关步骤 实例化分区(ConnectionPartition) ,并填充到分区数组 同 时 实 例 化 TransferQueue , 将 该 数 据 结 构 注 入 到 ConnectionPartition 。
注 , TransferQueue 是 jsr166y 中规定的标准接口,在 jsr166y 中提供了一种实现,即 LinkedTransferQueue 。
BoneCP 提 供 了 两 种 实 现 , 即 LIFOQueue 和 BoundedLinkedTransferQueue。
根据 config 中的配置选择相应的数据结构,后面的章 节会对这些 Queue 进行详细的分析。
完成队列的注入之后,再次判断 config.isLazyInit,如果返回 false,就直接为每个 分区添加 connection 对象(满足最小连接数) 。
注,这里使用的 connection 对象是 com.jolbox.bonecp.ConnectionHandle 的实例。
这个类是对 JDBCconnection 的包装, 它实现了 java.
sql.Connection 接口。
通过包装来完成对脱离队列的 connection 的管 理。
该包装类中持有原 pool 对象和 partition 对象。
启动线程池(根据 config 中的配置进行判断)13. 再次初始化并启动一个线程池,用来提供释放 statement 的辅助线程(如果 config 中配 置的该属性大于 0) 。
14. 如果激活了 JMX,就执行 initJMX2.4 BoneCP 的 getConnection过程分析1. 判断自己是否关闭(即调用了 shutdown 方法) ,如果发现关闭,就抛出 SQLException2. 获取当前线程 ID,按分区数量对该值取模,计算出要访问的分区数组下标。
3. 按下标获取分区对象(ConnectionPartition) ,然后从分区持有的队列中 poll 出一个空闲 的 connection 对象(类型为 ConnectionHandle,它包装了 JDBCconnection) 。
4. 如果 poll 的结果为 null,说明该分区的队列中没有空闲的 connection,那么从分区数组 的 0 号开始轮询每个分区, (循环结束之后仍有可 直到 poll 出一个非 null 的 connection。
能为 null)5. 判断分区的 connection 是否达到了上限, 如果没有,就向队列中插入一个新的 connection6. 如果得到的结果还是为 null,那么就调用分区的“阻塞一定时间的 poll 方法”,当该 poll 方法执行结束,仍然为 null,就抛出 SQLException7. 调用 ConnectionHandle 对象的 renewConnection 方法,将该对象标记为“打开(也就是设 置一个 boolean 变量)”。
这一方法会将当前线程设置进 ConnectionHandle 对象。
8. 判断该 ConnectionHandle 对象是否持有一个 Hook, 如果有, 就执行 Hook 的 onCheckOut 方法,并将该 ConnectionHandle 对象作为参数传入。
Hook 对象用以监听 connection 的 生命周期。
9. 如果配置了 closeConnectionWatch 属性为 true, 就开启一个线程来监听 ConnectionHandle 对象的 close 操作。
这是一种用于 debug 的功能。
10. 最后将该 ConnectionHandle 对象作为 java.
sql.Connection 类型返回。
2.5 BoneCP 的 shutdown1. 设置 poolShuttingDown 属性为 true,表明该对象已经关闭,如果有线程继续调用 getConnection 方法,将抛出 SQLException2. 设置 shutdownStackTrace 属性(将当前线程的堆栈轨迹拼装为一个 String),这个字符串 将作为 SQLException 的参数传入3. 关闭所有线程池4. 在 lock 环境下关闭所有连接,并记录一些统计信息。
如果 ConnectionHandle 设置了 ConnectionHook,就调用 Hook 的 onDestroy 方法。
3. BoneCP 使用的队列数据结构分析3.1 LIFOQueueLIFOQueue 继 承 自 java.util.concurrent.LinkedBlockingDeque , 借 助 父 类 的 API 来 实 现TransferQueue 接口。
并重写相应的方法,从 FIFO 队列变成了 LIFO 队列。
所以,对该数据结构的分析主要集中在 LinkedBlockingDeque 上。
3.2 LinkedBlockingDeque(JDK1.6) 它基于双向双端链表,持有头节点和尾节点的引用。
节点(Node)是一个静态内部类, 它是存放队列元素的最小单位,并持有上一个节点和下一个节点的引用。
具有固定的容量。
在构造时,一旦指定容量,将无法插入更多元素(容量字段 capacity 。
如果无法预计容量的上限,那么可以使用默认的构造方法(它使用 具有 final 修饰符) Integer.MAX_VALUE 作为容量值)。
该实现通过一个 ReentrantLock 对象和两个 Condition 对象来实现“阻塞”和“同步”。
以下 是这几个字段的介绍: privatefinalReentrantLocklocknewReentrantLock 这把“可重入锁”用于对链表进行添加删除的方法,避免并发问题。
privatefinalConditionnotEmptylock.newCondition 执行 take 操作的“阻塞”方法调用 notEmpty.await方法“等待链表非空”,当链表插 入元素后,会调用 notEmpty.signal方法,唤醒某个被 notEmpty 阻塞的方法。
privatefinalConditionnotFulllock.newCondition 执行 put 操作的“阻塞”方法调用 notFull.await方法用于“等待链表未满”,当链表删 除元素后,会调用 notFull.signal方法,唤醒某个被 notFull 阻塞的方法。
上面介绍的是强制的阻塞,该队列通过 Condition 的 awaitNanoslong方法实现了超时阻 塞。
注,BoneCP 中使用了超时阻塞,而没有使用强制阻塞方法。
该数据结构的其它部分都是普通的链表操作,就不在本文中叙述。
3.3 BoundedLinkedTransferQueueBoundedLinkedTransferQueue 继承了 LinkedTransferQueue, 通过限定容量来实现了该类的 “有界(bounded)”版本。
它使用了原子变量 java.util.concurrent.atomic.AtomicInteger 来保存链表的大小。
并在 offer 方法中调用 ReentrantLock 的 lock 方法。
如果没有在配置文件中指定容量,构造 BoneCP 时将会选用 LinkedTransferQueue。
下面对 LinkedTransferQueue 进行关键部位的分析。
3.4 LinkedTransferQueue(将纳入 JDK1.7)该类是 jsr166y 提供的 TransferQueue 接口的实现。
它基于单向双端链表,与上面介绍的 LinkedBlockingDeque 的区别是,它的 Node 仅持有 其下一个节点的引用。
这是一个典型 FIFO 队列。
该实现类的一个最大的特点是,所有的队列基本操作都是去调用一个私有方法: privateExferEebooleanhaveDatainthowlongnanos… 该方法较为复杂,它的执行流程将在后面详细描述。
该类中,全部以 CAS(比较并交换)方式进行字段的赋值和链表的操作。
通过第三方的 sun.misc.Unsafe 类来实现 CAS 模式的无锁算法。
所以,在整个类中,你是看不到对字段 的赋值操作。
4 个特殊的静态变量语义(这些变量作为 xfer 的第三个参数传入,在不同的队列方法中 指定,比如 pollput 等) : NOW 用于不带超时的 poll 和 tryTransfer 方法 ASYNC 用于 offer,put 和 add 方法 SYNC 用于 transfer 和 take 方法 TIMED 用于带超时的 poll 和 tryTransfer 方法 该类中,静态内部类 Node 的实现较为复杂。
具有 4 个字段: finalbooleanisData 该布尔值用来判断 volatileObjectitem 存放插入的元素 volatileNodenext 下一个节点的引用 volatileThreadwaiter 存 放 当 前 线 程 , 当 执 行 LockSupport.parkthis 时 , 当 前 线 程 被 阻 塞 , 当 执 行 LockSupport.unparknode.waiter时,该节点对应的线程将解除阻塞3.4.1 LinkedTransferQueue 元素插入过程分析(调用 offerEe方法,或 put) 插入第一个节点 1. 执行 xferetrueASYNC0 2. 由于是第一次插入,头节点为 null,就跳过 for 循环 3. 判断第三个参数 howNOW,进入 if 块 4. 创建一个新节点 Node,将插入的元素和第二个参数传入构造方法 5. 然后调用私有方法 tryAppend,将 Node 和第二个参数传入 6. 经过一些判断, 以 tryAppend 方法调用 casHead 方法, CAS 的方式将 Node 赋值给 head 字段(头节点)。
插入第二个节点 1. 同样,执行 xferetrueASYNC0 2. 进入 for 循环,取得头节点的引用。
但在第一次循环时,判断该节点的 isData 字段 .
上一篇:
MySQL Performance Tuning Best Practices
下一篇:
基于web二手汽车交易管理系统(含录像)