Java异常 | JedisException: Could not get a resource from the pool
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 的服务性能,降低资源开销。下面两表对一些重要参数进行说明,并提供设置建议。
参数 | 说明 | 默认值 | 建议 |
---|---|---|---|
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,就是对所有连接做空闲监测。 |
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相关推荐
- jedis异常:Could not get a resource from the pool
前几天公司后端系统出现了故障,导致app多个功能无法使用,查看日志,发现日志出现较多的redis.clients.jedis.exceptions.JedisConnectionException: ...
- 使用redis做缓存,遇到Could not return the resource to the pool异常怎么办呐!
使用redis做缓存,短短几天就遇到两次redis.clients.jedis.exceptions.JedisException: Could not return the resource to ...
- redis使用中经常出现 Could not get a resource from the pool 异常,解决办法总结
背景: 最近使用jedis(redis)开发一项功能,查阅日志发现,服务运行一段时间之后,就会出现 redis.clients.jedis.exceptions.JedisException: Cou ...
- 一篇不错的讲解Java异常的文章(转载)
六种异常处理的陋习 你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了Java的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗? 1 OutputStreamWriter ...
- java异常—— finally 子句+带资源的 try语句
[0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java异常-- finally 子句+带资源的 try语句 的相关知识: [1] final ...
- java异常标记_java异常机制
------------------------------------------------------------------下面是一些java异常集---------------------- ...
- 一篇不错的讲解Java异常的文章
一篇不错的讲解Java异常的文章 六种异常处理的陋习 你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了J ...
- 一篇不错的讲解Java异常的文章(转载)----感觉很不错,读了以后很有启发
六种异常处理的陋习 你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了Java的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗? 1 OutputStreamWriter ...
- java 异常面试问题_Java异常面试问答
java 异常面试问题 Java provides a robust and object-oriented approach to handle exception scenarios known ...
最新文章
- php rabbmq教程_RabbitMQ+PHP 教程一(Hello World)
- 福师2021计算机应用基础,2021福师《计算机应用基础》在线作业二【满分答案】...
- 用python画大白_[Python][可视化]matplotlib基础入门
- mqadmin命令运行出错
- activeMQ的源码分析 -TCP通讯机制
- haproxy + keepalived + mycat 高可用与负载均衡集群配置 centos7
- python基础教程十进制_Python基础教程(四)
- 精选了20个Python实战项目(附源码)
- python函数调用执行的四个步骤_如何调用python函数
- 如何获取淘宝APP原数据现场教学
- 一个亿,啪一下就没了!
- 智能语音产品架构及应用场景
- java九宫格案例讲解_Java实现九宫格的教程详解
- 汽车电子行业入门指南「主要国内新能源车销量」
- 一张图理解栈顶指针加加减减的问题
- 写给大家看的CSS书,写给大家看的设计书
- matlab求二阶微分方程的通解,二阶常微分方程matlab的数值解和解析解分析总报告.ppt...
- JavaScript检测视频的编码格式是否为h264
- hadoop之slaves文件详细分析
- 使用adb命令将手机和至电脑上的文件互传
热门文章
- 关于7z各种不能用的操作解决办法 7za 7z x Error: Can not open file as archive there is no such archive
- 乘法/积运算和符号(点乘/内积/数量积,叉乘/向量积,矩阵乘法,Hadamard, Kronecker积,卷积)一网打尽
- 面试培训机构里的教师时,一分钟的自我介绍和五分钟的试讲如何安排?需要注意什么?
- java中继承内部类的方法_Java自学-接口与继承 内部类
- 蜡笔同步 java_蜡笔同步常见问题解析
- [FirefoxOS_开发环境]Linux和Ubuntu环境下B2G(Firefox OS)安装、编译、测试教程集合
- 斯坦福2021年度AI报告:从研发、技术、经济等多维度解读AI发展
- 本世纪,佛祖派机器人来弘扬佛法
- 写一个旅行青蛙攻略APP
- html5 css3制作柱形图,一步一步教你实现纯CSS的柱形图