JedisPool中的Jedis对象个数是有限的,默认是8个。这里假设使用的默认配置,若是有8个Jedis对象被占用,而且没有归还,若是调用者还要从JedisPool中借用Jedis,就须要进行等待(例如设置了maxWaitMillis>0),若是在maxWaitMillis时间内仍然没法获取到Jedis对象就会抛出以下异常。java
1 2 3 4 |
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) |
还有一种状况,就是设置了blockWhenExhausted=false,那么调用者发现池子中没有资源时,会当即抛出异常不进行等待,下面的异常就是blockWhenExhausted=false时的效果。git
1 2 3 4 |
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) |
对于这个问题,须要重点讨论的是为何链接池没有资源了,形成没有资源的可能的缘由很是多github
1.客户端:高并发下链接池设置太小,出现供不该求,因此会出现上面的错误,可是正常状况下只要比默认的最大链接数(8个)多一些便可,由于正常状况下JedisPool以及Jedis的处理效率足够高。redis
2.客户端:没有正确使用链接池,好比没有进行释放,例以下面代码所示:
定义JedisPool,使用默认的链接池配置。apache
1 2 3 4 5 6 7 8 9 10 11 12 |
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379); //向JedisPool借用8次链接,可是没有执行归还操做。 for (int i = 0; i < 8; i++) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.ping(); } catch (Exception e) { e.printStackTrace(); } } |
当调用者再向链接池借用Jedis时(以下操做),就会抛出异常:网络
1 |
jedisPool.getResource().ping(); |
Jedis在调用Redis时,若是出现了读写超时后,会出现下面的异常:并发
1 |
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out |
形成该异常的缘由也有如下几种:运维
Jedis在调用Redis时,若是出现了读写超时后,会出现下面的异常:tcp
1 |
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out |
形成该异常的缘由也有如下几种:分布式
Jedis在调用Redis时,若是出现客户端数据流异常,会出现下面的异常。
1 |
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream. |
形成这个异常缘由可能有以下几种:
1 |
config set client-output-buffer-limit "normal 1048576 1048576 60 slave 268435456 67108864 60 pubsub 33554432 8388608 60" |
若是使用get命令获取一个bigkey(例如3M),就会出现这个异常。
若是Redis当前正在执行Lua脚本,而且超过了lua-time-limit,此时Jedis调用Redis时,会收到下面的异常。对于如何处理这类问题(Lua lua-time-limit配置以前章节已经介绍了)
1 |
redis.clients.jedis.exceptions.JedisDataException: BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. |
Jedis调用Redis时,若是Redis正在加载持久化文件,那么会收到下面的异常。
1 |
redis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the dataset in memory |
Jedis调用Redis执行写操做时,若是Redis的使用内存大于maxmemory的设置,会收到下面的异常,此时应该调整maxmemory并找到形成内存增加的缘由(maxmemory以前章节已经介绍了)
1 |
redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'. |
若是客户端链接数超过了maxclients,新申请的链接就会出现以下异常:
1 |
redis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached |
此时新的客户端链接执行任何命令,返回结果都是以下:
1 2 |
127.0.0.1:6379> get hello (error) ERR max number of clients reached |
这个问题可能会比较棘手,由于此时没法执行Redis命令,通常来讲能够从两个方面进行着手。
1.客户端:若是maxclients参数不是很小的话,应用方的客户端链接数基本不会超过maxclients,一般来看是因为应用方对于Redis客户端使用不当形成的。此时若是应用方是分布式结构的话,能够经过下线部分应用节点(例如占用链接较多的节点),使得Redis的链接数先降下来。从而让绝大部分节点能够正常运行,此时在再经过查找程序bug或者调整maxclients进行问题的修复。
2.服务端:若是此时客户端没法处理,而当前Redis为高可用模式(例如Redis Sentinel和Redis Cluster),能够考虑将当前Redis作故障转移。
此问题不存在肯定的解决方式,可是不管从哪一个方面进行处理,故障的快速恢复极为重要,固然更为重要的是找到问题的所在,不然一段时间后客户端链接数依然会超过maxclients。
附赠GenericObjectPoolConfig的重要属性
序号 | 参数名 | 含义 | 默认值 |
---|---|---|---|
1 | maxActive | 链接池中最大链接数 | 8 |
2 | maxIdle | 链接池中最大空闲的链接数 | 8 |
3 | minIdle | 链接池中最少空闲的链接数 | 0 |
4 | maxWaitMillis | 当链接池资源用尽后,调用者的最大等待时间(单位为毫秒),通常不建议使用默认值 | -1:表示永远不超时,一直等。 |
5 | jmxEnabled | 是否开启jmx监控,若是应用开启了jmx端口而且jmxEnabled设置为true,就能够经过jconsole或者jvisualvm看到关于链接池的相关统计,有助于了解链接池的使用状况,而且能够针对其作监控统计 | true |
6 | minEvictableIdleTimeMillis | 链接的最小空闲时间,达到此值后空闲链接将被移除 | 30分钟 |
7 | numTestsPerEvictionRun | 作空闲链接检测时,每次的采样数 | 3 |
8 | testOnBorrow | 向链接池借用链接时是否作链接有效性检测(ping),无效链接会被移除,每次借用多执行一次ping命令 | false |
9 | testOnReturn | 向链接池归还链接时是否作链接有效性检测(ping),无效链接会被移除,每次归还多执行一次ping命令 | false |
10 | testWhileIdle | 向链接池借用链接时是否作链接空闲检测,空闲超时的链接会被移除 | false |
11 | timeBetweenEvictionRunsMillis | 空闲链接的检测周期(单位为毫秒) | -1:表示不作检测 |
12 | blockWhenExhausted | 当链接池用尽后,调用者是否要等待,这个参数是和maxWaitMillis对应的,只有当此参数为true时,maxWaitMillis才会生效 | true |
本文部份内容来自《Redis开发与运维》一书,转载请声明。