前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Jedis源码解读-JedisPool

Jedis源码解读-JedisPool

作者头像
日薪月亿
发布2021-12-23 09:31:36
6470
发布2021-12-23 09:31:36
举报
文章被收录于专栏:技术探索技术探索

1. 什么是对象池

对于一个对象,其生命周期大致可以分为 创建 -> 使用 -> 销毁三大阶段,这个对象的时间是 T1(创建)+T2(使用)+T3(销毁), 对于创建N个对象都需要这个步骤的话,肯定很耗时并且消耗性能的。 官方对于对象池的解释是:

 将用过的对象保存起来,等下次需要这种对象的时候再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销,用于充当保存对象的”容器”对象,被称为”对象池”。

2. JedisPool的创建

Jedis的连接池是基于apache.common.pool2,因此jedisPool的实现都是基于Pool2。 关于pool2的源码文档可以参考 http://commons.apache.org/proper/commons-pool/

JedisPool是Pool抽象类的子类。

代码语言:javascript
复制
public class JedisPoolAbstract extends Pool<Jedis> {
...
}

其中JedisPool构造函数最终都是调用了Pool#initPool的方法

代码语言:javascript
复制
package redis.clients.jedis.util;
public abstract class Pool<T> implements Closeable {
  protected GenericObjectPool<T> internalPool;

  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) {
      }
    }
    # 实例化pool2中的GenericeObjectPool
    this.internalPool = new GenericObjectPool<T>(factory, poolConfig);
  }
  # 从JedisPool获取Jedis和释放Jedis实例, 
  public T getResource() {
    try {
      # 阅读源码位置 org.apache.commons.pool2.impl#borrowObject
      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);
    }
  }

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

initPool方法中有两个参数:一个GenericObjectPoolConfig配置项封装类,一个是 PooledObjectFactory工厂类获取到连接池后从连接池中获取jedis连接对象。 根据配置信息实例化pool2中的GenericeObjectPool这个对象池的管理者。

当调用 getResource 获取Jedis时, 实际上是Pool内部的internalPool调用borrowObject()拿到一个实例 ,而internalPool 这个 GenericObjectPool 又调用了 JedisFactory 的 makeObject() 来完成实例的生成 (在Pool中资源不够的时候)

Jedis是如何实例化的

我们先看下JedisFactory的源码

代码语言:javascript
复制
class JedisFactory implements PooledObjectFactory<Jedis> {
  @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);

  }
...
}

JedisFactory实现了pool2的PooledObjectFactory接口,池中对象创建和销毁的接口,交由业务方(就是文中的JedisFactory来实现)。 在JedisFactory#makeObject()方法中创建了Jedis对象。 Jedis继承自BinaryJedis,其有一个Client属性,Client是Connection的子类,Connection中有socket这个属性,也就是真正跟redis服务端创建连接的类,并且这个socket是个长连接。

redis.clients.jedis.Connection#connect

代码语言:javascript
复制
public void connect() {
    if (!isConnected()) {
      try {
        socket = new Socket();
        // ->@wjw_add
        socket.setReuseAddress(true);
        #建立长连接
        socket.setKeepAlive(true); // Will monitor the TCP connection is
        // valid
        socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to
        // ensure timely delivery of data
        socket.setSoLinger(true, 0); // Control calls close () method,
        // the underlying socket is closed
        // immediately
        // <-@wjw_add

        socket.connect(new InetSocketAddress(host, port), connectionTimeout);
        socket.setSoTimeout(soTimeout);
        ......
        outputStream = new RedisOutputStream(socket.getOutputStream());
        inputStream = new RedisInputStream(socket.getInputStream());
      } catch (IOException ex) {
        broken = true;
        throw new JedisConnectionException("Failed connecting to host " 
            + host + ":" + port, ex);
      }
    }
  }

从上面的源码可以看到,Jedis和网络连接是一一绑定的,假如redis对象被GC了,那么它的client和socket连接一并销毁。

客户端归还对象池

归还池的处理规则是由common-pool2来实现的,就是把jedis对象放到空闲队列中,如果队列满了,就将其直接销毁,销毁是在JedisFactory实现的 redis.clients.jedis.JedisFactory#destroyObject

代码语言:javascript
复制

@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) {

    }
  }

}

当主动关闭socket连接,在common-pool2中的GenericObjectPool也会把他从空闲池和总池中移除。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 什么是对象池
  • 2. JedisPool的创建
  • Jedis是如何实例化的
  • 客户端归还对象池
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档