聊聊對象池框架 commons pool2
當資源對象的創建/銷毀比較耗時的場景下,可以通過"池化"技術,達到資源的復用,以此來減少系統的開銷、增大系統吞吐量,比如數據庫連接池、線程池、Redis 連接池等都是使用的該方式。
Apache Commons Pool 提供了通用對象池的實現,用于管理和復用對象,以提高系統的性能和資源利用率。
圖片
1.基礎用法
1.1 添加依賴
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
</dependency>
1.2 定義對象工廠
PooledObjectFactory 是一個池化對象工廠接口,定義了生成對象、激活對象、鈍化對象、銷毀對象的方法,如下:
public interface PooledObjectFactory<T> {
/**
* Creates an instance that can be served by the pool and wrap it in a
*/
PooledObject<T> makeObject() throws Exception;
/**
* Destroys an instance no longer needed by the pool
*/
void destroyObject(PooledObject<T> p) throws Exception;
/**
* Ensures that the instance is safe to be returned by the pool
*/
boolean validateObject(PooledObject<T> p);
/**
* Reinitializes an instance to be returned by the pool
*/
void activateObject(PooledObject<T> p) throws Exception;
/**
* Uninitializes an instance to be returned to the idle object pool
*/
void passivateObject(PooledObject<T> p) throws Exception;
}
以下是一個簡單的示例:
- 定義需要池化的對象 MyObject
public class MyObject {
private String uid = UUID.randomUUID().toString();
privatevolatileboolean valid = true;
public void initialize() {
System.out.println("初始化對象" + uid);
valid = true;
}
public void destroy() {
System.out.println("銷毀對象" + uid);
valid = false;
}
public boolean isValid() {
return valid;
}
public String getUid() {
return uid;
}
}
- 定義對象工廠
public class MyObjectFactory implements PooledObjectFactory<MyObject> {
@Override
public PooledObject<MyObject> makeObject() throws Exception {
// 創建一個新對象
MyObject object = new MyObject();
// 初始化對象
object.initialize();
returnnew DefaultPooledObject<>(object);
}
@Override
public void destroyObject(PooledObject<MyObject> p) throws Exception {
// 銷毀對象
p.getObject().destroy();
}
@Override
public boolean validateObject(PooledObject<MyObject> p) {
return p.getObject().isValid();
}
@Override
public void activateObject(PooledObject<MyObject> p) throws Exception {
}
@Override
public void passivateObject(PooledObject<MyObject> p) throws Exception {
}
}
1.3 配置對象池
創建 GenericObjectPool 對象,并設置相關參數,如最大對象數量、最小空閑對象數量等。
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(5);
config.setTestWhileIdle(true);
config.setMinEvictableIdleTimeMillis(60000L);
GenericObjectPool<MyObject> pool = new GenericObjectPool<>(new MyObjectFactory(), config);
1.4 借用和歸還對象
MyObject myObject = null;
try {
myObject = pool.borrowObject();
System.out.println("get對象" + myObject.getUid() + " thread:" + Thread.*currentThread*().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (myObject != null) {
pool.returnObject(myObject);
}
} catch (Exception e) {
e.printStackTrace();
}
}
2.Jedis 連接池
Jedis 是一個 Java 語言的 Redis 客戶端庫。它提供了一組易于使用的 API,可以用來連接和操作 Redis 數據庫。
它的內部使用 Commons Pool 來管理 Redis 連接 ,我們使用 jedis 3.3.0 版本寫一個簡單的示例。
public class JedisMain {
public static void main(String[] args) throws Exception{
// 創建連接池配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(20);
// 創建連接池
JedisPool pool = new JedisPool(config, "localhost", 6379);
// 獲取連接
Jedis jedis = pool.getResource();
jedis.set("hello" , "張勇");
// 使用連接
String value = jedis.get("hello");
System.out.println(value);
// 歸還連接
jedis.close();
// 關閉連接池
// pool.close();
Thread.sleep(5000);
}
}
如下圖,JedisFactory 實現了對象工廠,實現了創建對象、銷毀對象、驗證對象、激活對象四個方法。
圖片
比如驗證對象方法,邏輯是調用 Jedis 的 ping 方法,判斷該連接是否存活。
3.原理解析
我們重點解析 GenericObjectPool 類的原理。
3.1 初始化
public GenericObjectPool(
final PooledObjectFactory<T> factory,
final GenericObjectPoolConfig<T> config) {
super(config, ONAME_BASE, config.getJmxNamePrefix());
if (factory == null) {
jmxUnregister(); // tidy up
thrownew IllegalArgumentException("factory may not be null");
}
this.factory = factory;
idleObjects = new LinkedBlockingDeque<>(config.getFairness());
setConfig(config);
}
privatefinal Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
new ConcurrentHashMap<>();
初始化做三件事情:
- 初始化 JedisFactory 工廠對象。
- 對象容器 idleObjects , 類型是 LinkedBlockingDeque 。因此存儲容器有兩個,所有的對象 allObjects 和空閑對象 idleObjects (可以直接取出使用)。
- 配置對象池屬性 。
3.2 創建對象
我們關注 GenericObjectPool 類的 borrowObject 方法。
圖片
邏輯其實很簡單 :
- 從容器中獲取第一個條目對象,若沒有獲取,則調用工廠對象的創建對象方法,并將該對象加入到全局對象 Map。
- 創建成功后,調用對象的激活方法,接著驗證對象的可靠性,最后將對象返回。
3.3 歸還連接
圖片
流程如下:
- 判斷返還對象時是否校驗,假如校驗失敗,則銷毀該對象,將該對象從存儲容器中刪除 ;
- 調用工廠對象的激活對象方法 ;
- 若空閑對象 Map 元素大小達到最大值,則銷毀該對象,將該對象從存儲容器中刪除 ;
- 正常將對象放回到空閑對象容器 idleObjects 。