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. c++ 读文件 文件指针 继续读_FatFs文件系统使用笔记
  2. 二章: CentOS6.5 连接FTP服务器、部署telnet服务、安装SCP、服务端FTP、SFTP部署
  3. linux mv 环境变量,linux环境变量 cp mv 以及文档查看的几个命令
  4. Android特效 五种Toast详解
  5. iOS开发针对对Masonry下的FPS优化讨论
  6. qtcreator下拉列表怎么制作_设置EXCEL动态下拉菜单,只需要一个组合键,新手也能快速掌握...
  7. python数据结构之字典(dict)——超详细
  8. 谷歌更新漏洞披露规则:不管补丁打没打,够90天才披露
  9. Flutter之Widget 更新机制updateChild原理浅析
  10. char 数组和 int 之间转化
  11. 解决NLPIR汉语分词系统init failed问题
  12. 公众号900篇文章分类和索引
  13. 怎样在计算机上设置纸大小,电脑中打印机设备自定义纸张打印大小的方法
  14. [硬件]_ELVE_VS2015下opencv3.3的配置问题
  15. Java高级之HashMap中的put()方法和putIfAbsent()方法
  16. num find matlab,matlab中find函数的使用说明
  17. 在Elasticsearch中回测隨機(Stochastic)指標交叉交易策略
  18. 鸿蒙十大凶兽排名,上古十大神兽|上古十大洪荒神兽|上古十大神兽资料大全【图文】...
  19. Firefox浏览器基本使用
  20. (附源码)计算机毕业设计SSM幼儿园管理系统

热门文章

  1. 深入浅出GAN框架原理
  2. ORA-12154 另一种解决方式,IIS发布后出现的---解决思路---终极方案
  3. 级联引用完整性约束ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT }
  4. Springcould学习总结
  5. 面向对象的三大特征:封装、继承和多态的简单概述
  6. bmob php支付,GitHub - bmob/bmob-php-sdk: PHP SDK相关源码
  7. 保险初识经验汇总(重疾、医疗、寿险、意外)
  8. Scrum: 时间框 (Timeboxing) 是什么?
  9. 手机怎么改android版本号,安卓手机build.prop每行的意思,如果想改版本信息型号等...
  10. 功放与喇叭的匹配原则