这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党
背景
最近在看rocketmq-exporter
源码,发现了rocketmq-exporter
在管理rocketmq-client
使用了一个apache的三方库commons-pool
,所以打算研究下commons-pool
(对象池化技术)
作用
commons-pool
的使用场景是什么呢?很简单,就是一些对象的场景和销毁代价比较大的,就需要使用对象池化技术,也就是commons-pool
。
类似我们的数据库连接池、线程池
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
官网
核心组件
对象工厂
目前对象工厂有两个常用的抽象类
- BaseKeyedPooledObjectFactory
- BasePooledObjectFactory
BaseKeyedPooledObjectFactory
管理的对象池是一个key value的样子
BasePooledObjectFactory
管理的就是一个vlaue对象
举个最简单的例子,比如我们只有一个RocketMQ
集群
我们使用BasePooledObjectFactory
即可,如果我们有多个RocketMQ
集群,不同集群获取的rocketmq-client
是不同的,所以我们使用BaseKeyedPooledObjectFactory
更合适
ObjectFactory
主要是负责对象的创建、初始化、状态检测和消费,这些方法都需要我们自己去实现
这里我们使用BasePooledObjectFactory类给大家演示一个demo
要创建的对象
- XiaoZou
public class XiaoZou {
private final String name;
public XiaoZou(String name) {
this.name = name;
}
public void create() {
System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象:" + name + "正在被创建。。。。。。");
}
public void destroy() {
System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象:" + name + "正在被销毁。。。。。。");
}
public boolean isValid() {
System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象" + name + "正在检验是否可用。。。。。。");
return true;
}
}
我们的对象工厂
- XiaoZouBasePooledObjectFactory
public class XiaoZouBasePooledObjectFactory extends BasePooledObjectFactory<XiaoZou> {
@Override
public XiaoZou create() {
// 创建一个新的MyObject对象
XiaoZou myObject = new XiaoZou(UUID.randomUUID().toString());
myObject.create();
return myObject;
}
@Override
public PooledObject<XiaoZou> wrap(XiaoZou myObject) {
// 将MyObject对象封装到一个PooledObject对象中并返回
return new DefaultPooledObject<>(myObject);
}
@Override
public void destroyObject(PooledObject<XiaoZou> pooledObject) {
// 销毁对象
XiaoZou myObject = pooledObject.getObject();
myObject.destroy();
}
@Override
public boolean validateObject(PooledObject<XiaoZou> pooledObject) {
// 验证对象是否可用
XiaoZou myObject = pooledObject.getObject();
return myObject.isValid();
}
}
对象池
- GenericObjectPool
我们最终操作获取对象都不是通过上面提到的PooledObjectFactory
,而是通过GenericObjectPool
,GenericObjectPool
通过持有PooledObjectFactory
然后操作对象
GenericObjectPool
实现了ObjectPool
接口,ObjectPool
定义了操作对象的一些方法
public interface ObjectPool<T> extends Closeable {
//添加对象
void addObject() throws Exception, IllegalStateException,
UnsupportedOperationException;
// 批量添加对象
default void addObjects(final int count) throws Exception {
for (int i = 0; i < count; i++) {
addObject();
}
}
//从池中获取对象
T borrowObject() throws Exception, NoSuchElementException,
IllegalStateException;
//清除池,池可用
void clear() throws Exception, UnsupportedOperationException;
//关闭池,池不可用
@Override
void close();
//获取活跃对象个数
int getNumActive();
//获取空闲对象个数
int getNumIdle();
//废弃对象
void invalidateObject(T obj) throws Exception;
default void invalidateObject(final T obj, final DestroyMode destroyMode) throws Exception {
invalidateObject(obj);
}
//将对象放回池中
void returnObject(T obj) throws Exception;
}
对象池配置
关于对象池的配置我们都封在在GenericObjectPoolConfig
中
这里我们可以看看GenericObjectPoolConfig
的一个简单配置
// 创建对象池配置
GenericObjectPoolConfig<XiaoZou> poolConfig = new GenericObjectPoolConfig<>();
// 对象池中最大对象数
poolConfig.setMaxTotal(3);
// 对象池中最小空闲对象数
poolConfig.setMinIdle(1);
// 对象池中最大空闲对象数
poolConfig.setMaxIdle(2);
// 当对象池耗尽时,是否等待获取对象
poolConfig.setBlockWhenExhausted(true);
// 创建对象时是否进行对象有效性检查
poolConfig.setTestOnCreate(true);
// 借出对象时是否进行对象有效性检查
poolConfig.setTestOnBorrow(true);
// 归还对象时是否进行对象有效性检查
poolConfig.setTestOnReturn(true);
// 空闲时是否进行对象有效性检查
poolConfig.setTestWhileIdle(true);
// 获取对象最大等待时间 默认 -1 一直等待
poolConfig.setMaxWait(Duration.ofSeconds(2));
其实可以看到和线程池的一些属性有些类似,毕竟都是池
完整demo演示
博客涉及的源码已全部上传至github
地址: https://github.com/weihubeats/weihubeats_demos/tree/master/java-demos/commons-pool-demo
定义一个我们要管理的对象
- XiaoZou
public class XiaoZou {
private final String name;
public XiaoZou(String name) {
this.name = name;
}
public void create() {
System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象:" + name + "正在被创建。。。。。。");
}
public void destroy() {
System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象:" + name + "正在被销毁。。。。。。");
}
public boolean isValid() {
System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象" + name + "正在检验是否可用。。。。。。");
return true;
}
}
- 定义对象工厂
public class XiaoZouBasePooledObjectFactory extends BasePooledObjectFactory<XiaoZou> {
@Override
public XiaoZou create() {
// 创建一个新的MyObject对象
XiaoZou myObject = new XiaoZou(UUID.randomUUID().toString());
myObject.create();
return myObject;
}
@Override
public PooledObject<XiaoZou> wrap(XiaoZou myObject) {
// 将MyObject对象封装到一个PooledObject对象中并返回
return new DefaultPooledObject<>(myObject);
}
@Override
public void destroyObject(PooledObject<XiaoZou> pooledObject) {
// 销毁对象
XiaoZou myObject = pooledObject.getObject();
myObject.destroy();
}
@Override
public boolean validateObject(PooledObject<XiaoZou> pooledObject) {
// 验证对象是否可用
XiaoZou myObject = pooledObject.getObject();
return myObject.isValid();
}
}
- 创建对象池
public enum XiaZouPool {
/**
* 线程安全的单例
*/
INSTANCE;
private final GenericObjectPool<XiaoZou> objectPool;
XiaZouPool() {
// 创建对象池配置
GenericObjectPoolConfig<XiaoZou> poolConfig = new GenericObjectPoolConfig<>();
// 对象池中最大对象数
poolConfig.setMaxTotal(3);
// 对象池中最小空闲对象数
poolConfig.setMinIdle(1);
// 对象池中最大空闲对象数
poolConfig.setMaxIdle(2);
// 当对象池耗尽时,是否等待获取对象
poolConfig.setBlockWhenExhausted(true);
// 创建对象时是否进行对象有效性检查
poolConfig.setTestOnCreate(true);
// 借出对象时是否进行对象有效性检查
poolConfig.setTestOnBorrow(true);
// 归还对象时是否进行对象有效性检查
poolConfig.setTestOnReturn(true);
// 空闲时是否进行对象有效性检查
poolConfig.setTestWhileIdle(true);
// 获取对象最大等待时间 默认 -1 一直等待
poolConfig.setMaxWait(Duration.ofSeconds(2));
// 创建对象工厂
XiaoZouBasePooledObjectFactory objectFactory = new XiaoZouBasePooledObjectFactory();
// 创建对象池
objectPool = new GenericObjectPool<>(objectFactory, poolConfig);
}
/**
* 从对象池中借出一个对象
* @return
* @throws Exception
*/
public XiaoZou borrowObject() throws Exception {
XiaoZou zou = objectPool.borrowObject();
int numActive = objectPool.getNumActive();
int numIdle = objectPool.getNumIdle();
System.out.println("ThreadName:" + Thread.currentThread().getName() + " 活跃对象数量:" + numActive + " 空闲对象数量:" + numIdle);
System.out.println("------------------------------------------------------------");
return zou;
}
public void returnObject(XiaoZou myObject) {
// 将对象归还给对象池
objectPool.returnObject(myObject);
}
/**
* 获取活跃的对象数
* @return
*/
public int getNumActive() {
return objectPool.getNumActive();
}
/**
* 获取空闲的对象数
* @return
*/
public int getNumIdle() {
return objectPool.getNumIdle();
}
}
- 测试
@Test
public void singleTest() throws Exception{
// 最大对象数量3 最小空闲对象数量1 最大空闲数量 2
XiaZouPool myObjectPool = XiaZouPool.INSTANCE;
numActiveAndNumIdle(myObjectPool);
// 获取对象
XiaoZou obj = myObjectPool.borrowObject();
// 获取对象
XiaoZou obj2 = myObjectPool.borrowObject();
// 获取对象
XiaoZou obj3 = myObjectPool.borrowObject();
// 获取对象 已超出最大对象数
XiaoZou obj4 = myObjectPool.borrowObject();
//归还对象
myObjectPool.returnObject(obj);
System.out.println("ThreadName:" + Thread.currentThread().getName() + " returned: " + JSONObject.toJSONString(obj));
sleep();
numActiveAndNumIdle(myObjectPool);
}
这里故意获取对象超过最大对象数量演示超时
总结
本文我们主要学习了Apache Commons Pool
是什么,以及核心组件,还有如何使用,不涉及原理和源码的分析。在知道了如何使用后我们后面才会去分析源码