对于Redis服务器的输出(也就是命令的返回值)来说,其大小通常是不可控制的。有可能一个简单的命令,能够产生体积庞大的返回数据。另外也有可能因为执行了太多命令,导致产生返回数据的速率超过了往客户端发送的速率,这是也会导致服务器堆积大量消息,从而导致输出缓冲区越来越大,占用过多内存,甚至导致系统崩溃。

所幸,Redis设置了一些保护机制来避免这种情况的出现,不同类型的客户端有不同的限制参数。限制方式有如下两种:

(1)、大小限制,当某一个客户端的缓冲区超过某一个大小值时,直接关闭这个客户端的连接;

(2)、持续性限制,当某一个客户端的缓冲区持续一段时间占用过大空间时,会直接关闭客户端连接。

我们来看看配置文件关于客户端输出缓冲区的配置:

client-output-buffer-limit normal 0 0 0

client-output-buffer-limit slave 256mb 64mb 60

client-output-buffer-limit pubsub 8mb 2mb 60

不同客户端有不同策略,策略如下:

Ø  对于普通客户端来说,限制为0,也就是不限制。因为普通客户端通常采用阻塞式的消息应答模式,何谓阻塞式呢?如:发送请求,等待返回,再发送请求,再等待返回。这种模式下,通常不会导致Redis服务器输出缓冲区的堆积膨胀;

Ø  对于Pub/Sub客户端(也就是发布/订阅模式),大小限制是8M,当输出缓冲区超过8M时,会关闭连接。持续性限制是,当客户端缓冲区大小持续60秒超过2M,则关闭客户端连接;

Ø  对于slave客户端来说,大小限制是256M,持续性限制是当客户端缓冲区大小持续60秒超过64M,则关闭客户端连接。

上述三种规则都是可以修改的。可以通过CONFIG SET 命令设置或者直接修改redis.conf文件。

jedis 的 Unexpected end of stream 解决方案

  • redis 服务端版本号:2.8.X
  • Jedis 客户端版本号:2.8.1
  • 单线程、无并发操作

Jedis单链接、JedisPool、ShardedJedisPool,无论使用哪一种方式对 redis 服务进行操作,均出现了 Unexpected end of stream 的问题。

通过查看源码发现,报错具体位置是:RedisInputStream 类的 ensureFill() 方法

private void ensureFill() throws JedisConnectionException {
if (count >= limit) {
try {
limit = in.read(buf);
count = 0;
if (limit == -1) {
throw new JedisConnectionException("Unexpected end of stream.");
}
} catch (IOException e) {
throw new JedisConnectionException(e);
}
}
}

解决方案:

第一步,检查 redis config 中的 client-output-buffer-limit 配置

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 0 0 0
client-output-buffer-limit pubsub 0 0 0
  • 1
  • 2
  • 3

请根据实际情况合理设置Redis输出缓冲区限制,确定不是因为缓冲区太小,导致链接关闭,进而引起 Unexpected end of stream ,可以临时都设置成 “0(关闭缓冲区限制)” 来验证此种场景

第二步,检查 redis config 中的 timeout 配置

当此时间设置过短时,同一个 jedis 链接,两次访问 redis 服务的时间间隔 > ${timeout} , 服务端会单方关闭这个jedis链接,第二次使用这个jedis对象 操作 redis 时,会发生 Unexpected end of stream , 可以将其设置成“0(无过期)”来验证此种场景

第三步,优化客户端代码,增加重试机制

经过前两步的调试,根据业务实际情况,调整配置,但这样并不能完全杜绝 Unexpected end of stream 的发生,比如 “网络抖动”之类的场景下,依然会发生此问题(实际生产环境中,此种情况发生概率极低), 
但是为了防止此异常引起 jvm宕机,建议在代码层面上增加加重试机制。

话外篇:

使用 redis 做存储层时,有可能出现数数据不一致的情况( redis 无事务)

Redis缓冲区设置的更多相关文章

  1. 深入Redis客户端(redis客户端属性、redis缓冲区、关闭redis客户端)

    深入Redis客户端(redis客户端属性.redis缓冲区.关闭redis客户端) Redis 数据库采用 I/O 多路复用技术实现文件事件处理器,服务器采用单线程单进程的方式来处理多个客户端发送过 ...

  2. redis密码设置、访问权限控制等安全设置

    redis作为一个高速数据库,在互联网上,必须有对应的安全机制来进行保护,方法有2,如下. 1.比较安全的办法是采用绑定IP的方式来进行控制.  请在redis.conf文件找到如下配置 # If y ...

  3. Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十一)redis密码设置、安全设置

    警惕 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合进阶篇(九)Linux下安装redis及redis的常用命令和操作>主要是一个简单的介绍,针对redis ...

  4. 如果redis没有设置expire,他是否默认永不过期?

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/soulmate_P/article/details/81136054如果没有设置有效期,即使内存用完 ...

  5. java操作Redis缓存设置过期时间

    关于Redis的概念和应用本文就不再详解了,说一下怎么在java应用中设置过期时间. 在应用中我们会需要使用redis设置过期时间,比如单点登录中我们需要随机生成一个token作为key,将用户的信息 ...

  6. CentOS7 安装redis 并且设置成服务自动启动

    通过 博客园 https://www.cnblogs.com/zuidongfeng/p/8032505.html 学习以及记录 1. 下载redis 现在最新的stable版本是 4.0.10 wg ...

  7. Redis 密码设置和查看密码

    Redis 密码设置和查看密码 redis没有实现访问控制这个功能,但是它提供了一个轻量级的认证方式,可以编辑redis.conf配置来启用认证. 1.初始化Redis密码: 在配置文件中有个参数: ...

  8. window下php5安装redis扩展 设置自启动服务

    最近想在5.6版本的开发环境装一下redis的扩展,结果找了半天都是失效链接,特此做下备份 5.3-5.6 https://pecl.php.net/package/redis/2.2.7/windo ...

  9. redis批量设置过期时间

    Redis 中有删除单个 Key 的指令 DEL,但好像没有批量删除 Key 的指令,不过我们可以借助 Linux 的 xargs 指令来完成这个动作.代码如下: redis-cli keys &qu ...

随机推荐

  1. 页面的checkbox框的全选与反选

    if (typeof jQuery == 'undefined') {     alert("请先导入jQuery");} else {    jQuery.extend({    ...

  2. Jquery实现$.fn.extend和$.extend函数

    $.fn.extend(   {      method:function(){   }   })   $.extend(   {      method:function(){   }   })

  3. EasyTouch的使用官方文档操作步骤

    对于移动平台上的RPG类的游戏,我们常用虚拟摇杆来控制人物角色的行走和一些行为,相信我们对它并不陌生,之前尝试了EasyTouch2.5,发现并没有最新版的3.1好用,2.5版本的对于自适应没有做的很 ...

  4. [转]ASP.NET MVC 5 - 给电影表和模型添加新字段

    在本节中,您将使用Entity Framework Code First来实现模型类上的操作.从而使得这些操作和变更,可以应用到数据库中. 默认情况下,就像您在之前的教程中所作的那样,使用 Entit ...

  5. sublime常用插件及配置

      以下是我的sublime插件列表: Side​Bar​Enhancements增强版侧边栏 这个插件官方不支持通过package安装了,只能手动了,下载地址https://github.com/S ...

  6. Python 高斯坐标转经纬度算法

    # 高斯坐标转经纬度算法# B=大地坐标X# C=大地坐标Y# IsSix=6度带或3度带def GetLatLon2(B, C,IsSix): #带号 D = math.trunc(C / 1000 ...

  7. linux 添加交换分区

    [操作简介] 增加swap分区方法: 1.新建磁盘分区作为swap分区 2.用文件作为swap分区 (操作更简单,我更常用) 下面介绍这两种方法:(都必须用root权限,操作过程应该小心谨慎.)   ...

  8. 编译OSG的FreeType插件时注意的问题

    使用自己编译的freetype.lib,在编译osgdb_freetype插件项目时,报错LINK错误,找不到png的一堆函数 最简单的方式是不要使用PNG编译freetype.记住不要犯贱.

  9. c/c++左值和右值

    C/C++中的变量有左值和右值之分,他们的区别主要如下: (1)左值可以放在赋值号 = 的左右两边,右值只能放在赋值号 = 的右边 (2)在C语言中,有名字的变量即为左值:而函数的运行结果或表达式中间 ...

  10. JS-在线运行代码小工具

    原理:window.open()方法,open一个新的空白页,然后把文本框中粘贴的代码通过DOM操作,写到新的代码页中, 再利用document.write的功能(写进去之前把其他的全部删掉,并且写进 ...