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

 

赞 (0) 评论 分享 ()