设为首页 收藏本站
查看: 903|回复: 0

[经验分享] Redis客户端常见异常分析

[复制链接]

尚未签到

发表于 2018-11-3 14:35:35 | 显示全部楼层 |阅读模式
  一.无法从连接池获取到连接
  JedisPool中的Jedis对象个数是有限的,默认是8个。这里假设使用的默认配置,如果有8个Jedis对象被占用,并且没有归还,如果调用者还要从JedisPool中借用Jedis,就需要进行等待(例如设置了maxWaitMillis>0),如果在maxWaitMillis时间内仍然无法获取到Jedis对象就会抛出如下异常。
  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>  at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
  还有一种情况,就是设置了blockWhenExhausted=false,那么调用者发现池子中没有资源时,会立即抛出异常不进行等待,下面的异常就是blockWhenExhausted=false时的效果。
  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)
  对于这个问题,需要重点讨论的是为什么连接池没有资源了,造成没有资源的可能的原因非常多
  1.客户端:高并发下连接池设置过小,出现供不应求,所以会出现上面的错误,但是正常情况下只要比默认的最大连接数(8个)多一些即可,因为正常情况下JedisPool以及Jedis的处理效率足够高。
  2.客户端:没有正确使用连接池,比如没有进行释放,例如下面代码所示: 定义JedisPool,使用默认的连接池配置。
  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的理解,顺藤摸瓜逐渐找到问题所在。 二、 客户端读写超时
  Jedis在调用Redis时,如果出现了读写超时后,会出现下面的异常:
  1
  redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
  造成该异常的原因也有以下几种:
  读写超时设置的过短。
  命令本身就比较慢。
  客户端与服务端网络不正常。
  Redis自身发生阻塞。 三 客户端连接超时
  Jedis在调用Redis时,如果出现了读写超时后,会出现下面的异常:
  1
  redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out
  造成该异常的原因也有以下几种:
  连接超时设置的过短。
  Redis发生阻塞,造成tcp-backlog已满,造成新的连接失败。
  客户端与服务端网络不正常。 四、客户端缓冲区异常
  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对象同时被多个线程并发操作,可能会出现上述异常。 五、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.
  六、Redis正在加载持久化文件
  Jedis调用Redis时,如果Redis正在加载持久化文件,那么会收到下面的异常。
  1
  redis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the dataset in memory
  七、Redis使用的内存超过maxmemory配置
  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。
  九、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


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-630332-1-1.html 上篇帖子: Redis 基础、高级特性与性能调优 下篇帖子: Redis 连接池配置及redis操作
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表