掌握JedisPoolConfig参数配置,学会调优技能

点击上方☝Java编程技术乐园,轻松关注!及时获取有趣有料的技术文章做一个积极的人编码、改bug、提升自己我有一个乐园,面向编程,春暖花开!你好,JedisPoolConfigJava中…

大家好,又见面了,我是全栈君。

点击上方☝Java编程技术乐园,轻松关注!

及时获取有趣有料的技术文章

做一个积极的人
编码、改bug、提升自己

我有一个乐园,面向编程,春暖花开!

掌握JedisPoolConfig参数配置,学会调优技能

你好,JedisPoolConfig

Java中使用Jedis作为连接Redis的工具。在使用Jedis的也可以配置JedisPool连接池,JedisPool配置参数大部分是由JedisPoolConfig的对应项来赋值的。本文简单总结几个常用的配置,然后通过源码(版本jedis-3.1.0)的角度让你理解配置这些参数的原理。

首先了解一下池化((对象池、数据库连接池、线程池等等))的一些思想和好处。方便后面对JedisPoolConfig的配置的理解。

池化的基本思想

1、可以在初始化的时候创建一些对象,当有需要使用的时候不直接从池中获取,提高响应速度;

2、使用过的对象不进行销毁,保存起来,等下一次需要对象的时候,拿出来重复使用,减少频繁创建对象所造成的开销;

3、创建的对象统一保存,方面管理和维护。

池化好处总结

1、提高响应的速度

2、降低资源的消耗

3、方便管理和维护

JedisPoolConfig配置说明

类图和源码解析

首先看一下类图:

掌握JedisPoolConfig参数配置,学会调优技能

BaseGenericObjectPool:封装公共的配置的参数。

private long maxWaitMillis = DEFAULT_MAX_WAIT_MILLIS;
    // DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 30L
    private long minEvictableIdleTimeMillis =
            DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;


    // DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L
     private long timeBetweenEvictionRunsMillis =
            DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;


    // DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3
    private int numTestsPerEvictionRun =
            DEFAULT_NUM_TESTS_PER_EVICTION_RUN;


    // DEFAULT_TEST_ON_CREATE = false
    private boolean testOnCreate = DEFAULT_TEST_ON_CREATE;
    // DEFAULT_TEST_ON_BORROW = false
    private boolean testOnBorrow = DEFAULT_TEST_ON_BORROW;
    // DEFAULT_TEST_ON_RETURN = false
    private boolean testOnReturn = DEFAULT_TEST_ON_RETURN;
    // DEFAULT_TEST_WHILE_IDLE = false
    private boolean testWhileIdle = DEFAULT_TEST_WHILE_IDLE;
    //...
}

GenericObjectPoolConfig:继承BaseGenericObjectPool,内部代码很简单,封装了GenericObjectPool的配置。主要是maxTotalmaxIdleminIdle。 此类不是线程安全的;它仅用于提供创建池时使用的属性。在创建单例的JedisPool 使用JedisPoolConfig需要注意线程安全问题,下面会有个demo介绍创建单例JedisPool

public class GenericObjectPoolConfig<T> extends BaseObjectPoolConfig<T> {


    /**
     * The default value for the {@code maxTotal} configuration attribute.
     * @see GenericObjectPool#getMaxTotal()
     */
    public static final int DEFAULT_MAX_TOTAL = 8;
    // ...


    // DEFAULT_MAX_TOTAL = 8
    private int maxTotal = DEFAULT_MAX_TOTAL;
    // DEFAULT_MAX_IDLE = 8
    private int maxIdle = DEFAULT_MAX_IDLE;
    // DEFAULT_MIN_IDLE = 0
    private int minIdle = DEFAULT_MIN_IDLE;
    // ...
}

JedisPoolConfig继承了上面的优良基因,然后又对其他的几个设置属性重新设值。

为了方便使用,Jedis提供了JedisPoolConfig,它继承了GenericObjectPoolConfig在空闲检测上的一些设置。

public class JedisPoolConfig extends GenericObjectPoolConfig {
  public JedisPoolConfig() {
    // defaults to make your life with connection pool easier :)
    setTestWhileIdle(true);
    setMinEvictableIdleTimeMillis(60000);
    setTimeBetweenEvictionRunsMillis(30000);
    setNumTestsPerEvictionRun(-1);
  }
}

配置参数解析

JedisPoolConfig中可以能够配置的参数有很多,连接池实现依赖apache 的commons-pool2。上面源码也大致列举了一些配置参数,下面在详细说明一下。

把池理解为工厂,池中的实例理解为工人,如下图,这样池中的很多参数理解起来就比较容易了。

掌握JedisPoolConfig参数配置,学会调优技能

Jedis连接就是连接池中JedisPool管理的资源,JedisPool保证资源在一个可控范围内,并且保障线程安全。使用合理的GenericObjectPoolConfig配置能够提升Redis的服务性能,降低资源开销。下列两表将对一些重要参数进行说明,并提供设置建议。

参数 说明 默认值 建议
maxTotal 资源池中的最大连接数 8 参见关键参数设置建议
maxIdle 资源池允许的最大空闲连接数 8 参见关键参数设置建议
minIdle 资源池确保的最少空闲连接数 0 参见关键参数设置建议
blockWhenExhausted 当资源池用尽后,调用者是否要等待。只有当值为true时,下面的maxWaitMillis才会生效。 true 建议使用默认值。
maxWaitMillis 当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)。 -1(表示永不超时) 不建议使用默认值。
testOnBorrow 向资源池借用连接时是否做连接有效性检测(ping)。检测到的无效连接将会被移除。 false 业务量很大时候建议设置为false,减少一次ping的开销。
testOnReturn 向资源池归还连接时是否做连接有效性检测(ping)。检测到无效连接将会被移除。 false 业务量很大时候建议设置为false,减少一次ping的开销。
jmxEnabled 是否开启JMX监控 true 建议开启,请注意应用本身也需要开启。

空闲Jedis对象检测由下列四个参数组合完成,testWhileIdle是该功能的开关。

名称 说明 默认值 建议
testWhileIdle 是否开启空闲资源检测。 false true
timeBetweenEvictionRunsMillis 空闲资源的检测周期(单位为毫秒) -1(不检测) 建议设置,周期自行选择,也可以默认也可以使用下方JedisPoolConfig 中的配置。
minEvictableIdleTimeMillis 资源池中资源的最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除。 180000(即30分钟) 可根据自身业务决定,一般默认值即可,也可以考虑使用下方JeidsPoolConfig中的配置。
numTestsPerEvictionRun 做空闲资源检测时,每次检测资源的个数。 3 可根据自身应用连接数进行微调,如果设置为 -1,就是对所有连接做空闲监测。

说明 可以在org.apache.commons.pool2.impl.BaseObjectPoolConfig中查看全部默认值。

关键参数设置建议

maxTotal(最大连接数)

想合理设置maxTotal(最大连接数)需要考虑的因素较多,如:

•业务希望的Redis并发量;•客户端执行命令时间;•Redis资源,例如nodes (如应用个数等) * maxTotal不能超过Redis的最大连接数;•资源开销,例如虽然希望控制空闲连接,但又不希望因为连接池中频繁地释放和创建连接造成不必要的开销。

假设一次命令时间,即borrow|return resource加上Jedis执行命令 ( 含网络耗时)的平均耗时约为1ms,一个连接的QPS大约是1000,业务期望的QPS是50000,那么理论上需要的资源池大小是50000 / 1000 = 50。

但事实上这只是个理论值,除此之外还要预留一些资源,所以maxTotal可以比理论值大一些。这个值不是越大越好,一方面连接太多会占用客户端和服务端资源,另一方面对于Redis这种高QPS的服务器,如果出现大命令的阻塞,即使设置再大的资源池也无济于事。

maxIdle与minIdle

maxIdle实际上才是业务需要的最大连接数,maxTotal 是为了给出余量,所以 maxIdle 不要设置得过小,否则会有new Jedis(新连接)开销,而minIdle是为了控制空闲资源检测。

连接池的最佳性能是maxTotal=maxIdle,这样就避免了连接池伸缩带来的性能干扰。但如果并发量不大或者maxTotal设置过高,则会导致不必要的连接资源浪费。

您可以根据实际总QPS和调用Redis的客户端规模整体评估每个节点所使用的连接池大小。

使用监控获取合理值

在实际环境中,比较可靠的方法是通过监控来尝试获取参数的最佳值。可以考虑通过JMX等方式实现监控,从而找到合理值。

上面参数配置:JedisPool资源池优化[1]

创建JedisPool代码

// volatile 修饰
private static volatile JedisPool jedisPool = null;


private JedisPoolUtils(){
}


public static JedisPool getJedisPoolInstance() {
    // 使用双重检查创建单例
    if(null == jedisPool) {
        synchronized (JedisPoolUtils.class) {
            if(null == jedisPool) {
                JedisPoolConfig poolConfig = new JedisPoolConfig();
                poolConfig.setMaxTotal(10);
                poolConfig.setMaxIdle(10);
                poolConfig.setMinIdle(2);
                poolConfig.setMaxWaitMillis(30*1000);
                poolConfig.setTestOnBorrow(true);
                poolConfig.setTestOnReturn(true);
                poolConfig.setTimeBetweenEvictionRunsMillis(10*1000);
                poolConfig.setMinEvictableIdleTimeMillis(30*1000);
                poolConfig.setNumTestsPerEvictionRun(-1);
                jedisPool = new JedisPool(poolConfig,"localhost",6379);
            }
        }
    }
    return jedisPool;
}

实例创建和释放大致流程解析

掌握JedisPoolConfig参数配置,学会调优技能

根据流程进行源码解析

创建过程

使用pool.getResource()进行Jedis实例的创建。

//org.apache.commons.pool2.impl.GenericObjectPool#borrowObject(long)
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {


    final boolean blockWhenExhausted = getBlockWhenExhausted();


    PooledObject<T> p = null;
    boolean create;
    final long waitTime = System.currentTimeMillis();


    while (p == null) {
        create = false;
        // 从空闲队列中获取
        p = idleObjects.pollFirst();
        if (p == null) {
            // 创建实例
            p = create();
            if (p != null) {
                create = true;
            }
        }
        // 吃资源是否耗尽
        if (blockWhenExhausted) {
            if (p == null) {
                // 等待时间小于0
                if (borrowMaxWaitMillis < 0) {
                    p = idleObjects.takeFirst();
                } else {
                    p = idleObjects.pollFirst(borrowMaxWaitMillis,
                                              TimeUnit.MILLISECONDS);
                }
            }
            if (p == null) {
                throw new NoSuchElementException(
                    "Timeout waiting for idle object");
            }
        } else {
            if (p == null) {
                throw new NoSuchElementException("Pool exhausted");
            }
        }
        if (!p.allocate()) {
            p = null;
        }


        if (p != null) {
            try {
                // 重新初始化要由池返回的实例。
                factory.activateObject(p);
            } catch (final Exception e) {


            }
        }
    }
    updateStatsBorrow(p, System.currentTimeMillis() - waitTime);


    return p.getObject();
}


释放过程

从Jedis3.0版本后pool.returnResource()遭弃用,官方重写了Jedis的close方法用以代替;官方建议应用redis.clients.jedis#Jedis的close方法进行资源回收,官方代码如下:

 @Override
  public void close() {
    if (dataSource != null) {
      JedisPoolAbstract pool = this.dataSource;
      this.dataSource = null;
      if (client.isBroken()) {
        pool.returnBrokenResource(this);
      } else {
        pool.returnResource(this);
      }
    } else {
      super.close();
    }
  }

这里主要看:pool.returnResource(this);

//org.apache.commons.pool2.impl.GenericObjectPool#returnObject
public void returnObject(final T obj) {
    // 获取要释放的实例对象
    final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));


    if (p == null) {
        if (!isAbandonedConfig()) {
            throw new IllegalStateException(
                "Returned object not currently part of this pool");
        }
        return; // Object was abandoned and removed
    }
    // 将对象标记为返回池的状态。
    markReturningState(p);


    final long activeTime = p.getActiveTimeMillis();


    // 这里就和上面配置的参数有关系,释放的时候是否做连接有效性检测(ping)
    if (getTestOnReturn() && !factory.validateObject(p)) {
        try {
            destroy(p);
        } catch (final Exception e) {
            swallowException(e);
        }
        try {
            ensureIdle(1, false);
        } catch (final Exception e) {
            swallowException(e);
        }
        updateStatsReturn(activeTime);
        return;
    }


    // 检查空闲对象,如果最大空闲对象数小于当前idleObjects大小,则销毁
    final int maxIdleSave = getMaxIdle();
    if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
        try {
            destroy(p);
        } catch (final Exception e) {
            swallowException(e);
        }
    } else {
        // 否则加入到空闲队列中,空闲队列是一个双端队列
        // getLifo 也和配置的参数有关,默认True
        if (getLifo()) {
            // last in first out,加到队头
            idleObjects.addFirst(p);
        } else {
            // first in first out ,加到队尾
            idleObjects.addLast(p);
        }
    }
    updateStatsReturn(activeTime);
}

上面创建和释放删除了一些代码,具体完整代码都是在GenericObjectPool类中。

小结,后悔有期

看完本文,应该大致对JedisPoolConfig有了一定的了解,指定里面的一些配置参数,并且能够基本的参数调优,以及实例资源的创建和释放的过程。

如果感谢兴趣的伙伴可以下载Jedis的源码进行阅读和学习,掌握了JedisPoolConfig的配置,其他池化框架的配置也是大同小异,举一反三!江湖不远,后会有期!

References

[1] JedisPool资源池优化: https://www.alibabacloud.com/help/zh/doc-detail/98726.htm

推荐阅读:

Redis的模糊查询在生产环境出现严重的性能问题

避免产生事故,应如何访问 redis 中的海量数据?

掌握JedisPoolConfig参数配置,学会调优技能

掌握JedisPoolConfig参数配置,学会调优技能

掌握JedisPoolConfig参数配置,学会调优技能欢迎长按下图关注公众号掌握JedisPoolConfig参数配置,学会调优技能

掌握JedisPoolConfig参数配置,学会调优技能

后台回复【秘籍】,获取珍藏的武功秘籍!

一起来修炼掌握JedisPoolConfig参数配置,学会调优技能

掌握JedisPoolConfig参数配置,学会调优技能

黯然~销魂 掌握JedisPoolConfig参数配置,学会调优技能

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/120993.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Visdom使用

    Visdom使用这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML图表FLowchart流程图导出与导入导出导入欢迎使用Ma…

  • 点击轮播图片左右button,实现轮播效果

    点击轮播图片左右button,实现轮播效果

  • JavaScript中数组Array方法详解[通俗易懂]

    JavaScript中数组Array方法详解[通俗易懂]ECMAScript3在Array.prototype中定义了一些很有用的操作数组的函数,这意味着这些函数作为任何数组的方法都是可用的。1、Array.join()方法Array.join()方

  • 利用SecureCRTPortable远程连接虚拟机

    利用SecureCRTPortable远程连接虚拟机利用SecureCRTPortable远程连接虚拟机1.出现的问题​ 打开SecureCRTP之后,输入正确的ip地址却无法连接到虚拟机。​ 2.解决调试​ 1.通过windows的cmd去ping虚拟机,同样还是ping不通2.查看网络适配器中VMnet8(该网卡是作为虚拟机与本机交互的一个网卡)3.错误原因:网卡中的ip地址与虚拟机中的地址相同,网关与虚拟机不相同​ 更改网卡中的ip地址使得…

  • 阿里云社区之新的开始

    阿里云社区之新的开始

  • dropdownlist控件绑定数据库_凡科可以绑定数据库吗

    dropdownlist控件绑定数据库_凡科可以绑定数据库吗DropDownList1.DataSource=myReader;//myReader为ExecuteReader()的结果集DropDownList1.DataTextField(DataValueField)=”填充在myReader中的数据集,用哪个列填充就写哪个列名就ok了”;DropDownList1.DataBind();aspx页面上有三个DropDownLi

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号