前段时间研究了下dbcp的一些源码,发现dbcp对common pools依赖比较严重,基本就是基于pool的扩展接口实现的。所以也就顺便看了下pools的源码。
common pools的代码总体结构来说是比较简单的。
先上一个类图:
说明: ObjectPool和KeyedObjectPool基本接口都是一致的,唯一不同的就是KeyedObjectPool是基于key做为键值,其对应的存储结构就是 Map<Object , List<Object>>, 每次根据key定位List<objct>的value后,再进行池化管理。
1. GenericObjectPool: 通用的对象池处理类,维护池的大小,空闲链接等等。 针对对象的makeObject,destoryObject等都是委托给PoolableObjectFactory进行处理。
基本参数和dbcp的配置参数一样,可以参考: http://agapple.iteye.com/admin/blogs/772507
关注几个特别的参数:
2. StackObjectPool : 基于stack堆栈概念的对象池,对象池的borrow或者return都是符合stack的先进先出弹栈的管理,相比于GenericObjectPool,它只有maxIdle(最大空闲数), maxTotal(最大资源数)管理,暂时还未想到特定的应用场景,因为觉得GenericObjectPool的功能基本可以覆盖StackObjectPool,只要适当的调整参数。
3. SoftReferenceObjectPool : 看名字就应该猜到是基于SoftReference进行对象持有的pool,在内存不足时可以主动的释放pool object对象, 通过ReferenceQueue获取object对象需要被清除的时间点。至于SoftReference可以看下理解 Java 的 GC 与 幽灵引用
4. GenericKeyedObjectPool: 区别于GenericObjectPool,主要是该pool池,针对每个资源都有自己特定的域,就是一个对应的key。每个key下可以有一组object。 maxTotal是针对所有的key下的object之和,而非单个。
首先以dbcp为例,介绍其相关扩展点。
说明:
1. dbcp整个连接池的管理是使用GenericObjectPool
2. dbcp这里通过扩展实现自定义的PoolableObjectFactory , 用于定义如何创建/销毁/校验一个datasource等。
3. 使用的statement cache利用的是GenericKeyedObjectPool,因为statement cache是基于key进行statement管理的,所以比较适合。但目前在使用statement cache会出现一些异常情况,慎用。
4. PoolingConnection实现了statement cache对应池对象的创建/销毁。 同时在datasource的执行close,同时需要清理整个statement cache pool的close动作,避免出现资源泄漏。
实现一个memcache client的连接池代码
1. 实现一个PoolableObjectFactory接口,提供创建/销毁/校验的方法
public class MemcachedPoolableSocketFactory implements PoolableObjectFactory { ....... public MemcachedPoolableSocketFactory(String host, int port, int connectedTimeout){ this.host = host; this.port = port; this.connectedTimeout = connectedTimeout; } @Override public Object makeObject() throws Exception { return new SockIO(host, port, connectedTimeout); } @Override public void destroyObject(Object obj) throws Exception { if (obj instanceof SockIO) { ((SockIO) obj).close(); } } @Override public boolean validateObject(Object obj) { if (obj instanceof SockIO) { SockIO sock = (SockIO) obj; try { sock.getOut().write("version \r\n".getBytes()); sock.getOut().flush(); } catch (IOException e) { e.printStackTrace(); } } return true; } ...... }
2. 创建SocketPool类,用于管理pool
public class MemcachedSocketPool { private GenericObjectPool sockPool = null; private int maxActive = 50; private int minIdle = 1; private int maxIdle = 50; private int maxWait = 1000; public MemcachedSocketPool(String host, int port){ sockPool = new GenericObjectPool(); sockPool.setMaxActive(maxActive); sockPool.setMaxIdle(maxIdle); sockPool.setMinIdle(minIdle); sockPool.setMaxWait(maxWait); sockPool.setTestOnBorrow(false); sockPool.setTestOnReturn(false); sockPool.setTimeBetweenEvictionRunsMillis(10 * 1000); sockPool.setNumTestsPerEvictionRun(maxActive + maxIdle); sockPool.setMinEvictableIdleTimeMillis(30 * 60 * 1000); sockPool.setTestWhileIdle(true); sockPool.setFactory(new MemcachedPoolableSocketFactory(host, port, 1000)); } public SockIO createSocket() { assert sockPool != null; try { return (SockIO) sockPool.borrowObject(); } catch (Exception e) { } return null; } }
3. 客户端端使用
MemcachedSocketPool pool = new MemcachedSocketPool("10.20.156.37", 6000); SockIO sock = pool.createSocket(); sock.write(cmd.getBytes()); sock.flush();
使用common-pools后,实现一个自己的连接池也是相对比较单了