数据库连接池clearpool(java实现)详解

clearpool的maven项目托管在https://github.com/xionghuiCoder/clearpool,同时也可以在http://www.oschina.net/p/clearpool上了解它的简单介绍。

首先大家可能会问,现在开源社区已经有了很多数据库连接池:c3p0、proxool、dbcp、bonecp、druid和tomcat_jdbc等,我们还需要重新发明轮子么?是的,上面这些连接池都非常优秀,并且都有自己优点:比如高并发时druid和tomcat_jdbc近乎完美的性能,再比如proxool和druid的监控功能;然而它们并不是完美的,比如性能是否还能再提升、如何管理分布式数据库、如何支持分布式事务。好的,下面就来介绍下clearpool以及它是如何支持这些功能的。

1、如何使用clearpool

clearpool提供的接口类为ClearPoolDataSource.java,它支持两种配置方法:配置xml、ioc注入(或者编程实现):xml的详细配置信息请参考https://github.com/xionghuiCoder/clearpool/tree/master/src/main/java/org/opensource/clearpool/configuration/example下面的三个示例。

这里说下clearpool和其它数据库池的不同的地方:

1)它放弃了最小连接池数(min-pool-size),取而代之的是核心连接池数(core-pool-size):个人认为min-pool-size没什么用处

2)它不提供返回真实连接的方法,ClearPoolDataSource.java的getConnection()方法返回的是数据库连接的代理,除非利用反射机制(不建议这么做),不然不能真正操作数据库连接;这么做的原因请参考3)

3)它不会在获取连接时测试连接是否有效,因为这个操作可能会非常耗时(比如需要远程调用);这是因为它并不会返回真实连接,所以就不用担心会不小心关闭连接等意外情况。

2、如何配置DataSource

clearpool提供三种数据源配置方式:

1)配置JDBC,请参考clearpool-jdbc.xml的<jdbc>标签;

2)配置jndi,请参考clearpool-jndi.xml的<jndi>标签

3)如果是自定义的DataSource,可以使用DataSourceHolder.java的setDataSourceMap()方法来配置(key为数据库池名称,和<alias>一致,value为DataSource);

clearpool支持三种数据源:ConnectionPoolDataSource、XADataSource和DataSource。

如果需要支持分布式事务则需要配置XADataSource或者支持XA协议的JDBC。

3、clearpool的性能

根据测试结果:druid和tomcat_jdbc的性能是非常优秀的,在某些高并发的情况下它俩的性能要比其它的连接池高上十倍甚至是几十倍。然而他们无一例外地都使用了连接池锁(proxool未使用锁,在高并发的情况下获取连接会导致ArrayIndexOutOfBoundsException),那么如果我们不使用锁的话那岂不是能提高性能?是的,这是当然。clearpool在获取连接时没有使用任何锁机制,而且不会导致任何异常,它的秘诀是使用Atomic操作代替了锁,详细的实现清参考类https://github.com/xionghuiCoder/clearpool/blob/master/src/main/java/org/opensource/clearpool/core/chain/AtomicSingleChain.java。然而AtomicSingleChain.java用到了sun.misc.Unsafe.java,或许比较保守的人会说这是一种冒险的做法;是的,或许以后某天oracle会抛弃Unsafe.java;不过没关系,这时我们可以使用AtomicSingleChain.java的替身LockSingleChain.java,这是一个普通的、使用锁的连接池实现。下图显示了clearpool和druid、tomcat_jdbc的性能比较结果(最大连接数为50、核心连接数为20、线程数为100):

数据库连接池clearpool(java实现)详解_第1张图片  

上图的测试结果来自于:https://github.com/xionghuiCoder/clearpool/blob/master/src/test/java/org/opensource/clearpool/CompareWithWonderfulPool.java,如果想了解和其它数据库的性能比较结果请运行测试类:https://github.com/xionghuiCoder/clearpool/blob/master/src/test/java/org/opensource/clearpool/CompareWithPopularPool.java

上图比较的是高并发下“获取连接”时的性能,如果需要测试高并发下的DML,clearpool的性能就和druid、tomcat_jdbc差不多了,原因在于clearpool使用动态代理生成Statement代理,而动态代理生成字节码时会比较耗时,并且会作为WeakReference缓存,而高并发下会多次启动gc,所以会多次回收并多次生成字节码,所以就影响了测试结果(详细原理请参考JDK的java.lang.reflect.Proxy.java的源码)。而druid和tomcat_jdbc使用的是普通的代理,所以没有这个问题。clearpool使用动态代理的原因主要是为了减少代码量(我是个懒人),如果使用静态代理,至少需要实现三个代理类(Statement、PreparedStatement和CallableStatement)以及它们的所有方法。而使用动态代理只需要一个handler(https://github.com/xionghuiCoder/clearpool/blob/master/src/main/java/org/opensource/clearpool/datasource/proxy/dynamic/StatementHandler.java)就足够了。

4、管理分布式数据库池和JTA

clearpool可以管理分布式数据库,配置分布式连接池时我们需要做的是配置多个<distribute-url>标签(xml配置)或者初始化多个连接池(IOC或者编程实现)。clearpool会根据配置自动管理所有连接池:比如多余空闲连接的回收、无效连接的重置、数据库池的监控等等。clearpool还提供了支持JTA的接口类:UserTransactionImpl.java,我们可以像下面这样使用分布式事务(con1和con2取自数据库连接池):

UserTransaction tx = new UserTransactionImpl();    
Statement st1 = con1.createStatement();     
Statement st2 = con2.createStatement();     
tx.begin();     
try{     
    st1.execute(“insert**”);     
    st2.execute(“update**”);     
    tx.commit();     
}catch(Throwable t){     
    tx.rollback();     
}

   

5、如何监控数据库连接池

clearpool通过JMX来实现数据库池的监控功能,而druid和proxool则是使用servlet实现;然而JMX的可编程性并没有servlet好,这里放弃servlet是考虑到可能有的用户并不需要web容器,如果为了监控数据库池而让他们专门搭建个web环境是不公平的。

如果想要使得clearpool监控数据库连接池,则需要配置<console>(xml配置)或者设置ClearPoolDataSource的Console字段,我们可以通过<console>的<port>配置端口、通过<console>的<security>配置用户名和密码,详细监控功能可参考测试用例https://github.com/xionghuiCoder/clearpool/blob/master/src/test/java/org/opensource/clearpool/UniqueFunction.java。下面是监控页面的部分截图:

数据库连接池clearpool(java实现)详解_第2张图片

如果需要打印sql,则需要配置<show-sql>为true或者设置ClearPoolDataSource的setShowSql()方法,同时需要启用日志功能(classpath中加入commons-logging.jar即可),下面是查询代码、打印的sql和sql耗时的截图:

PreparedStatement stmt = conn.prepareStatement("select 1 from geek where name=? and age=?");
stmt.setString(1, "Bill Joy");
stmt.setInt(2, 60);
stmt.execute();
stmt.close();

数据库连接池clearpool(java实现)详解_第3张图片

6、如何加密数据库密码

如果想要加密配置文件中的数据库密码,可以参考如下配置:

<jdbc>
	<jdbc-class>org.opensource.clearpool.util.MockTestDriver</jdbc-class>
	<url>jdbc:mock:case</url>
	<user>1</user>
	<password>1</password>
	<security-class>org.opensource.clearpool.security.SecretAES</security-class>
	<file-path>path</file-path>
	<status>encrypt</status>
</jdbc>


<security-class>为加密和解密的处理类(可以不设置,默认会使用SecretAES),如果设置它,则需要实现接口org.opensource.clearpool.security.Secret.javaSecret接口有两个方法:encrypt(String)进行加密,decrypt(String)进行解密。

<file-path>为该配置文件的绝对路径(请将里面的path替换为配置文件的真正路径),加密密码后会根据该路径来重置密码;如果<status>为encrypt则<file-path>需要配置,否则不

<status>表示<password>的状态:有效取值为encrypt和decrypt;encrypt表示需要加密(加密密码后会重置为decrypt),decrypt表示需要解密。

下图为加密后的xml:

<jdbc>
	<jdbc-class>org.opensource.clearpool.util.MockTestDriver</jdbc-class>
	<url>jdbc:mock:case</url>
	<user>1</user>
	<password>WDroom5uAqkPfQwgOMCQGQ==</password>
	<security-class>org.opensource.clearpool.security.SecretAES</security-class>
	<file-path>path</file-path>
	<status>decrypt</status>
</jdbc>

另外啰嗦两句:数据库密码加密功能有点儿掩耳盗铃的感觉,因为最终数据库的配置信息都会保存在connection的实现类的字段中,所以可以通过反射来获取密码(我尝试过oracle驱动和mysql驱动,都可以获取密码,没试过sqlserver驱动,不过应该也一样)。所以数据库密码加密只能防止用户直接从配置文件内获取密码。

7、其它

clearpool可以打印日志,然而并不提供log的任何实现,它并不像proxool那样强制把commons-logging.jar的代码内嵌入proxool.jar,这样耦合性太强了,考虑下如果commons-logging发布了新版本怎么办;而clearpool能够和commons-logging无缝衔接,如果需要日志功能则只需要把commons-logging.jar加入到classpath中。好吧,“clearpool并不生产日志,它只是日志的搬运工”。clearpool代码量十分地少,jar包只有75KB左右,非常易于使用,相信你能非常快地使用和修改它。

PS:clearpool支持JDK6和JDK7。


你可能感兴趣的:(连接池,数据库连接池,clearpool,clearpool,clearpool)