zoukankan      html  css  js  c++  java
  • 使用JedisPool资源池操作Redis,并进行性能优化

     正文前先来一波福利推荐:

    福利一:

    百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。

    福利二:

    毕业答辩以及工作上各种答辩,平时积累了不少精品PPT,现在共享给大家,大大小小加起来有几千套,总有适合你的一款,很多是网上是下载不到。

    获取方式:

    微信关注 精品3分钟 ,id为 jingpin3mins,关注后回复   百万年薪架构师 ,精品收藏PPT  获取云盘链接,谢谢大家支持!

    ------------------------正文开始---------------------------

    一、使用方法

    -----------------------------------------
    private volatile static JedisPool pool = null; //本地测试
    private volatile static JedisSentinelPool sentinelPool = null;
    private static GenericObjectPoolConfig config = null;
    private static final int TIMEOUT = 10000; // 10秒

    static {
    config = new JedisPoolConfig();
    config.setMaxTotal(500);
    // 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
    config.setMaxIdle(5);
    // 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
    config.setMaxWaitMillis(1000 * 60 * 1000);
    // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
    config.setTestOnBorrow(false);
    }

    public static void init(String host, int port, String password) {
    if(pool == null) {
    synchronized(RedisUtils.class) {
    if(pool == null) {
    pool = new JedisPool(config, checkNotNull(host), checkNotNull(port), TIMEOUT,checkNotNull(password));
    }
    }
    }
    }
    -----------------------------------------
    -----------------------------------------
    //初始化调用
    RedisUtils.init(host, port, password);
    -----------------------------------------
    -----------------------------------------

    //获得 redis 资源 与 释放 redis 资源
    /**
    * 获取jedis资源
    *
    * @return
    */
    public static Jedis getJedis() {
    Jedis jedis = null;
    try {
    if(pool != null) {
    jedis = pool.getResource();
    } else {
    jedis = sentinelPool.getResource();
    }
    } catch (Throwable t) {
    logger.error("", t);
    }
    return jedis;
    }

    /**
    * 归还jedis资源
    *
    * @param jedis
    */
    @SuppressWarnings("deprecation")
    public static void returnResource(Jedis jedis) {
    try {
    if(pool != null) {
    pool.returnResource(jedis);
    } else {
    sentinelPool.returnResource(jedis);
    }
    } catch (Throwable t) {
    logger.error("", t);
    if(pool != null) {
    pool.returnBrokenResource(jedis);
    } else {
    sentinelPool.returnBrokenResource(jedis);
    }
    }
    }
    -----------------------------------------
    -----------------------------------------
    数据操作,可以使用 Pipeline 操作:
    Pipeline pipeline = jedis.pipelined();
    Response<Long> response = pipeline.sadd(key, member);
    pipeline.expireAt(key, expireTime);
    -----------------------------------------


    二、参数说明

    JedisPool保证资源在一个可控范围内,并且提供了线程安全,但是一个合理的GenericObjectPoolConfig配置能为应用使用Redis保驾护航,下面将对它的一些重要参数进行说明和建议:

    在当前环境下,Jedis连接就是资源,JedisPool管理的就是Jedis连接。

    1. 资源设置和使用

    序号参数名含义默认值使用建议
    1 maxTotal 资源池中最大连接数 8 设置建议见下节
    2 maxIdle 资源池允许最大空闲的连接数 8 设置建议见下节
    3 minIdle 资源池确保最少空闲的连接数 0 设置建议见下节
    4 blockWhenExhausted 当资源池用尽后,调用者是否要等待。只有当为true时,下面的maxWaitMillis才会生效 true 建议使用默认值
    5 maxWaitMillis 当资源池连接用尽后,调用者的最大等待时间(单位为毫秒) -1:表示永不超时 不建议使用默认值
    6 testOnBorrow 向资源池借用连接时是否做连接有效性检测(ping),无效连接会被移除 false 业务量很大时候建议设置为false(多一次ping的开销)。
    7 testOnReturn 向资源池归还连接时是否做连接有效性检测(ping),无效连接会被移除 false 业务量很大时候建议设置为false(多一次ping的开销)。
    8 jmxEnabled 是否开启jmx监控,可用于监控 true 建议开启,但应用本身也要开启

    2.空闲资源监测

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

    序号参数名含义默认值使用建议
    1 testWhileIdle 是否开启空闲资源监测 false true
    2 timeBetweenEvictionRunsMillis 空闲资源的检测周期(单位为毫秒) -1:不检测 建议设置,周期自行选择,也可以默认也可以使用下面JedisPoolConfig中的配置
    3 minEvictableIdleTimeMillis 资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除 1000 60 30 = 30分钟 可根据自身业务决定,大部分默认值即可,也可以考虑使用下面JeidsPoolConfig中的配置
    4 numTestsPerEvictionRun 做空闲资源检测时,每次的采样数 3 可根据自身应用连接数进行微调,如果设置为-1,就是对所有连接做空闲监测

    为了方便使用,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);
        }
    }
    复制代码

    所有默认值可以从org.apache.commons.pool2.impl.BaseObjectPoolConfig中看到。

    三、资源池大小(maxTotal)、空闲(maxIdle minIdle)设置建议

    1.maxTotal:最大连接数

    实际上这个是一个很难回答的问题,考虑的因素比较多:

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

    以一个例子说明,假设:

    • 一次命令时间(borrow|return resource + Jedis执行命令(含网络) )的平均耗时约为1ms,一个连接的QPS大约是1000
    • 业务期望的QPS是50000

    那么理论上需要的资源池大小是50000 / 1000 = 50个。但事实上这是个理论值,还要考虑到要比理论值预留一些资源,通常来讲maxTotal可以比理论值大一些。

    但这个值不是越大越好,一方面连接太多占用客户端和服务端资源,另一方面对于Redis这种高QPS的服务器,一个大命令的阻塞即使设置再大资源池仍然会无济于事。

    2. maxIdle minIdle

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

    连接池的最佳性能是maxTotal = maxIdle ,这样就避免连接池伸缩带来的性能干扰。但是如果并发量不大或者maxTotal设置过高,会导致不必要的连接资源浪费。
    可以根据实际总OPS和调用redis客户端的规模整体评估每个节点所使用的连接池。

    3.监控

    实际上最靠谱的值是通过监控来得到“最佳值”的,可以考虑通过一些手段(例如jmx)实现监控,找到合理值。

    四、常见问题

    1.资源“不足"

    redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    …
    Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)

    或者

    redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    …
    Caused by: java.util.NoSuchElementException: Pool exhausted
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464)

    两种情况均属于无法从资源池获取到资源,但第一种是超时,第二种是因为blockWhenExhausted为false根本就不等。

    遇到此类异常,不要盲目的认为资源池不够大,第三节已经进行了分析。具体原因可以排查:网络、资源池参数设置、资源池监控(如果对jmx监控)、代码(例如没执行jedis.close())、慢查询、DNS等问题。

    具体可以参考该文章:https://www.atatech.org/articles/77799

    2. 预热JedisPool

    由于一些原因(例如超时时间设置较小原因),有的项目在启动成功后会出现超时。JedisPool定义最大资源数、最小空闲资源数时,不会真的把Jedis连接放到池子里,第一次使用时,池子没有资源使用,会new Jedis,使用后放到池子里,可能会有一定的时间开销,所以也可以考虑在JedisPool定义后,为JedisPool提前进行预热,例如以最小空闲数量为预热数量

    复制代码
    List<Jedis> minIdleJedisList = new ArrayList<Jedis>(jedisPoolConfig.getMinIdle());
    
    for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            minIdleJedisList.add(jedis);
            jedis.ping();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
        }
    }
    
    for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
        Jedis jedis = null;
        try {
            jedis = minIdleJedisList.get(i);
            jedis.close();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
        
        }
    }
    复制代码



  • 相关阅读:
    终端设备 tty,pty,pts 概念与文件描述符的联系
    Nginx – access_log格式及配置
    Nginx – rewrite 配置 URL重写及301跳转原理图
    nginx命令启动及选项
    nginx-web身份验证
    nginx_server_location对客户资源的辨别规则
    利用Session防止表单重复提交
    归并排序
    Cookie/Session的机制与安全
    HTTP Cookie Session
  • 原文地址:https://www.cnblogs.com/gxyandwmm/p/10973511.html
Copyright © 2011-2022 走看看