当前位置: 移动技术网 > IT编程>开发语言>Java > JedisPool资源池优化方法

JedisPool资源池优化方法

2019年07月19日  | 移动技术网IT编程  | 我要评论

背景

合理的jedispool资源池参数设置能为业务使用redis保驾护航,本文将对jedispool的使用、资源池的参数进行详细说明,最后给出“最合理”配置。

一、使用方法

以官方的2.9.0为例子(jedis release),maven依赖如下:

<dependency>
  <groupid>redis.clients</groupid>
  <artifactid>jedis</artifactid>
  <version>2.9.0</version>
  <scope>compile</scope>
</dependency>

jedis使用apache commons-pool2对jedis资源池进行管理,所以在定义jedispool时一个很重要的参数就是资源池genericobjectpoolconfig,使用方式如下,其中有很多资源管理和使用的参数(具体看第二节)

注意:后面会提到建议用jedispoolconfig代替genericobjectpoolconfig

genericobjectpoolconfig jedispoolconfig = new genericobjectpoolconfig();
jedispoolconfig.setmaxtotal(..);
jedispoolconfig.setmaxidle(..);
jedispoolconfig.setminidle(..);
jedispoolconfig.setmaxwaitmillis(..);

jedispool的初始化如下:

// redishost和redisport是实例的ip和端口
// redispassword是实例的密码
// timeout,这里既是连接超时又是读写超时,从jedis 2.8开始有区分connectiontimeout和sotimeout的构造函数

jedispool jedispool = new jedispool(jedispoolconfig, redishost, redisport, timeout, redispassword);
jedis jedis = null;
try {
  jedis = jedispool.getresource();
  //具体的命令
  jedis.executecommand()
} catch (exception e) {
  logger.error(e.getmessage(), e);
} finally {
  if (jedis != null) 
    jedis.close(); //注意这里不是关闭连接,在jedispool模式下,jedis会被归还给资源池。
}

二、参数说明

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:最大连接数

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

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

以一个例子说明,假设:

  1. 一次命令时间(borrow|return resource + jedis执行命令(含网络) )的平均耗时约为1ms,一个连接的qps大约是1000
  2. 业务期望的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等问题。

具体可以参考该文章:

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 {
  
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网