池化 - Apache Commons Pool

来源:http://www.cnblogs.com/newcooler/archive/2016/08/30/5822965.html
-Advertisement-
Play Games

對於那些創建耗時較長,或者資源占用較多的對象,比如網路連接,線程之類的資源,通常使用池化來管理這些對象,從而達到提高性能的目的。比如資料庫連接池(c3p0, dbcp), java的線程池 ExecutorService.Apache Commons Pool提供一套池化規範介面,以及實現通用邏輯, ...


  對於那些創建耗時較長,或者資源占用較多的對象,比如網路連接,線程之類的資源,通常使用池化來管理這些對象,從而達到提高性能的目的。比如資料庫連接池(c3p0, dbcp), java的線程池 ExecutorService.Apache Commons Pool提供一套池化規範介面,以及實現通用邏輯,我們只需要要實現其抽象出來的方法就可以。Commons Pool主要有以下幾個對象   PooledObject:這個就是前面所說需要池化的資源,被池化的對象可以抽離出共有屬性,如,創建時間,狀態,最近一次使用時間等   PooledObjectFactory: 對象工廠,負責對PooledOjbect的創建,狀態驗證,銷毀之類的工作   ObjectPool: 對象池,它是負責和對象使用者直接打交道的, 對使用者提供獲取對象,返還對象介面   英文不太好的同學可能被這幾個對象的命名搞暈,其實世間萬物的道理都是想通的。如果把圖書館的書比作PooledObject 的話,那麼圖書館就是ObjectPool,圖書館為了管理書,對書添加了入庫時間,書借存狀態等用於管理的屬性。圖書館(ObjectPool)對借書人提供借書,還書的服務(即介面)。而書(PooledObject )的印刷,質量檢驗,回收(有點不現實)等實實在在的工作還是得交給印刷廠(PooledObjectFactory )來做。其流程關係如下:   下麵看看如何使用Commons Pool如何實現自己的對象池。創建自己的對象池大致需要以下工作,   1. 首先你已經編寫好了你的資源對象(這部分不屬於池化內容),之後編寫實現apache的PooledObjectFactory<T>介面的Factory類,這是編寫自己對象池最主要的工作。你的Factory可能需要添加一些用於池化對象的初始化 ,池化對象的驗證等參數作為成員變數。   2. 編寫自己的Pool類,讓其繼承或者內部引用apache的GenericObjectPool<T>,GenericObjectPool實現了ObjectPool介面,已經封裝了對池化對象的生命周期管理邏輯   3. 可選部分,繼承apache的GenericObjectPoolConfig,重寫構造器,添加一些適合自己業務場景的初始化參數。   我們以Jedis的源碼為例,學習它的實現。我們先看下使用JedisPool操作Redis的簡單例子
package com.eg.test.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class TestPool {
     public static void main(String[] args) {
        //JedisPoolConfig繼承apache的GenericObjectPoolConfig,配置Pool的相關參數如下:
        JedisPoolConfig config = new JedisPoolConfig(); 
        //如果賦值為-1,則表示不限制;如果pool已經分配了maxActive個jedis實例,則此時pool的狀態為exhausted(耗盡)。 
        config.setMaxTotal(500); 
        //控制一個pool最多有多少個狀態為idle(空閑的)的jedis實例。 
        config.setMaxIdle(5); 
        //表示當borrow(引入)一個jedis實例時,最大的等待時間,如果超過等待時間,則直接拋出JedisConnectionException; 
        config.setMaxWaitMillis(30000);; 
        //在borrow一個jedis實例時,是否提前進行validate操作;如果為true,則得到的jedis實例均是可用的; 
        config.setTestOnBorrow(true); 
       
        JedisPool pool = new JedisPool(config, "192.168.2.191", 8888); 
        //從pool中獲取對象
        Jedis jedis = pool.getResource(); 
        String value = jedis.get("someKey");
       
     }
}

  首先看JedisFactory的實現:

class JedisFactory implements PooledObjectFactory<Jedis> {
     private final AtomicReference<HostAndPort> hostAndPort = new AtomicReference<HostAndPort>();
     private final int connectionTimeout;
     private final int soTimeout;

     //省略構造函數,都是一些初始化成員變數的操作

     @Override
     public void activateObject(PooledObject<Jedis> pooledJedis) throws Exception {
           final BinaryJedis jedis = pooledJedis.getObject();
           if (jedis.getDB() != database) {
                jedis.select(database);
           }
     }

     @Override
     public void destroyObject(PooledObject<Jedis> pooledJedis) throws Exception {
           final BinaryJedis jedis = pooledJedis.getObject();
           if (jedis.isConnected()) {
                try {
                     try {
                           jedis.quit();
                     } catch (Exception e) {
                     }
                     jedis.disconnect();
                } catch (Exception e) {
                }
           }
     }

     @Override
     public PooledObject<Jedis> makeObject() throws Exception {
           final HostAndPort hostAndPort = this.hostAndPort.get();
           final Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, soTimeout, ssl,
                   sslSocketFactory, sslParameters, hostnameVerifier);
           try {
                jedis.connect();
                if (null != this.password) {
                     jedis.auth(this.password);
                }
                if (database != 0) {
                     jedis.select(database);
                }
                if (clientName != null) {
                     jedis.clientSetname(clientName);
                }
           } catch (JedisException je) {
                jedis.close();
                throw je;
           }
           return new DefaultPooledObject<Jedis>(jedis);
     }
     @Override
     public void passivateObject(PooledObject<Jedis> pooledJedis) throws Exception {
           // TODO maybe should select db 0? Not sure right now.
     }
     @Override
     public boolean validateObject(PooledObject<Jedis> pooledJedis) {
           final BinaryJedis jedis = pooledJedis.getObject();
           try {
                HostAndPort hostAndPort = this.hostAndPort.get();
                String connectionHost = jedis.getClient().getHost();
                int connectionPort = jedis.getClient().getPort();
                return hostAndPort.getHost().equals(connectionHost) && hostAndPort.getPort() == connectionPort
                        && jedis.isConnected() && jedis.ping().equals("PONG");
           } catch (final Exception e) {
                return false;
           }
     }
}

  我們看到JedisFactory代碼較少,但是邏輯很清晰。該Factory將作為ObjectPool的成員變數,其中四個重寫的方法被ObjectPool管理對象生命周期的時候調用。makeobject()方法負責創建Jedis實例,成功調用connect()方法建立有狀態的socket連接之後,返回一個包裝了jedis的DefaultPooledObject對象,DefaultPooledObject實現了關於統計池化對象狀態信息的PooledObject介面。validateObject()方法用於對對象狀態的檢驗,Jedis對象的狀態通過socket的ping-pong來驗證連接是否正常。destroyObject()方法用來銷毀對象,Jedis對象將會斷開連接,回收資源。

  再看JedisPool的實現,由於JedisPool繼承Pool<T>,所以我們主要看Pool<T>的部分代碼:

public abstract class Pool<T> implements Closeable {
     protected GenericObjectPool<T> internalPool;

     public Pool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory<T> factory) {
           initPool(poolConfig, factory);
     }

     public boolean isClosed() {
           return this.internalPool.isClosed();
     }
     public void initPool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory<T> factory) {
           if (this.internalPool != null) {
                try {
                     closeInternalPool();
                } catch (Exception e) {
                }
           }
           this.internalPool = new GenericObjectPool<T>(factory, poolConfig);
     }
     public T getResource() {
           try {
                return internalPool.borrowObject();
           } catch (NoSuchElementException nse) {
                throw new JedisException("Could not get a resource from the pool", nse);
           } catch (Exception e) {
                throw new JedisConnectionException("Could not get a resource from the pool", e);
           }
     }
     protected void returnResourceObject(final T resource) {
           if (resource == null) {
                return;
           }
           try {
                internalPool.returnObject(resource);
           } catch (Exception e) {
                throw new JedisException("Could not return the resource to the pool", e);
           }
     }

     public void addObjects(int count) {
           try {
                for (int i = 0; i < count; i++) {
                     this.internalPool.addObject();
                }
           } catch (Exception e) {
                throw new JedisException("Error trying to add idle objects", e);
           }
     }
}
  JedisPool通過內部引用GenericObjectPool,包裝其介面的裝飾者模式,相比繼承來說這種模式更加靈活。JedisPool的構造方法需要將JedisFactory以及JedisPoolConfig創建標準的ObjectPool作為自己的成員變數。所以pool.getResource()方法的背後還是調用PoolObject.borrowObject()。     最後我們稍微看下JedisPoolConfig,只是做了一些預初始化參數的工作。
public class JedisPoolConfig extends GenericObjectPoolConfig {
     public JedisPoolConfig() {
           // defaults to make your life with connection pool easier :)
           setTestWhileIdle(true);
           setMinEvictableIdleTimeMillis(60000);
           setTimeBetweenEvictionRunsMillis(30000);
           setNumTestsPerEvictionRun(-1);
     }
}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 易語言MySql註冊登錄 用到的主要易語言命令: 連接MySql (, , , , ) 執行SQL語句 (, ) 取記錄集 () 讀欄位值 (, , ) 釋放記錄集 () 斷開MySql () 命令介紹: ①、連接MySql (, , , , ):顧名思義,這條命令是用來連接你的MySql資料庫的, ...
  • 安裝 phoenix framework 安裝 phoenix 很簡單: 安裝之後,mix 的子命令中就多了 phoenix 相關的內容了。 phoenix 工程介紹 接下來,我們創建一個 phoenix 的工程。 phoenix 是個完整的 web 框架,包括 controller,view以及m ...
  • * ResultSet 結果集:封裝了使用JDBC 進行查詢的結果 * 1. 調用Statement 對象的 executeQuery(sql) 方法可以得到結果集 * 2. ResultSet 返回的實際上就是一張數據表,有一個指針指向數據表的第一行的前面, * 可以調用next()方法檢測下一行 ...
  • C 語言基本數據類型:字元型(char)整形(short, int, long)浮點型(float, double) 註:如下類型位元組數指一般情況,不同的平臺會有所不同,具體平臺可以用sizeof 關鍵字測試一下. -2147483648~~2147483647 0~~4294967295 -3.4 ...
  • 聲明:此文章轉載自 http://my.oschina.net/goldenshaw/blog/304493 許多時候,字元集與編碼這兩個概念常被混為一談,但兩者是有差別的,作為深入理解的第一步,首先要明確: 字元集與字元集編碼是兩個不同層面的概念 charset是character set的簡寫, ...
  • 說明:iOS 原生 Edit 透明框 適用:Berlin Firemonkey 方法:在 StyleLookup 輸入 transparentedit 效果: 如果有圖片 Image 在這二個 Edit 的上方,效果如下: 說明:iOS 原生 Edit 設定框色 適用:Berlin Firemonk ...
  • 一、首先要搞明白的一些事情。 1.從客戶端來看,需要搞明白: (1)要發送什麼樣格式的 JSON 數據才能被伺服器端的 SpringMVC 很便捷的處理,怎麼才能讓我們寫更少的代碼,如何做好 JSON 數據和實體之間的對應。 (2)如何組織這些發送的數據。 2.從伺服器端來看,需要搞明白: (1)S ...
  • 1、加密演算法 為了網路通訊中的報文安全,一般需要對報文進行加密,目前常用的加密演算法有: 非對稱加密演算法:又稱公鑰加密演算法,如RSA、DSA/DSS,最常用的就是RSA演算法(演算法公開,可自行百度瞭解演算法細節),演算法產生一個公鑰一個私鑰,用公鑰加密的報 文只能用私鑰解密,用私鑰加密的報文只能用公鑰解密; ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...