Redis 异常三连环

请添加图片描述

本文针对一种特殊情况下的Reids连环异常,分别是下面三种异常:

  1. NullPointerException: Cannot read the array length because “arg” is null
  2. JedisDataException: ERR Protocol error: invalid bulk length
  3. JedisConnectionException: Unexpected end of stream.

先说个普遍适用的处理错误经验,当你发现日志最新出现的某个错误的时候,多往前翻翻日志,看看更早的时候有没有其他的错误,很多时候的错误都在更早的时候出现,这个错误看似不影响后续的使用,但是埋下了很多坑,等待时机合适就会爆发出来。如果你只关注最后产生直接感受的这个错误,很可能走弯路,百思不得其解。

这个问题还频繁出现在分页插件PageHelper中,如果你有不规范的分页调用,例如下面这种情况:

PageHelper.start(1, 10);
if(满足条件) {result = mapper.select(params);
} else {result = emptyList();
}

调用PageHelper时,你设置的分页信息已经绑定到线程,当满足条件执行 mybatis 查询时,分页信息会被消费并清除,当不满足条件时,分页信息仍然绑定在线程上,相当于埋了个雷,等其他请求再使用这个线程时,只要有查询,就会消费这里的分页信息,你的感受就是我没有调用分页,为什么查询被分页了?分页插件文档有 安全调用 的多种方式,而且后续版本为了方便识别这种错误,还支持在消费分页信息时,打印设置分页参数时的堆栈信息,通过堆栈信息可以识别不安全的调用方式。很多人吐槽PageHelper不安全,实际上都是不正确使用导致的,如果乱用,Java安全吗,把系统删了怎么办?JDBC安全吗,把库删了怎么办?

回归正题,某个项目在测试环境没有用Redis缓存,在线上环境配置了Redis缓存,之后就遇到了缓存的问题,从日志能看到莫名其妙的 Unexpected end of stream.ERR Protocol error: invalid bulk length 错误信息,以及更靠前有明确其他错误提示的 java.io.NotSerializableException: com.example.SimpleValue,并没有看到上面列的第一个异常。使用 arthas 监控时发现错误2和3时操作的 key 和错误1时完全不相关,所以前期处理的重点是错误2和3,这两个错误执行过程看着都是正常的,所以按照经验先解决序列化的问题,解决这个问题后如何问题解决就说明是序列化导致的后续两个问题。

网上搜后两个问题时,大多数答案都不正确,但是在CSDN看到了一个答案:

  • 关于spring-data操作redis报invalid-bulk-length的错误分析

这里通过分析源码找到了问题的原因,还提供了一个复现的例子,例子有点麻烦,所以我写了一个更简单的复现三个错误的例子:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;import java.nio.charset.StandardCharsets;public class RedisErrorTest {public static void main(String[] args) {JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");try (Jedis jedis = pool.getResource()) {try {jedis.set("parent".getBytes(StandardCharsets.UTF_8), null);} catch (Exception e) {e.printStackTrace();}try {jedis.set("hello", "world");} catch (Exception e) {e.printStackTrace();}try {String val = jedis.get("hello");System.out.println(val);} catch (Exception e) {e.printStackTrace();}}pool.close();}
}

错误的重点是 jedis.set("parent".getBytes(StandardCharsets.UTF_8), null); 这里设置了 null,这会导致下面的异常:

java.lang.NullPointerException: Cannot read the array length because "arg" is nullat redis.clients.jedis.Protocol.sendCommand(Protocol.java:99)at redis.clients.jedis.Protocol.sendCommand(Protocol.java:84)at redis.clients.jedis.Connection.sendCommand(Connection.java:127)at redis.clients.jedis.BinaryClient.set(BinaryClient.java:110)at queue.RedisErrorTest.main(RedisErrorTest.java:17)

这个异常会导致 jedis.set 下面的代码被跳过:

public String set(final byte[] key, final byte[] value) {checkIsInMultiOrPipeline();client.set(key, value);//下面代码没有被执行return client.getStatusCodeReply();
}public String getStatusCodeReply() {//因此不会执行 flushflush();pipelinedCommands--;final byte[] resp = (byte[]) readProtocolWithCheckingBroken();if (null == resp) {return null;} else {return SafeEncoder.encode(resp);}
}protected void flush() {try {//这里也就无法执行了outputStream.flush();} catch (IOException ex) {broken = true;throw new JedisConnectionException(ex);}
}

outputStream.flush() 的代码如下:

@Override
public void flush() throws IOException {flushBuffer();out.flush();
}
private void flushBuffer() throws IOException {if (count > 0) {out.write(buf, 0, count);//这里不执行count = 0;}
}

由于 count 没有置0,等下个命令执行时会在上面未完成命令基础上继续写入。

继续使用这个jedis连接操作 jedis.set("hello", "world") 时,看着很正常,但是会报错:

redis.clients.jedis.exceptions.JedisDataException: ERR Protocol error: invalid bulk lengthat redis.clients.jedis.Protocol.processError(Protocol.java:127)at redis.clients.jedis.Protocol.process(Protocol.java:161)at redis.clients.jedis.Protocol.read(Protocol.java:215)at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:239)at redis.clients.jedis.Jedis.set(Jedis.java:121)at queue.RedisErrorTest.main(RedisErrorTest.java:22)

此时当前连接已经不能正常响应了,后续再 jedis.get("hello") 就会报错:

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)at redis.clients.jedis.Protocol.read(Protocol.java:215)at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)at redis.clients.jedis.Connection.getBinaryBulkReply(Connection.java:259)at redis.clients.jedis.Connection.getBulkReply(Connection.java:248)at redis.clients.jedis.Jedis.get(Jedis.java:153)at queue.RedisErrorTest.main(RedisErrorTest.java:27)

这个错误信息是因为没有收到服务器端响应(in.read() == -1)导致的,后续发送的所有命令都收不到响应,都会是这个错误。

这个错误和前面普遍适用的经验一样,和分页插件遇到的问题类似,当你使用不对埋下雷时,解决炸雷是解决不了问题的,解决了埋雷的问题才能从根本解决问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/848150.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NAT端口映射,实现外网访问内网服务器

目录 前言一、搭建网络拓扑1.1 配置server和pc1.1.1 配置server01.1.2 配置server11.1.3 配置pc0 1.2 配置客户路由器1.2.1 配置路由器IP1.2.2 配置静态路由 1.3 配置ISP路由器 二、配置端口映射2.1 在客户路由器配置端口映射2.2 测试公网计算机访问私网服务器2.2.1 PC0向serve…

Base64前端图片乱码转换

title: Base64码乱转换 date: 2024-06-01 20:30:28 tags: vue3 后端图片前端显示乱码 现象 后端传来一个图片,前端能够接收,但是console.log()后发现图片变成了乱码,但是检查后台又发现能够正常的收到了这张图片。 处理方法 笔者有尝试将…

dotenv 配置踩坑-显示undefined

今天在学习dotenv,结果自己按照官方文档巧下来竟然还是不行,人麻了~ 这是我的目录结构 按照配置那么,我们只需要在config.default.js中写入如下代码就可以实现它将环境变量从文件加载到process.env中。 但是,但是这里犯了一个低级错误&#…

通用高电子迁移率晶体管(HEMT)的差分微变解算方案及分析型模型

来源:A Difference-Microvariation Solution and Analytical Model for Generic HEMTs(TED 22年) 摘要 这篇论文提出了一种AlGaN/GaN和AlGaAs/GaAs基高电子迁移率晶体管(HEMT)的分析型直流模型。该模型考虑了高栅偏压下势垒层中积累的电荷。…

什么?!这年代还有人用父子组件通信?

创作背景 因为本前端菜鸟写代码从没考虑过代码是否易于维护,所以一旦涉及组件通信,一律使用Pinia状态管理,至于父子组件通信啥的,学完Pinia之后就被我狠狠抛弃了,当时就在想:为什么不直接教Pinia&#xff…

YOLO-Worldv2两分钟快速部署

本次部署使用的框架基于ultralytics, 并且已经集成最新版本的YOLOv8框架: 一键环境配置 pip install ultralytics基础使用 训练 from ultralytics import YOLOWorld model YOLOWorld(yolov8x-worldv2.pt) results model.train(datacoco8.yaml, epo…

康谋技术 | 自动驾驶:揭秘高精度时间同步技术(一)

众所周知,在自动驾驶中,主要涵盖感知、规划、控制三个关键的技术层面。在感知层面,单一传感器采集外界信息,各有优劣,比如摄像头采集信息分辨率高,但是受外界条件影响较大,一般缺少深度信息&…

算法导论 总结索引 | 第三部分 第十四章:数据结构的扩张

1、通过存储 额外信息的方法来扩张一 种标准的数据结构,然后对这种数据结构,编写新的操作来支持所需的应用。因为添加的信息 必须要能被该数据结构上的常规操作更新和维护 2、通过扩张红黑树构造出的两种数据结构:14.1介绍 一种支持一般动态…

从迷茫到精通,小李的IPD培训‘逆袭’之路!——精益咨询

谈及IPD培训,这确实是一个让小李心生敬畏而又充满期待的词汇。作为刚刚踏上这段“奇幻”旅程的小白,小李深感其不仅是一场知识的狂欢,更是一次思维的洗礼和团队的熔炼。 IPD,全称集成产品开发,它代表着一种前沿的产品…

使用 Node.js 和 Azure Function App 自动更新 Elasticsearch 索引

作者:来自 Elastic Jessica Garson 维护最新数据至关重要,尤其是在处理频繁变化的动态数据集时。这篇博文将指导你使用 Node.js 加载数据,并通过定期更新确保数据保持最新。我们将利用 Azure Function Apps 的功能来自动执行这些更新&#xf…

云计算百科:类型、服务与业务优势一网打尽

了解云计算的权威指南是至关重要的,掌握云计算的类型、服务、用途以及它们如何为组织带来好处尤为重要。 假设某公司的员工正在办公室工作,突然间火警响起,原来IT部门的主服务器机房着火了,所有服务器都受到了影响。这一消息引发…

新一代企业共享服务中心,开启企业智慧管理决策新纪元

随着数字化浪潮加速来袭,企业面临着与以往全然不同的挑战与机遇。 业务与人员的增长致使服务请求与日俱增,业务类型愈加复杂,大量来自不同业务线的服务请求使内部服务压力增加。业务激增后只得依赖更多资源投入,势必掣肘服务效率。…

CSS函数: 实现数据限阈的数字函数

CSS函数中提供了几个比较实用的数字函数,它可以帮助我们实现一定的数学计算功能。常见的数字函数目前提供了五个:calc()、max()、min()和clamp()函数。其基本实现功能如下: calc():允许在声明 CSS 属性值时执行一些计算。max()&a…

eNSP学习——配置RIPv2认证

目录 主要命令 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、基本配置 2、搭建RIP网络 3、模拟网络攻击 4、配置RIPv2简单验证 5、配置RIPv2 MD5密文验证 需要eNSP各种配置命令的点击链接自取:华为eNSP各种设备配置命令大全PD…

通过LabVIEW提升生产设备自动化水平

现代制造业对生产设备的自动化水平提出了越来越高的要求。使用LabVIEW这一强大的图形化编程环境,可以显著提升生产设备的自动化程度,改善生产效率和产品质量。本文将详细分析如何通过LabVIEW改善生产设备的自动化水平,并提供具体的实施策略与…

SpringBoot社区配送服务系统小程序-计算机毕业设计源码88705

摘要 随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,社区当然也不例外。社区配送服务系统小程序是以实际运用为开发背景,运用软件工程原理和开发方法,采…

alist配合onlyoffice 实现在线预览

alist配合onlyoffice 实现在线预览 文章目录 alist配合onlyoffice 实现在线预览一、安装onlyoffice二、增加view.html文件三、安装nginx,并增加conf配置文件四、alist预览配置增加 一、安装onlyoffice 我是采用docker安装,采用的版本是7.2, …

安装Acunetix

Acunetix是一个自动化的Web应用程序安全测试工具,可以扫描任何可通过Web浏览器访问的和遵循HTTP/HTTPS规则的Web站点和Web应用程序,提供全面的漏洞扫描服务,并生成响应的报告 # 1. 安装 代码语言:javascript 复制 [rootwhcode…

高效管理近30万稳定运力,科技物流企业万联易达这样做

万联易达物流科技有限公司(以下简称“万联易达”),是由中泽集团投资设立的创新型物流科技企业。为提高平台数智化服务水平,提升业务开展效率,达到运输全场景合规化管理,万联易达平台引用法大大电子合同&…

在iPad上恢复丢失数据的3方法

iPad概览 iPad不仅可以用来看电影,还可以用来工作和学习。使用 Apple Pencil,您可以在 iPad 上做笔记、画画、制作音乐、编辑视频和在课堂上教学等。同时,由于体积小,您可以在商务旅行中轻松随身携带。因此,iPad已成为…