我們在服務器開發的過程中,往往會有一些對象,它的創建和初始化需要的時間比較長,比如數據庫連接,網絡IO,大數據對象等。在大量使用這些對象時,如果不采用一些技術優化,就會造成一些不可忽略的性能影響。一種辦法就是使用對象池,每次創建的對象並不實際銷毀,而是緩存在對象池中,下次使用的時候,不用再重新創建,直接從對象池的緩存中取即可。為了避免重新造輪子,我們可以使用優秀的開源對象池化組件apache-common-pool2,它對對象池化操作進行了很好的封裝,我們只需要根據自己的業務需求重寫或實現部分接口即可,使用它可以快速的創建一個方便,簡單,強大對象連接池管理類。
下面先看看主要的幾個種要的接口和實現類:
- PooledObjectFactory<T>: 對象工廠,在需要的時候生成新的對象實例,並放入池中,一般使用抽象類BasePooledObjectFactory<T>,在GenericObjectPool中,有兩個我們會用到的方法:public Jedis create() throws Exception 創建對象,public PooledObject<T> wrap(T t) 封裝為池化對象。其它還有一些方法,可以查看下面的MyJedisFactory代碼。
- ObjectPool: 對象池,用於存儲對象,並管理對象的入池和出池。對象池的實現類是 GenericObjectPool<T>;在GenericObjectPool中,有兩個我們會用到的方法:public T borrowObject() throws Exception 從對象池中獲取一個對象,public void returnObject(T obj) 對象使用完之后,歸還到對象池,其它還有一些方法,比如關閉對象池,銷毀對象池等。
- BaseObjectPoolConfig: 池屬性,用於設置連接池的一些配置信息,比如最大池容量、超過池容量后的處理邏輯等。池屬性的實現類是:GenericObjectPoolConfig;
- Object: 池對象,由對象工廠負責創建,並放入到對象池中;需要使用時從對象池中取出,執行對象的業務邏輯,使用完后再放回對象池。
下面我們來用代碼實現jedis的連接池,類關系圖如下:
1,引入依賴
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.10.0</version> </dependency> <!-- 下面這個是為了使用jedis,但是我們不用它自帶的redisPool --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.1</version> </dependency>
2,創建MyJedisFactory類,繼承BasePooledObjectFactory<Jedis>
public class MyJedisFactory extends BasePooledObjectFactory<Jedis> { //計數,連接池內的對象個數 private AtomicInteger sum = new AtomicInteger(0); private static String HOST = "******"; private static int PORT = 6379; private static String PASSWORD = "****"; /** * 創造對象 * @return * @throws Exception */ @Override public Jedis create() throws Exception { System.out.println("創造了 " + sum.incrementAndGet() + "個對象"); Jedis jedis = new Jedis(HOST, PORT); jedis.auth(PASSWORD); return jedis; } /** * 破壞對象 * @param p * @throws Exception */ @Override public void destroyObject(PooledObject<Jedis> p) throws Exception { System.out.println("破壞" ); super.destroyObject(p); } /** * 封裝為池化對象 * @param jedis * @return */ @Override public PooledObject<Jedis> wrap(Jedis jedis) { return new DefaultPooledObject<Jedis>(jedis); } /** * 拿取時調用 * @param p * @throws Exception */ @Override public void activateObject(PooledObject<Jedis> p) throws Exception { super.activateObject(p); System.out.println("拿取" + sum.get()); } /** * 返還池子里時調用 * @param pooledObject * @throws Exception */ @Override public void passivateObject(PooledObject<Jedis> pooledObject) throws Exception{ super.passivateObject(pooledObject); System.out.println("返還" + sum.get()); } /** * * @param p * @return */ @Override public boolean validateObject(PooledObject<Jedis> p){ System.out.println("校驗" ); return super.validateObject(p); } }
3,創建MyJedisPool類,繼承GenericObjectPool<Jedis>
public class MyJedisPool extends GenericObjectPool<Jedis> { public MyJedisPool(PooledObjectFactory<Jedis> factory) { super(factory); } public MyJedisPool(PooledObjectFactory<Jedis> factory, GenericObjectPoolConfig config) { super(factory, config); } public MyJedisPool(PooledObjectFactory<Jedis> factory, GenericObjectPoolConfig config, AbandonedConfig abandonedConfig) { super(factory, config, abandonedConfig); } }
4,測試testPool類
public class testPool { public static void main(String[] args) { PooledObjectFactory<Jedis> fac = new MyJedisFactory(); GenericObjectPool<Jedis> pool = new MyJedisPool(fac,new GenericObjectPoolConfig()); int testCount = 20; CountDownLatch c = new CountDownLatch(testCount); for (int i = 0; i < testCount; i++) { new Thread(() -> { testPool(pool); c.countDown(); }).start(); } try { c.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("活躍數: " + pool.getNumActive()); System.out.println("空閑數: " + pool.getNumIdle()); } static void testPool(GenericObjectPool<Jedis> pool) { Jedis myJedis = null; try { // 從池中獲取對象 myJedis = pool.borrowObject(); // 使用對象 System.out.println(myJedis.get("*********")); } catch (Exception e) { try { // 出現錯誤將對象置為失效 pool.invalidateObject(myJedis); myJedis = null; } catch (Exception ex) { } } finally { try { if (null != myJedis) { // 使用完后必須 returnObject pool.returnObject(myJedis); } } catch (Exception e) { } } } }
下面我們看看運行結果: