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); } }