Redis客户端常见异常分析

一.没法从链接池获取到链接

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();

 

  • 3.客户端:存在慢查询操做,这些慢查询持有的Jedis对象归还速度会比较慢,形成池子满了。
  • 4.服务端:客户端是正常的,可是Redis服务端因为一些缘由形成了客户端命令执行过程的阻塞,也会使得客户端抛出这种异常。
    能够看到形成这个异常的缘由是多个方面的,不要被异常的表象所迷惑,并且并不存在万能钥匙能解决全部问题,开发和运维只能不断增强对于Redis的理解,顺藤摸瓜逐渐找到问题所在。

2、 客户端读写超时

Jedis在调用Redis时,若是出现了读写超时后,会出现下面的异常:并发

1
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

 

形成该异常的缘由也有如下几种:运维

  • 读写超时设置的太短。
  • 命令自己就比较慢。
  • 客户端与服务端网络不正常。
  • Redis自身发生阻塞。

三 客户端链接超时

Jedis在调用Redis时,若是出现了读写超时后,会出现下面的异常:tcp

1
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out

 

形成该异常的缘由也有如下几种:分布式

  • 链接超时设置的太短。
  • Redis发生阻塞,形成tcp-backlog已满,形成新的链接失败。
  • 客户端与服务端网络不正常。

4、客户端缓冲区异常

Jedis在调用Redis时,若是出现客户端数据流异常,会出现下面的异常。

1
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.

 

形成这个异常缘由可能有以下几种:

  • 1.输出缓冲区满。例如将普通客户端的输出缓冲区设置为1M 1M 60:
    1
    config set client-output-buffer-limit "normal 1048576 1048576 60 slave 268435456 67108864 60 pubsub 33554432 8388608 60"

若是使用get命令获取一个bigkey(例如3M),就会出现这个异常。

  • 2.长时间闲置链接被服务端主动断开,能够查询timeout配置的设置以及自身链接池配置是否须要作空闲检测。
  • 3.不正常并发读写:Jedis对象同时被多个线程并发操做,可能会出现上述异常。

5、Lua脚本正在执行

若是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.

6、Redis正在加载持久化文件

Jedis调用Redis时,若是Redis正在加载持久化文件,那么会收到下面的异常。

1
redis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the dataset in memory

 

7、Redis使用的内存超过maxmemory配置

Jedis调用Redis执行写操做时,若是Redis的使用内存大于maxmemory的设置,会收到下面的异常,此时应该调整maxmemory并找到形成内存增加的缘由(maxmemory以前章节已经介绍了)

1
redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'.

8、客户端链接数过大

若是客户端链接数超过了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。

9、JedisCluster异常将在集群章节介绍。

附赠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开发与运维》一书,转载请声明。

相关文章
相关标签/搜索