zoukankan      html  css  js  c++  java
  • 池化

      对于那些创建耗时较长,或者资源占用较多的对象,比如网络连接,线程之类的资源,通常使用池化来管理这些对象,从而达到提高性能的目的。比如数据库连接池(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);
         }
    }
    Talking with giants
  • 相关阅读:
    windows下vagrant使用及工作环境配置
    使用docker搭建lnmp环境
    Docker常用命令
    [docker] 管理docker容器中的数据
    python初始化父类错误
    Linux--
    用少于2MB内存存下5百万个介于0到1千万之间的整数
    k路归并:数组、链表
    剑指offer新思路
    阿里电话面试
  • 原文地址:https://www.cnblogs.com/newcooler/p/5822965.html
Copyright © 2011-2022 走看看