JedisPool的实现

看本文前可以先参看

小芳芳:Apache Common Pool2 对象池应用浅析zhuanlan.zhihu.com图标

此文介绍了Apache Common Pool2对象池;


其中也提到了jedis中也使用到了Apache Common Pool2对象池的问题

以我们项目中的应用为例:

为了使用redis ,引入了jedis-2.8.1.jar和commons-pool2-2.0.jar

因为Jedis中的Jedis对象池就是使用了Common Pool2的方式实现的。

实现逻辑如下

public void void init() throws Exception
{
    final JedisPoolConfig config = new JedisPoolConfig();
    config.setMaxTotal(一个int值)
    config.setMaxIdle(一个int值)
    config.setMaxWaitMillis(一个int值)
    this.jedisPool = new JedisPool(config,string类型的ip,int类型的port,
    Protocol.DEFAULT_TIMEOUT,password)
}

核心接口

Common Pool2 的核心部分比较简单,围绕着三个基础接口和相关的实现类来实现:

  • ObjectPool:对象池,持有对象并提供取/还等方法。
  • PooledObjectFactory:对象工厂,提供对象的创建、初始化、销毁等操作,由 Pool 调用。一般需要使用者自己实现这些操作。
  • PooledObject:池化对象,对池中对象的封装,封装对象的状态和一些其他信息。

对应到Jedis当中

redis.client.jedis.JedisFactory 就是实现了PooledObjectFactory接口的Jedis工厂类;

该工厂类实现了Jedis对象的创建、初始化、销毁等操作;

看我们上面的实现逻辑发现我们创建JedisPool的时候并没有指定JedisFactory对象,那是因为在JedisPool的构造方法中默认使用了JedisFactory类,所以没有暴露出接口;

具体我们来看:

接口的方法:

void activate Object(PooledObject<T> p)
Reinitialize an instance to be returned by the pool.

void destroy Object(PooledObject<T> p)
Destroys an instance no longer needed by the pool.

PooledObject<T> makeObject() 
Create an instance that can be served by the pool 
and wrap it in aPooledObjectto be managed by the pool.

void passivateObject(PooledObject<T> p) 
Uninitialize an instance to be returned to the idle object pool.

boolean validateObject(PooledObject<T> p)  
Ensures that the instance is safe to be returned by the pool.


我们来看下JedisFactory中的makeObject做了哪些事情:

  @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 (password != null) 
      {
        jedis.auth(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);
}

构建了一个Jedis对象,主要的参数有ip和port,自然是redis服务器的链接地址,还有就是connect超时时间,以及socketTimeout时间;


可看到的是创建了Jedis对象,且返回;


我们知道JedisPool中的getReasoure()方法返回的是Jedis对象,

那么getResource()方法又是怎么和JedisFactory中的makeObject()发生关系的呢?

我们知道JedisPool继承自Pool<T>

package redis.clients.jedis.util;

import java.io.Closeable;
import java.util.NoSuchElementException;

import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.exceptions.JedisExhaustedPoolException;

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

  /**
   * Using this constructor means you have to set and initialize the internalPool yourself.
   */
  public Pool() {
  }

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

  @Override
  public void close() {
    destroy();
  }

  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) {
      if (null == nse.getCause()) { // The exception was caused by an exhausted pool
        throw new JedisExhaustedPoolException(
            "Could not get a resource since the pool is exhausted", nse);
      }
      // Otherwise, the exception was caused by the implemented activateObject() or ValidateObject()
      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);
    }
  }

里面封装了GenericObjectPool对象,该对象是commons-pool2-2.0.jar中的核心类,该对象的构造方法有两个核心参数,一个是PooledObjectFactory<T> factory(这是个接口)一个是GenericObjectPoolConfig配置,这个核心类的初始化时在创建JedisPool时传入的,参见最上面的代码,而PooledObkectFactory对象则是JedisPool的构造方法默认实现的;

该对象的borrowObject方法最终调用到了factory的makeObject()方法;

我们来下看GenericObjectPool对象的borrowObject方法

public Object borrowObject() throws Exception {  
    Object value = null;  
    synchronized (this) {  
        if(!_pool.isEmpty()){  
            value = _pool.remove();  
        }  
          
    }         
    for(;;) {       
        //如果Pool中没有"对象",则根据相应的"耗尽"策略  
        if(value == null) {  
            switch(whenExhaustedAction) {  
                //如果耗尽,仍继续创建新"对象"  
                case WHEN_EXHAUSTED_GROW:  
                    value = _factory.makeObject();  
                    break;  
                //如果耗尽,则终止,此时以异常的方式退出.  
                case WHEN_EXHAUSTED_FAIL:  
                    throw new NoSuchElementException("Pool exhausted");  
                //如果耗尽,则阻塞,直到有"对象"归还  
                case WHEN_EXHAUSTED_BLOCK:  
                    try {  
                        synchronized (value) {  
                            if (value == null) {  
                                //maxWait为Config中指定的"最大等待时间"  
                                if(maxWait <= 0) {  
                                    latch.wait();  
                                } else {  
                                        latch.wait(waitTime);  
                                }  
                            } else {  
                                break;  
                            }  
                        }  
                    } catch(InterruptedException e) {         
                        //  
                        break;  
                    }  
                      
                default://  
            }  
        }  
  
        try {  
            _factory.activateObject(latch.getPair().value);  
            if(_testOnBorrow &&  
                    !_factory.validateObject(latch.getPair().value)) {  
                throw new Exception("ValidateObject failed");  
            }  
           return value;  
        }  
        catch (Throwable e) {  
            try {  
                _factory.destroyObject(latch.getPair().value);  
            } catch (Throwable e2) {  
                //  
            }  
        }  
    }  
}  


--------------------------

不使用JedisPool的话jedis的创建和网络连接时机是不同的,

使用JedisPool的时候,创建的同时就将网络连接准备好了,单使用Jedis的时候是在执行redis命令的时候才执行的。

具体可参看

郭无心:Jedis与Redis数据库的链接建立zhuanlan.zhihu.com图标


http://shift-alt-ctrl.iteye.com/blog/1917782shift-alt-ctrl.iteye.com

这篇文章介绍了Common Pool的实现细节

编辑于 2018-08-05

文章被以下专栏收录