二、客户端缓冲区异常
1.异常堆栈
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream. at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:199) at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40) at redis.clients.jedis.Protocol.process(Protocol.java:151) ......
2.异常描述
这个异常是客户端缓冲区异常,产生这个问题可能有三个原因:
(1) 常见原因:多个线程使用一个Jedis连接,正常的情况是一个线程使用一个Jedis连接,可以使用JedisPool管理Jedis连接,实现线程安全,避免出现这种情况。例如下面代码就是两个线程共用了一个Jedis连接:
new Thread(new Runnable() { public void run() { for (int i = 0; i < 100; i++) { jedis.get("hello"); } } }).start(); new Thread(new Runnable() { public void run() { for (int i = 0; i < 100; i++) { jedis.hget("haskey", "f"); } } }).start();
(2) 客户端缓冲区满了
Redis有三种客户端缓冲区:
普通客户端缓冲区(normal):用于接受普通的命令,例如get、set、mset、hgetall、zrange等。 slave客户端缓冲区(slave):用于同步master节点的写命令,完成复制。 发布订阅缓冲区(pubsub):pubsub不是普通的命令,因此有单独的缓冲区。
Redis客户端缓冲区配置的格式是:
client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
class: 客户端类型:可选值为normal、slave和pubsub。
hard limit: 如果客户端使用的输出缓冲区大于hard limit,客户端会被立即关闭,单位为秒。
soft limit和soft seconds: 如果客户端使用的输出缓冲区超过了soft limit并且持续了soft limit秒,客户端会被立即关闭,单位为秒。
例如下面是一份Redis缓冲区的配置,所以当条件满足时,客户端连接会被关闭,就会出现Unexpected end of stream。
redis> config get client-output-buffer-limit 1) "client-output-buffer-limit" 2) "normal 524288000 0 0 slave 2147483648 536870912 480 pubsub 33554432 8388608 60"
(3) 长时间闲置连接会被服务端主动断开,可以查询timeout配置的设置以及自身连接池配置确定是否需要做空闲检测。
3.解决方法和处理途径
客户:排查自身代码是否使用JedisPool管理Jedis连接,是否存在并发操作Jedis的情况。
工单: 排查是否上述(2)或(3)原因。云数据库Redis版默认的timeout值为0, 目前不支持修改。client-output-buffer-limit默认值为500MB,为阿里云优化后的合理值。如果超过该值,说明用户返回的值过多, 出于性能和稳定性考虑, 可建议用户优化应用程序。