最近在做一个内部测试工具类的优化工作中接触到了连接池, 对象池技术,
将原有的未使用连接池的数据库访问操作改成连接池方式.性能有了非常大的提升, 事实证明, 经过两次改造, 原来一个比较大的测试类需要500多秒,
第一次优化后只需要300多秒, 第二次改用连接池之后同一个测试类只需要80多秒.下面是改造过程中的一些总结.
对象池就是以”空间换时间”的 一种常用缓存机制,
这里的”时间”特指创建时间,因此这也给出了对象池的适用范围:如果一种对象的创建过程非常耗时的话, 那么请使用对象池. 内部原理简单的说,
就是将创建的对象放到一个容器中, 用完之后不是销毁而是再放回该容器, 让其他的对象调用, 对象池中还涉及到一些高级的技术, 比如过期销毁,
被破坏时销毁, 对象数超过池大小销毁, 对象池中没有可用空闲对象时等待等等.
apache的common-pool工具库是对池化技术原理的一种具体实现. 在阐述原来之前, 这里先理解几个概念:
对象池
(ObjectPool接口): 可以把它认为是一种容器, 它是用来装池对象的, 并且包含了用来创建池对象的工厂对象
池对象
:就是要放到池容器中的对象, 理论上可以是任何对象.
对象池工厂
(ObjectPoolFactory接口):用来创建对象池的工厂, 这个没什么好说的.
池对象工厂
(PoolableObjectFactory 接口):用来创建池对象,
将不用的池对象进行钝化(passivateObject), 对要使用的池对象进行激活(activeObject),
对池对象进行验证(validateObject), 对有问题的池对象进行销毁(destroyObject)等工作
对象池中封装了创建, 获取, 归还, 销毁池对象的职责, 当然这些工作都是通过池对象工厂来实施的, 容器内部还有一个或多个用来盛池对象的容器.对象池会对容器大小, 存放时间, 访问等待时间, 空闲时间等等进行一些控制, 因为可以根据需要来调整这些设置.
当 需要拿一个池对象的时候, 就从容器中取出一个, 如果容器中没有的话, 而且又没有达到容器的最大限制, 那么就调用池对象工厂,
新建一个池对象, 并调用工厂的激活方法, 对创建的对象进行激活, 验证等一系列操作. 如果已经达到池容器的最大值,
而对象池中又经没有空闲的对象, 那么将会继续等待, 直到有新的空闲的对象被丢进来, 当然这个等待也是有限度的, 如果超出了这个限度,
对象池就会抛出异常.
“出来 混, 总是要还的”, 池对象也是如此, 当将用完的池对象归还到对象池中的时候, 对象池会调用池对象工厂对该池对象进行验证,
如果验证不通过则被认为是有问题的对象, 将会被销毁, 同样如果容器已经满了, 这个归还池对象将变的”无家可归”, 也会被销毁,
如果不属于上面两种情况, 对象池就会调用工厂对象将其钝化并放入容器中. 在整个过程中, 激活, 检查, 钝化处理都不是必须的,
因此我们在实现PoolableObjectFactory接口的时候, 一般不作处理, 给空实现即可,
所以诞生了BasePoolableObjectFactory.
当然你也可以将要已有的对象创建好, 然后通过addObject放到对象池中去, 以备后用.
为了确保对对象池的访问都是线程安全的, 所有对容器的操作都必须放在synchronized中.
在 apache的common-pool工具库中有5种对象池:GenericObjectPool和
GenericKeyedObjectPool, SoftReferenceObjectPool, StackObjectPool,
StackKeyedObjectPool.
五种对象池可分为两类, 一类是无key的:
另一类是有key的:
前面两种用CursorableLinkedList来做容器, SoftReferenceObjectPool用ArrayList做容器,
一次性创建所有池化对象, 并对容器中的对象进行了软引用(SoftReference)处理,
从而保证在内存充足的时候池对象不会轻易被jvm垃圾回收, 从而具有很强的缓存能力. 最后两种用Stack做容器.
不带key的对象池是对前面池技术原理的一种简单实现, 带key的相对复杂一些, 它会将池对象按照key来进行分类,
具有相同的key被划分到一组类别中, 因此有多少个key, 就会有多少个容器. 之所以需要带key的这种对象池,
是因为普通的对象池通过makeObject()方法创建的对象基本上都是一模一样的, 因为没法传递参数来对池对象进行定制.
因此四种池对象的区别主要体现在内部的容器的区别, Stack遵循”后进先出”的原则并能保证线程安全,
CursorableLinkedList是一个内部用游标(cursor)来定位当前元素的双向链表, 是非线程安全的,
但是能满足对容器的并发修改.ArrayList是非线程安全的, 便利方便的容器.
使用对象池的一般步骤:创建一个池对象工厂, 将该工厂注入到对象池中, 当要取池对象, 调用borrowObject, 当要归还池对象时, 调用returnObject, 销毁池对象调用clear(), 如果要连池对象工厂也一起销毁, 则调用close().
下面是一些时序图:
borrowObject:
returnObject:
invalidateObject:
apache的连接池工具库common-dbcp是common-pool在数据库访问方面的一个具体应用.当对common-pool熟悉之后,
对common-dbcp就很好理解了. 它通过对已有的Connection,
Statment对象包装成池对象PoolableConnection, PoolablePreparedStatement.
然后在这些池化的对象中, 持有一个对对象池的引用, 在关闭的时候, 不进行真正的关闭处理, 而是通过调用:
1. _pool.returnObject(this);
或:
1. _pool.returnObject(_key,this);
这样一句, 将连接对象放回连接池中.
而对应的对象池前者采用的是ObjectPool, 后者是KeyedObjectPool, 因为一个数据库只对应一个连接, 而执行操作的Statement却根据Sql的不同会分很多种. 因此需要根据sql语句的不同多次进行缓存
在对连接池的管理上, common-dbcp主要采用两种对象:
一个是PoolingDriver, 另一个是PoolingDataSource, 二者的区别是PoolingDriver是一个更底层的操作类,
它持有一个连接池映射列表, 一般针对在一个jvm中要连接多个数据库, 而后者相对简单一些. 内部只能持有一个连接池,
即一个数据源对应一个连接池.
下面是common-dbcp的结构关系:
下面是参考了common-dbcp的例子之后写的一个从连接池中获取连接的工具类
1. /**
2. * 创建连接
3. *
4. * @since 2009-1-22 下午02:58:35
5. */
6. public class ConnectionUtils {
7. // 一些common-dbcp内部定义的protocol
8. private static final String POOL_DRIVER_KEY = "jdbc:apache:commons:dbcp:";
9. private static final String POLLING_DRIVER = "org.apache.commons.dbcp.PoolingDriver";
10.
11. /**
12. * 取得池化驱动器
13. *
14. * @return
15. * @throws ClassNotFoundException
16. * @throws SQLException
17. */
18. private static PoolingDriver getPoolDriver() throws ClassNotFoundException,
19. SQLException {
20. Class.forName(POLLING_DRIVER);
21. return (PoolingDriver) DriverManager.getDriver(POOL_DRIVER_KEY);
22. }
23.
24. /**
25. * 销毁所有连接
26. *
27. * @throws Exception
28. */
29. public static void destory() throws Exception {
30. PoolingDriver driver = getPoolDriver();
31. String[] names = driver.getPoolNames();
32. for (String name : names) {
33. driver.getConnectionPool(name).close();
34. }
35. }
36.
37. /**
38. * 从连接池中获取数据库连接
39. */
40. public static Connection getConnection(TableMetaData table)
41. throws Exception {
42. String key = table.getConnectionKey();
43.
44. PoolingDriver driver = getPoolDriver();
45.
46. ObjectPool pool = null;
47. // 这里找不到连接池会抛异常, 需要catch一下
48. try {
49. pool = driver.getConnectionPool(key);
50. } catch (Exception e) {
51. }
52.
53. if (pool == null) {
54. // 根据数据库类型构建连接工厂
55. ConnectionFactory connectionFactory = null;
56. if (table.getDbAddr() != null
57. && TableMetaData.DB_TYPE_MYSQL == table.getDbType()) {
58. Class.forName(TableMetaData.MYSQL_DRIVER);
59. connectionFactory = new DriverManagerConnectionFactory(table
60. .getDBUrl(), null);
61. } else {
62. Class.forName(TableMetaData.ORACLE_DRIVER);
63. connectionFactory = new DriverManagerConnectionFactory(table
64. .getDBUrl(), table.getDbuser(), table.getDbpass());
65. }
66.
67. // 构造连接池
68. ObjectPool connectionPool = new GenericObjectPool(null);
69. new PoolableConnectionFactory(connectionFactory, connectionPool,
70. null, null, false, true);
71.
72. // 将连接池注册到driver中
73. driver.registerPool(key, connectionPool);
74. }
75.
76. // 从连接池中拿一个连接
77. return DriverManager.getConnection(POOL_DRIVER_KEY + key);
78. }
79.
80. }
虽然对象池技术在实际开发过程中用的不是很多, 但是理解之后对我们写程序还是有莫大的好处的, 至少我是这样的
- 大小: 40.7 KB
- 大小: 43.9 KB
- 大小: 21.2 KB
- 大小: 18.2 KB
- 大小: 13.9 KB
- 大小: 9.9 KB
分享到:
相关推荐
DBCP(DataBase Connection Pool)是 apache common上的一个 java 连接池项目,也是 tomcat 使用的连接池组件,依赖 于Jakarta commons-pool 对象池机制,DBCP可以直接的在应用程序中使用。 使用DBCP会用到commons-...
DBCP(DataBase Connection Pool)是 apache common上的一个 java 连接池项目,也是 tomcat 使用的连接池组件,依赖 于Jakarta commons-pool 对象池机制,DBCP可以直接的在应用程序中使用。 使用DBCP会用到commons-...
commons-pool.jar和commons-dbcp-1.2.jar包
commons-dbcp-1.4 jar java连接池. .commons-dbcp 是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件。单独使用dbcp需要3个包:
commons-dbcp-1.4和commons-pool-1.6驱动包下载(亲测可用)
数据源和连接池的jar包 commons-dbcp-1.2.2.jar commons-pool-1.3.jar
commons-dbcp-1.4.jar和commons-pool-1.5.6.jar
commons-pool-1.5.4-bin.tar commons-dbcp-1.2.1 有用的上的
commons-collections-3.2.jar+commons-dbcp-1.2.1.jar+commons-pool-1.3.jar
用strust 在strust-config 中配置数据库连接源的时候 需要这两个jar 包 (struts1.2,如果是struts1.1可能还需要一个jar包)否则回报 jar包里面的一个类未找到
commons-pool-1.6,commons-dbcp-1.4 连接池下载
dbcp连接池所需jar(commons-collections-3.1.jar,commons-dbcp-1.2.jar,commons-pool-1.6.)
开发工具 commons-dbcp2-2.1.1开发工具 commons-dbcp2-2.1.1开发工具 commons-dbcp2-2.1.1开发工具 commons-dbcp2-2.1.1开发工具 commons-dbcp2-2.1.1开发工具 commons-dbcp2-2.1.1开发工具 commons-dbcp2-2.1.1开发...
commons-dbcp-1.2.2.jar和commons-pool-1.3.jar包,数据源包
Commons-pool-文件+commons-dbcp文件.rar Commons-pool-文件+commons-dbcp文件.rar Commons-pool-文件+commons-dbcp文件.rar
使用jdbc时用到的jar包,其中包含(commons-collections-3.1.jar、commons-dbcp-1.2.2.jar、commons-pool.jar)
commons-collections.jar、 commons-pool.jar、 commons-dbcp-1.2.1.jar三个jar包3分超值放送啊
commons-pool-1.3.jar 和commons-dbcp-1.2.2.jar 解决注解与baseDAO继承HibernateDaoSupport冲突的问题
commons-pool-1.6.jar DBCP数据库连接池必不可少的jar包
Apache commons-pool本质上是"对象池",即通过一定的规则来维护对象集合的容器;commos-pool在很多场景中,用来实现"连接池"/"任务worker池"等,大家常用的dbcp数据库连接池,也是基于commons-pool实现.