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 null
	at 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() {
    //因此不会执行 flush
	flush();
	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 length
	at 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)导致的,后续发送的所有命令都收不到响应,都会是这个错误。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

续航1977公里的穿越之旅:比亚迪秦L DM-i试驾体验

5月31日,在西安这座古老而又充满活力的城市,一群自媒体驾驶着比亚迪秦L DM-i,踏上了从祖国西北到东南的穿越之旅。 在本次试驾活动中,自媒体们不仅要体验这款新能源车型的驾驶性能,而且还要亲自验证它在实际道路和极端…

基于聚类和回归分析方法探究蓝莓产量影响因素与预测模型研究

🌟欢迎来到 我的博客 —— 探索技术的无限可能! 🌟博客的简介(文章目录) 目录 背景数据说明数据来源思考 正文数据预处理数据读取数据预览数据处理 相关性分析聚类分析数据处理确定聚类数建立k均值聚类模型 多元线性回…

【专利 超音速】基于分类模型的轻量级工业图像关键点检测方法

申请号CN202311601629.7公开号(公开)CN117710683A申请日2023.11.27申请人(公开)超音速人工智能科技股份有限公司发明人(公开)张俊峰(总); 杨培文(总); 沈俊羽; 张小村 摘要 本发明涉及一种基于分类模型的…

【Makefile笔记】小白入门篇

【Makefile笔记】小白入门篇 文章目录 【Makefile笔记】小白入门篇所需组件一、简单了解Makefile1.Makefile简介2.Makefile 原理 二、为什么要使用Makefile1.解决编译时链库的不便2.提高编译效率,缩短编译时间(尤其是大工程) 三、Makefile语法…

vue 使用 Vxe UI vxe-print 实现复杂的 Web 打印,支持页眉、页尾、分页的自定义模板

Vxe UI vue 使用 Vxe UI vxe-print 实现复杂的 Web 打印,支持页眉、页尾、分页的自定义模板 官方文档 https://vxeui.com 查看 github、gitee 页眉-自定义标题 说明:vxe-print-page-break标签用于定义分页,一个标签一页内容,超…

Rust 性能分析

都说Rust性能好,但是也得代码写得好,猜猜下面两个代码哪个快 . - 力扣&#xff08;LeetCode&#xff09; use std::collections::HashMap; use lazy_static::lazy_static;lazy_static! {static ref DIGIT: HashMap<char, usize> {let mut m HashMap::new();for c in …

最新版点微同城源码34.7+全套插件+小程序前后端

带全套插件 自己耐心点配置一下插件 可以H5可以小程序 一款专属的同城服务平台对于企业和个人而言&#xff0c;无疑是拓展业务、提升服务品质的重要一环。点微同城源码搭配全套插件&#xff0c;以及完善的小程序前后端&#xff0c;将为您的业务发展提供强大支持 源码免费下载…

【PB案例学习笔记】-15怎样限制应用程序运行次数?

写在前面 这是PB案例学习笔记系列文章的第15篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

python词云生成库-wordcloud

内容目录 一、模块介绍二、WordCloud常用的方法1. generate(self, text)2. generate_from_frequencies(frequencies)3. fit_words(frequencies)4. generate_from_text(text) 三、进阶技巧1. 设置蒙版2. 设置过滤词 WordCloud 是一个用于生成词云的 Python 库&#xff0c;它可以…

【算法速查】万字图解带你快速入门八大排序(下)

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;首先在这里祝大家中秋国庆双节同乐&#xff01;&#xff01;抓住假期的小尾巴&#xff0c;今天来把算法速查的八大排序的后续写完&#xff0c;当…

用 OpenCV 实现图像中水平线检测与校正

前言 在本文中&#xff0c;我们将探讨如何使用 Python 和 OpenCV 库来检测图像中的水平线&#xff0c;并对图像进行旋转校正以使这些线条水平。这种技术可广泛应用于文档扫描、建筑摄影校正以及机器视觉中的各种场景。 环境准备 首先&#xff0c;确保您的环境中安装了 OpenC…

gulimall-search P125 springboot整合elasticsearch版本冲突

一、问题 spring-boot.version 2.2.4.RELEASE,在gulimall-search pom.xml中添加elasticsearch.version 7.4.2后&#xff0c;发现出现如下问题&#xff1a;elasticsearch版本是springboot引入的6.8.6&#xff0c;没有变为7.4.2。 二、原因 在gulimall-search 的pom文件中&#…

01_初识微服务

文章目录 一、微服务概述1.1 什么是微服务1.2 对比微服务架构与单体架构1.3 微服务设计原则1.4 微服务开发框架1.5 简单理解分布式部署与集群部署 二、微服务的核心概念2.1 服务注册与发现2.2 微服调用&#xff08;通信&#xff09;2.3 服务网关2.4 服务容错2.5 链路追踪参考链…

初级网络工程师之入门到入狱(一)

本文是我在学习过程中记录学习的点点滴滴&#xff0c;目的是为了学完之后巩固一下顺便也和大家分享一下&#xff0c;日后忘记了也可以方便快速的复习。 网络工程师从入门到入狱 前言一、交换机二、路由器三、DHCP&#xff08;动态主机配置协议&#xff09;四、路由器配置 DHCP自…

前端面试宝典总结2-CSS(1)

前端面试宝典总结之CSS&#xff08;1&#xff09; 本文章 对各大学习技术论坛知识点&#xff0c;进行总结、归纳自用学习&#xff0c;共勉&#x1f64f; 上一篇&#x1f449;: 前端面试宝典总结1- html 文章目录 前端面试宝典总结之CSS&#xff08;1&#xff09;1.分析比较 o…

C++ | Leetcode C++题解之第129题求根节点到叶节点数字之和

题目&#xff1a; 题解&#xff1a; class Solution { public:int sumNumbers(TreeNode* root) {if (root nullptr) {return 0;}int sum 0;queue<TreeNode*> nodeQueue;queue<int> numQueue;nodeQueue.push(root);numQueue.push(root->val);while (!nodeQueu…

【全开源】CMS内容管理系统(ThinkPHP+FastAdmin)

基于ThinkPHPFastAdmin的CMS内容管理系统&#xff0c;自定义内容模型、自定义单页、自定义表单、专题、统计报表、会员发布等 提供全部前后台无加密源代码和数据库私有化部署&#xff0c;UniAPP版本提供全部无加密UniAPP源码​ &#x1f50d; 解锁内容管理新境界&#xff1a;C…

高级 Go 程序设计:使用 net/http/httputil 包构建高效网络服务

高级 Go 程序设计&#xff1a;使用 net/http/httputil 包构建高效网络服务 介绍ReverseProxy 的使用基本概念实现步骤高级配置实际案例 DumpRequest 的使用功能说明代码示例应用场景NewSingleHostReverseProxy 的特性功能概述 详细教程 注意事项使用 NewChunkedWriter 实现高效…

【Linux-Uboot】

Linux-Uboot ■ Uboot使用 串口软件&#xff08;超级终端&#xff09;接受文件■ ■ Uboot使用 串口软件&#xff08;超级终端&#xff09;接受文件 添加链接描述 ■

ubuntu-server(22.04)安装

准备工作 首先我们先从网上获取ubuntu的iso镜像文件 Index of /ubuntu-releases/22.04/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 我们安装这个最小包即可 找到我们ubuntu安装完成后所需要下载安装源的网址&#xff08;常用是阿里云&#xff09; ubuntu安装…