<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
池塘裡養:Object;
首先看一個基於common-pool2
物件池元件的應用案例,主要有工廠類、物件池、物件三個核心角色,以及池化物件的使用流程:
import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ObjPool { public static void main(String[] args) throws Exception { // 宣告物件池 DevObjPool devObjPool = new DevObjPool() ; // 池中借用物件 DevObj devObj = devObjPool.borrowObject(); System.out.println("Idle="+devObjPool.getNumIdle()+";Active="+devObjPool.getNumActive()); // 使用物件 devObj.devObjInfo(); // 歸還給物件池 devObjPool.returnObject(devObj); System.out.println("Idle="+devObjPool.getNumIdle()+";Active="+devObjPool.getNumActive()); // 檢視物件池 System.out.println(devObjPool.listAllObjects()); } } /** * 物件定義 */ class DevObj { private static final Logger logger = LoggerFactory.getLogger(DevObj.class) ; public DevObj (){ logger.info("build...dev...obj"); } public void devObjInfo (){ logger.info("dev...obj...info"); } } /** * 物件工廠 */ class DevObjFactory extends BasePooledObjectFactory<DevObj> { @Override public DevObj create() throws Exception { // 建立物件 return new DevObj() ; } @Override public PooledObject<DevObj> wrap(DevObj devObj) { // 池化物件 return new DefaultPooledObject<>(devObj); } } /** * 物件池 */ class DevObjPool extends GenericObjectPool<DevObj> { public DevObjPool() { super(new DevObjFactory(), new GenericObjectPoolConfig<>()); } }
案例中物件是完全自定義的;物件工廠中則重寫兩個核心方法:建立和包裝,以此建立池化物件;物件池的構建依賴定義的物件工廠,設定採用元件提供的常規設定類;可以通過調整物件範例化的時間以及建立物件的個數,初步理解物件池的原理。
BasePooledObjectFactory類
則是該介面的基礎實現;Closeable
介面,管理物件生命週期,以及活躍和空閒物件的資料資訊獲取;GenericObjectPool類
是對於該介面的實現,並且是可設定化的方式;DefaultPooledObject
包裝類,實現該介面並且執行緒安全,注意工廠類中的重寫;通過物件池獲取物件,可能是通過工廠新建立的,也可能是空閒的物件;當物件獲取成功且使用完成後,需要歸還物件;在案例執行過程中,不斷查詢物件池中空閒和活躍物件的數量,用來監控池的變化。
public GenericObjectPool(final PooledObjectFactory<T> factory,final GenericObjectPoolConfig<T> config);
在完整的構造方法中,涉及到三個核心物件:工廠物件、設定物件、雙端阻塞佇列;通過這幾個物件建立一個新的物件池;在config中提供了一些簡單的預設設定:例如maxTotal、maxIdle、minIdle等,也可以擴充套件自定義設定;
private final LinkedBlockingDeque<PooledObject<T>> idleObjects; public GenericObjectPool(final PooledObjectFactory<T> factory,final GenericObjectPoolConfig<T> config) { idleObjects = new LinkedBlockingDeque<>(config.getFairness()); }
LinkedBlockingDeque支援在佇列的首尾操作元素,例如新增和移除等;操作需要通過主鎖進行加鎖,並且基於兩個狀態鎖進行共同作業;
// 隊首節點 private transient LinkedBlockingDeque.Node<E> first; // 隊尾節點 private transient LinkedBlockingDeque.Node<E> last; // 主鎖 private final InterruptibleReentrantLock lock; // 非空狀態鎖 private final Condition notEmpty; // 未滿狀態鎖 private final Condition notFull;
關於連結串列和佇列的特點,在之前的文章中有單獨分析過,此處的原始碼在JDK的容器中也很常見,這裡不在贅述,物件池的整個構造有大致輪廓之後,下面再來細看物件的管理邏輯。
建立一個新物件並且放入池中,通常應用在需要預載入的場景中;涉及到兩個核心操作:工廠建立物件,物件池化管理;
public void GenericObjectPool.addObject() throws Exception ;
public T GenericObjectPool.borrowObject(final long borrowMaxWaitMillis) throws Exception ;
首先從佇列中獲取物件;如果沒有獲取到,呼叫工廠建立方法,之後池化管理;物件獲取之後會改變狀態為ALLOCATED
使用中;最後經過工廠的確認,完成物件獲取動作;
public void GenericObjectPool.returnObject(final T obj) ;
歸還物件的時候,首先轉換為池化物件和標記RETURNING
狀態;經過多次校驗判斷,如果失敗則銷燬該物件,並重新維護物件池中可用的空閒物件;最終物件被標記為空閒狀態,如果不超出最大空閒數,則物件被放到佇列的某一端;
關於池化物件的狀態在PooledObjectState
類中有列舉和描述,在圖中只是對部分幾個狀態流轉做示意,更多細節可以參考狀態類;
可以參考在上述案例中使用到的DefaultPooledObject
預設池化物件類中相關方法,結合狀態列舉,可以理解不同狀態之間的校驗和轉換。
Lettuce作為Redis高階的使用者端元件,通訊層使用Netty元件,並且是執行緒安全,支援同步和非同步模式,支援叢集和哨兵模式;作為當下專案中常用的設定,其底層物件池基於common-pool2
元件。
基於如下設定即表示採用Lettuce元件,其中涉及到池的幾個引數設定:最小空閒、最大活躍、最大空閒;這裡可以對比GenericObjectPoolConfig中的設定:
spring: redis: host: ${REDIS_HOST:127.0.0.1} lettuce: pool: min-idle: 10 max-active: 100 max-idle: 100
圍繞物件池的特點,自然去追尋原始碼中關於:設定、工廠、物件幾個核心的角色類;從上述設定引數切入,可以很容易發現如下幾個類:
// 連線設定 class LettuceConnectionConfiguration extends RedisConnectionConfiguration { private static class PoolBuilderFactory { // 構建物件池設定 private GenericObjectPoolConfig<?> getPoolConfig(RedisProperties.Pool properties) { GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>(); config.setMaxTotal(properties.getMaxActive()); config.setMaxIdle(properties.getMaxIdle()); config.setMinIdle(properties.getMinIdle()); return config; } } }
這裡將組態檔中Redis的相關引數,構建到GenericObjectPoolConfig類中,即設定載入過程;
class LettucePoolingConnectionProvider implements LettuceConnectionProvider { // 物件池核心角色 private final GenericObjectPoolConfig poolConfig; private final BoundedPoolConfig asyncPoolConfig; private final Map<Class<?>, GenericObjectPool> pools = new ConcurrentHashMap(32); LettucePoolingConnectionProvider(LettuceConnectionProvider provider, LettucePoolingClientConfiguration config) { this.poolConfig = clientConfiguration.getPoolConfig(); this.asyncPoolConfig = CommonsPool2ConfigConverter.bounded(this.config); } }
在構造方法中獲取物件池的設定資訊,這裡並沒有直接範例化池物件,而是採用ConcurrentHashMap容器來動態維護;
class LettucePoolingConnectionProvider implements LettuceConnectionProvider { // 獲取Redis連線 public <T extends StatefulConnection<?, ?>> T getConnection(Class<T> connectionType) { GenericObjectPool pool = (GenericObjectPool)this.pools.computeIfAbsent(); StatefulConnection<?, ?> connection = (StatefulConnection)pool.borrowObject(); } // 釋放Redis連線 public void release(StatefulConnection<?, ?> connection) { GenericObjectPool<StatefulConnection<?, ?>> pool = (GenericObjectPool)this.poolRef.remove(connection); } }
在獲取池物件時,如果不存在則根據相關設定建立池物件,並維護到Map容器中,然後從池中借用Redis連線物件;釋放物件時首先判斷物件所屬的池,將物件歸還到相應的池中。
最後總結,本文從物件池的一個簡單案例切入,主要分析common-pool2
元件關於:池、工廠、設定、物件管理幾個角色的原始碼邏輯,並且參考其在Redis中的實踐,只是冰山一角,像這種通用型並且應用範圍廣的元件,很值得時常去讀一讀原始碼,真的令人驚歎其鬼斧天工的設計。
應用倉庫:
https://gitee.com/cicadasmile/butte-flyer-parent
元件封裝:
https://gitee.com/cicadasmile/butte-frame-parent
到此這篇關於基於Apache元件分析物件池原理的文章就介紹到這了,更多相關Apache元件分析物件池原理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45