在编程中,池的运用应当是很广泛了,如连接池,线程池,资源池等。实际编程中,我们也可以构建Cache池,也可能有涉及到其它的情况,需要使用pool方案来解决。为此Apache中开发了一个通用的Pool工具:commons-pool,现在这个工具有两个版本(1.X, 2.X)并且有很大的不同。这里说的是Commons-pool 的1.X版本。
本文将分为两个部分,第一部分介绍commons-pool 1.X API。第二部分使用commons-pool构筑简易ConnectionPool。
第一部分Commons-pool 1.X 介绍
下面是commons-pool的设计方案:
总体来看,就是由ObjectPoolFactory创建出ObjectPool,然后在使用ObjectPool的过程中(例如borrowObject(),returnObject()等方法),通过ObjectFactory,来对特定的对象进行处理。
由于官方文档中,对于commons-pool的描述并不多,所以只能看代码来了解了。
如果你的ObjectPool组件都设计完毕,在使用ObjectPool时,入口就是ObjectPool。
下面就看看ObjectPool的说明 :
从这个说明上可以知道,只需要在合适的地方合理的调用borrowObject()、invalidateObject()、returnObject() 对对象进行处理即可。通过源码知道,这几个方法在处理过程中会委托给PoolableObjectFactory来处理,下面就来看看这个接口的设计。
从上面的描述也可以看出:
1、borrowObject时,会先从pool中找出一个idle的对象,如果找不到,就可以使用makeObject()来创建对象,并使用activateObject()将对象设置为active状态。如果找到了对象,不需要调用makeObject(),但是会调用validateObject()。
2、如果一个对象要被抛弃,就可以使用invaliateObejct()将对象设置为无效状态。
3、如果对象正常使用完毕,就应该让对象返回pool中,并将其钝化,也就是设置为idle状态。
通过对commons-pool的了解,应该就可以明白,在使用commons-pool,可以对ObjectPoolFactory、PoolableObjectFactory进行定制,在必要的情况下,也可以对ObjectPool进行定制。
第二部分使用commons-pool构筑简易ConnectionPool
// 定义一个ConnectionPoolFactory,目前中是实现了构建器,其它功能还待完善。
package com.fjn.frame.apache.commons.pool.connectionPool; import org.apache.commons.pool.PoolableObjectFactory; import org.apache.commons.pool.impl.GenericObjectPoolFactory; public class ConnectionPoolFactory extends GenericObjectPoolFactory{ public ConnectionPoolFactory(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis, boolean lifo) { super(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle, softMinEvictableIdleTimeMillis, lifo); } }
package com.fjn.frame.apache.commons.pool.connectionPool; public class ConnectionInfo { ConnectionFactory factory; public ConnectionFactory getFactory() { return factory; } public void setFactory(ConnectionFactory factory) { this.factory = factory; } }
// 连接工厂
package com.fjn.frame.apache.commons.pool.connectionPool; import java.sql.DriverManager; import org.apache.commons.pool.BasePoolableObjectFactory; public class ConnectionFactory extends BasePoolableObjectFactory{ private String driverClass; private String username; private String password; private String url; public String getDriverClass() { return driverClass; } public void setDriverClass(String driverClass) { this.driverClass = driverClass; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } Connection _createConnection() throws Exception{ Connection connection=null; java.sql.Connection conn=null; Class.forName(driverClass); conn= DriverManager.getConnection(url, username, password); connection=new Connection(); connection.setConn(conn); ConnectionInfo connInfo=new ConnectionInfo(); connInfo.setFactory(this); return connection; } // 只为了测试使用 private Object createConnection(){ return new Connection(); } @Override public Object makeObject() throws Exception { return createConnection(); } }
// Connection
package com.fjn.frame.apache.commons.pool.connectionPool; import java.sql.SQLException; public class Connection { private java.sql.Connection conn; private ConnectionInfo connInfo; public java.sql.Connection getConn() { return conn; } public void setConn(java.sql.Connection conn) { this.conn = conn; } public ConnectionInfo getConnInfo() { return connInfo; } public void setConnInfo(ConnectionInfo connInfo) { this.connInfo = connInfo; } public void close(){ if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
// 测试
package com.fjn.frame.apache.commons.pool.connectionPool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.pool.ObjectPool; import org.apache.commons.pool.impl.GenericObjectPool; public class ConnectionManager { private ObjectPool pool; private ConnectionFactory connFactory; private ConnectionPoolFactory connPoolFactory; public void initConnectionPool(int maxNum, int maxIdleCount, int minIdleCount, long maxWaitTime) { int maxActive = maxNum; byte whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION; long maxWait = maxWaitTime; int maxIdle = maxIdleCount; int minIdle = minIdleCount; boolean testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW; boolean testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN; long timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; int numTestsPerEvictionRun = GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; long minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; boolean testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE; long softMinEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS; boolean lifo = GenericObjectPool.DEFAULT_LIFO; connPoolFactory = new ConnectionPoolFactory(connFactory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle, softMinEvictableIdleTimeMillis, lifo); } public static void main(String[] args) { final ConnectionManager mgr = new ConnectionManager(); mgr.connFactory = new ConnectionFactory(); mgr.connFactory.setDriverClass("com.mysql.jdbc.Driver"); mgr.connFactory.setPassword("mysql"); mgr.connFactory.setUsername("mysql"); mgr.connFactory.setUrl("url:localhost:3306"); // 改成真实的URL mgr.initConnectionPool(1000, 50, 5, 1000 * 60); mgr.pool = mgr.connPoolFactory.createPool(); final AtomicInteger count = new AtomicInteger(0); int threadNum = Runtime.getRuntime().availableProcessors(); ExecutorService client = Executors.newFixedThreadPool(threadNum); for (int i = 0; i < threadNum; i++) { client.submit(new Runnable() { @Override public void run() { while (true && count.get() < 100) { try { Thread.sleep(500); } catch (InterruptedException e1) { e1.printStackTrace(); } Connection connection = null; try { connection = (Connection) mgr.pool.borrowObject(); try { int value = count.incrementAndGet(); if (value < 100) { String threadName = Thread.currentThread() .getName(); int activeNum = mgr.pool.getNumActive(); int idleNum = mgr.pool.getNumIdle(); String content = "ThreadName: " + threadName + "\t SQL: " + "insert into tableA ( ct ) values (‘" + value + "‘); \t activeNum=" + activeNum + "\t idleNum=" + idleNum; System.out.println(content); } } catch (Exception e) { mgr.pool.invalidateObject(connection); connection = null; } finally { // make sure the object is returned to the pool if (null != connection) { mgr.pool.returnObject(connection); } } } catch (Exception e) { // failed to borrow an object } } } }); } } }
目前使用这个小程序,应用可以跑起来了,但是它并不是一个真实的连接池实现,需要的工作还有很多。
这个小程序,只是一个commons-pool 1.X的demo ,也只是为了证明我上面的想法,具体如何使用,还需要深入的研究。