1. 异常现象

20190429 16:27:58,200 | ERROR | (RedisClient.java:262)RedisClient:262 - jedisInfo ... NumActive=8, NumIdle=0, NumWaiters=0, isClosed=false
20190429 16:27:59,462 | ERROR | (RedisClient.java:264)RedisClient:264 - GetJedis error,
redis.clients.jedis.exceptions.JedisException: Could not get a resource from the poolat redis.clients.util.Pool.getResource(Pool.java:51)at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226)...at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)at java.lang.Thread.run(Thread.java:745)
Caused by: java.util.NoSuchElementException: Timeout waiting for idle objectat org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)at redis.clients.util.Pool.getResource(Pool.java:49)

2. 排查分析

2.1. Jedis 依赖

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version>
</dependency>

确认没有问题。

2.2. Jedis 配置

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"><property name="testOnBorrow" value="true"/><property name="maxIdle" value="50" /><property name="maxWaitMillis" value="3000" /></bean>

疑点1:没有设置 maxTotal,使用默认值。MaxTotal 默认配置是 8。然而异常提示 8个资源正在被使用,连接池里没有了。

疑点2:Redis 连接得不到及时释放,其他线程无法申请到资源

导致 Redis 资源不足,无法从资源池获取到资源。与异常信息匹配:JedisException: Could not get a resource from the pool

2.3. Jedis 资源获取与释放

    /*** 获取Jedis实例** @return*/private synchronized Jedis getRedisClient() {int timeoutCount = 0;try {while (timeoutCount < MAX_TIMEOUT_COUNT) {try {return jedisPool.getResource();} catch (JedisConnectionException e) {timeoutCount++;logger.error("getJedis timeoutCount={}", timeoutCount);}}} catch (Exception e) {if (jedisPool != null) {logger.error("jedisInfo ... NumActive=" + jedisPool.getNumActive() + ", NumIdle=" + jedisPool.getNumIdle()+ ", NumWaiters=" + jedisPool.getNumWaiters() + ", isClosed=" + jedisPool.isClosed());}logger.error("GetJedis error,", e);}return null;}/*** 释放Jedis资源** @param jedis*/public void closeResource(Jedis jedis) {if (jedis != null) {jedis.close();}}

确认没有什么问题。

2.4. 默认值 - 源码

源码 GenericObjectPoolConfig.class

public class GenericObjectPoolConfig extends BaseObjectPoolConfig {public static final int DEFAULT_MAX_TOTAL = 8;public static final int DEFAULT_MAX_IDLE = 8;public static final int DEFAULT_MIN_IDLE = 0;private int maxTotal = 8;private int maxIdle = 8;private int minIdle = 0;public GenericObjectPoolConfig() {}......
}

从源码可以看到,maxTotal 默认配置是 8,maxIdle 默认配置是8,minIdle 默认配置是0。确认使用的是默认配置,与异常信息一致。

NumActive=8, NumIdle=0, NumWaiters=0, isClosed=false

源码 JedisPool.class

    /** @deprecated */@Deprecatedpublic void returnBrokenResource(Jedis resource) {if(resource != null) {this.returnBrokenResourceObject(resource);}}/** @deprecated */@Deprecatedpublic void returnResource(Jedis resource) {if(resource != null) {try {resource.resetState();this.returnResourceObject(resource);} catch (Exception var3) {this.returnBrokenResource(resource);throw new JedisException("Could not return the resource to the pool", var3);}}}

Jedis 2.9.0 版本及以上的 JedisPool 的 returnBrokenResource() 和 returnResource() 方法被标注废弃了,取而代之的是 Jedis 的 close() 。

源码 Jedis.class

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

所以,这里使用 jedis.close() 释放 Jedis 资源处理没有问题。

3. 解决方案

JedisPool 资源池优化,spring-jedis.xml 优化后配置:

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"><!--向资源池借用连接时是否做连接有效性检测(ping)。检测到的无效连接将会被移除。如果为true,则得到的jedis实例均是可用的--><property name="testOnBorrow" value="true"/><!--资源池中的最大连接数,默认8个--><property name="maxTotal" value="50" /><!--资源池允许的最大空闲连接数,默认8个--><property name="maxIdle" value="50" /><!--当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)。--><property name="maxWaitMillis" value="30000" /></bean>

4. 参数说明

DruidDataSource配置兼容DBCP,但个别配置的语意有所区别。

Jedis 就是集成了 Redis 的一些命令操作,封装了 Redis 的 Java 客户端,提供了连接池管理。一般不直接使用 Jedis,而是在其上再封装一层,作为业务的使用。

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

表 1. 资源设置与使用相关参数
参数 说明 默认值 建议
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 是该功能的开关。

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

5. 关键参数设置建议

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 不要设置得过小,否则会有 nwe Jedis  (新连接)开销,而 minIdle 是为了控制空闲资源检测。

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

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

使用监控获取合理值

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

此类异常的原因不一定是资源池不够大,建议从网络、资源池参数设置、资源池监控(如果对 JMX 监控)、代码(例如没执行jedis.close())、慢查询、DNS等方面进行排查。


参考技术文档:
https://help.aliyun.com/document_detail/98726.html#section-m2c-5kr-zfb
https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8

Java异常 | JedisException: Could not get a resource from the pool相关推荐

  1. jedis异常:Could not get a resource from the pool

    前几天公司后端系统出现了故障,导致app多个功能无法使用,查看日志,发现日志出现较多的redis.clients.jedis.exceptions.JedisConnectionException: ...

  2. 使用redis做缓存,遇到Could not return the resource to the pool异常怎么办呐!

    使用redis做缓存,短短几天就遇到两次redis.clients.jedis.exceptions.JedisException: Could not return the resource to ...

  3. redis使用中经常出现 Could not get a resource from the pool 异常,解决办法总结

    背景: 最近使用jedis(redis)开发一项功能,查阅日志发现,服务运行一段时间之后,就会出现 redis.clients.jedis.exceptions.JedisException: Cou ...

  4. 一篇不错的讲解Java异常的文章(转载)

    六种异常处理的陋习 你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了Java的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗? 1 OutputStreamWriter ...

  5. java异常—— finally 子句+带资源的 try语句

    [0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java异常-- finally 子句+带资源的 try语句 的相关知识: [1] final ...

  6. java异常标记_java异常机制

    ------------------------------------------------------------------下面是一些java异常集---------------------- ...

  7. 一篇不错的讲解Java异常的文章

     一篇不错的讲解Java异常的文章                                       六种异常处理的陋习     你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了J ...

  8. 一篇不错的讲解Java异常的文章(转载)----感觉很不错,读了以后很有启发

    六种异常处理的陋习 你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了Java的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗? 1 OutputStreamWriter ...

  9. java 异常面试问题_Java异常面试问答

    java 异常面试问题 Java provides a robust and object-oriented approach to handle exception scenarios known ...

最新文章

  1. php rabbmq教程_RabbitMQ+PHP 教程一(Hello World)
  2. 福师2021计算机应用基础,2021福师《计算机应用基础》在线作业二【满分答案】...
  3. 用python画大白_[Python][可视化]matplotlib基础入门
  4. mqadmin命令运行出错
  5. activeMQ的源码分析 -TCP通讯机制
  6. haproxy + keepalived + mycat 高可用与负载均衡集群配置 centos7
  7. python基础教程十进制_Python基础教程(四)
  8. 精选了20个Python实战项目(附源码)
  9. python函数调用执行的四个步骤_如何调用python函数
  10. 如何获取淘宝APP原数据现场教学
  11. 一个亿,啪一下就没了!
  12. 智能语音产品架构及应用场景
  13. java九宫格案例讲解_Java实现九宫格的教程详解
  14. 汽车电子行业入门指南「主要国内新能源车销量」
  15. 一张图理解栈顶指针加加减减的问题
  16. 写给大家看的CSS书,写给大家看的设计书
  17. matlab求二阶微分方程的通解,二阶常微分方程matlab的数值解和解析解分析总报告.ppt...
  18. JavaScript检测视频的编码格式是否为h264
  19. hadoop之slaves文件详细分析
  20. 使用adb命令将手机和至电脑上的文件互传

热门文章

  1. 关于7z各种不能用的操作解决办法 7za 7z x Error: Can not open file as archive there is no such archive
  2. 乘法/积运算和符号(点乘/内积/数量积,叉乘/向量积,矩阵乘法,Hadamard, Kronecker积,卷积)一网打尽
  3. 面试培训机构里的教师时,一分钟的自我介绍和五分钟的试讲如何安排?需要注意什么?
  4. java中继承内部类的方法_Java自学-接口与继承 内部类
  5. 蜡笔同步 java_蜡笔同步常见问题解析
  6. [FirefoxOS_开发环境]Linux和Ubuntu环境下B2G(Firefox OS)安装、编译、测试教程集合
  7. 斯坦福2021年度AI报告:从研发、技术、经济等多维度解读AI发展
  8. 本世纪,佛祖派机器人来弘扬佛法
  9. 写一个旅行青蛙攻略APP
  10. html5 css3制作柱形图,一步一步教你实现纯CSS的柱形图