在Redis客户端的使用过程中,无论是客户端使用不当或者Redis服务端出现问题,客户端会反应出一些异常,下面分析一下Jedis使用过程中常见的异常情况:
JedisPool中的Jedis对象个数是有限的,默认是8个。这里假设使用的默认配置,如果有8个Jedis对象被占用,并且没有归还,如果调用者还要从JedisPool中借用Jedis,就需要进行等待(例如设置了maxWaitMillis>0),如果在maxWaitMillis时间内仍然无法获取到Jedis对象就会抛出如下异常。
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时的效果。
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)
对于这个问题,需要重点讨论的是为什么连接池没有资源了,造成没有资源的可能的原因非常多
2.客户端:没有正确使用连接池,比如没有进行释放,例如下面代码所示:定义JedisPool,使用默认的连接池配置。
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时(如下操作),就会抛出异常:
jedisPool.getResource().ping();
Jedis在调用Redis时,如果出现了读写超时后,会出现下面的异常:
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
造成该异常的原因也有以下几种:
Jedis在调用Redis时,如果出现了读写超时后,会出现下面的异常:
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out
造成该异常的原因也有以下几种:
Jedis在调用Redis时,如果出现客户端数据流异常,会出现下面的异常。
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.
造成这个异常原因可能有如下几种:
1.输出缓冲区满。例如将普通客户端的输出缓冲区设置为1M 1M 60:
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配置之前章节已经介绍了)
redis.clients.jedis.exceptions.JedisDataException: BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
Jedis调用Redis时,如果Redis正在加载持久化文件,那么会收到下面的异常。
redis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the dataset in memory
Jedis调用Redis执行写操作时,如果Redis的使用内存大于maxmemory的设置,会收到下面的异常,此时应该调整maxmemory并找到造成内存增长的原因(maxmemory之前章节已经介绍了)
redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'.
如果客户端连接数超过了maxclients,新申请的连接就会出现如下异常:
redis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached
此时新的客户端连接执行任何命令,返回结果都是如下:
127.0.0.1:6379> get hello
(error) ERR max number of clients reached
这个问题可能会比较棘手,因为此时无法执行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 |
如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!
赞0
添加新评论0 条评论