问题带来多少成长,看你挖得有多深多痛

原文: 一次Redis访问超时的“捉虫”之旅

力是相互的,成长与痛苦也是相互的。

01-引言

最近在对一个老项目使用的docker镜像版本升级过程中碰到一个奇怪的问题,发现项目升级到高版本镜像后,访问Redis会出现很多超时错误,而降回之前的镜像版本后问题也随之消失。经过排查,最终定位问题元凶是一个涉及到Lettuce、Redis、Netty等多块内容的代码bug
在问题解决过程中也对相关组件的工作方式有了更深一步的理解。以下就对“捉虫”过程中的问题分析和排查过程做一个详细的介绍。

03-问题现象

为了固定问题场景,我们进行了一番条件测试,发现了一些端倪:

  • 低版本镜像上RedisTemplete缓存框架访问Redis集群正常
  • 高版本镜像上RedisTemplete访问Redis集群正常,缓存框架访问Redis集群超时。项目启动一段时间后框架访问恢复正常。
  • 低版本和高版本镜像中RedisTemplete和缓存框架访问Redis单机正常

根据以上现象不难推断出,问题应似乎出现在缓存框架访问Redis集群的机制上。结合项目启动一段时间后会恢复正常的特点,猜测应该和缓存预热流程有关。

04-排查过程

复现case

查阅代码后发现自研的缓存框架没有通过Spring访问Redis,而是直接使用了Sping底层的Redis客户端——Lettuce

排查至此,我们发现缓冲区的数据积压很可能就是造成反查请求超时的原因,明白了这一点后,我们开始思考:

  • 连接缓冲区中的数据应该由谁来消费?
  • 每个连接的作用是什么?
  • 为什么只有一个连接出现了数据积压情况?
  • 为什么积压情况只在高版本的镜像中出现?
  • 为什么通过Spring访问Redis就不会出现超时问题?

深度分析

要回答以上问题,首先要了解Lettuce的工作原理,重点是其底层是如何访问Redis集群的。
Lettuce的工作原理
根据官网介绍,Lettuce 底层基于 Netty 的NIO模型实现,只用有限的线程支持更多的 Redis 连接,在高负载情况下能更有效地利用系统资源。

EventLoop机制的核心功能是多路复用,这意味着一个线程可以处理多个连接的读写事件。但是要实现这一点的前提是EventLoop线程不能被阻塞,否则注册在该线程上的各个连接的事件将得不到响应。由此我们可以推测,如果socket缓冲区出现积压,可能是某些原因导致socket连接对应的 EventLoop 线程被阻塞,使其无法正常响应可读事件并读取缓冲区数据。

为了验证猜测,我们在日志中打印线程信息做进一步观察。
在这里插入图片描述
结果发现大部分超时都发生在同一个EventLoop线程上(Lettuce的epollEventLoop-9-3线程),那这个线程此刻的状态是什么呢?我们可以通过诊断工具查看线程堆栈,定位阻塞原因。

Arthas排障

这里我们利用阿里Arthas排障工具的thread命令查看线程状态和堆栈信息。
在这里插入图片描述
从堆栈信息可以看出,Lettuce一共创建了3个Netty EventLoop线程,其中9-3处在TIMED_WAITTING状态,该线程亦是Pub/Sub消息的监听线程,阻塞在了RedisLettucePubSubListener对象接收消息更新热key的get方法上。

定位原因

通过Arthas排障我们了解到,原来Lettuce是在Netty的EventLoop线程中响应Pub/Sub事件的。由此我们也基本定位了缓冲区的积压原因,即在RedisLettucePubSubListener中执行了阻塞的future get方法,导致其载体EventLoop线程被阻塞,无法响应与其Selector关联连接的io事件。

为什么Pub/Sub事件会和其他连接的io事件由同一个EventLoop处理呢?通过查阅资料,发现Netty对连接进行多路复用时,只会启动有限个EventLoop线程(默认是CPU数*2)进行连接管理,每个连接是轮询注册到 EventLoop上的,所以当EventLoop数量不多时,多个连接就可能会注册到同一个io线程上。

针对这种应用场景Lettuce官网上也有专门提醒:https://lettuce.io/core/release/reference/index.html

  • 即不要在Pub/Sub的回调函数中执行阻塞操作。
    在这里插入图片描述

解决方案

原因定位后,解决方案也呼之欲出。有两种方法:

增加io线程

异步化

比较优雅的方式是不要在nio线程中执行阻塞操作,即将处理Pub/Sub消息的过程异步化,最好放到独立的线程中执行,以尽早释放Netty的EventLoop资源。我们熟悉的spring-data-redis框架就是这么做的。

  • Spring-data-redis的做法是每次收到消息时都新启动新线程处理。
    在这里插入图片描述

思考

尽管问题已经解决,但之前还有几个遗留的疑问没有解答。经过一番研究,我们也找到了答案。

为什么低版本镜像没问题?

在之前的分析中,我们提到了因为 EventLoop 线程数量过少导致线程阻塞。高版本的实例中 EventLoop 线程数量为 3,那么低版本的情况呢?通过Arthas 查看,发现低版本 Lettuce 的 EventLoop 数量是 13,远远超过了高版本的数量。这表示在低版本环境中,Pub/Sub 连接和其他连接会注册到不同的 EventLoop 上,即使 Pub/Sub 处理线程被阻塞,也不会影响到其他连接读写事件的处理

为什么低版本的镜像会创建更多的 EventLoop 呢?这其实是 JDK 的一个坑。早期的 JDK 8 版本(8u131 之前)存在docker环境下Java获取cpu核心数不准确的问题,会导致程序拿到的是宿主机的核数。
(https://blogs.oracle.com/java/post/java-se-support-for-docker-cpu-and-memory-limits)

查看低版本镜像的jdk版本是8u101,应用宿主机的核数是16,也就是说,低版本应用误拿到了宿主机的核数16,因此会将每个连接注册到一个独立的EventLoop上,从而避免了阻塞的发生。换句话说,之所以低版本镜像没问题,其实是程序在错误的环境下获取到错误的数值,却得到了正确的结果,负负得正了。至于为什么最大线程号是 13 ,这是由于我们的 Redis 集群配置了两个域名,如下图所示。

在 RedisClusterClient 初始化时,会分别对域名(2)、所有集群节点(6)、Pub/Sub 通道(1)、集群主连接(1)、副连接(3)进行连接创建,加起来一共正好是 13 个。

为什么高版本通过Spring访问Redis不会出现超时问题?

原始项目访问Redis有Spring和缓存框架两种方式。前文中提到的所有 EventLoop 都是由自研缓存框架维护的 RedisClusterClient 对象创建的。而Spring 容器会使用单独的 RedisClusterClient 对象来创建Redis连接。在 Lettuce 中,每个 RedisClusterClient 对象底层都对应着不同的 EventLoopGroup。也就是说,Spring 创建的Redis连接一定不会和缓存框架的连接共用同一个 EventLoop。因此即使缓存框架所在的 EventLoop 线程被阻塞,也不会影响到 Spring 连接的事件响应。

为什么高版本镜像访问单机Redis没问题?

与RedisClusterClient访问Redis集群时会创建多个主副连接不同,访问单机Redis时Lettuce使用的RedisClient只会创建1个连接。再加上独立的Pub/Sub连接,相当于是2个连接注册到3个EventLoop上,避免了冲突。

05-总结

本文从实际工作中遇到的一个Redis访问超时问题出发,探究背后Spring、Lettuce和Netty的工作原理,并利用Arthas等调试工具,分析了EventLoop线程对连接处理的重要性,以及在处理Pub/Sub事件时避免阻塞操作的必要性。通过观察不同版本环境下的行为差异,加深了对JDK版本和程序环境适配的理解,为今后排查类似问题积累了宝贵经验。

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

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

相关文章

stable diffusion Temporal-kit和EbSynth视频转动画学习笔记

1、打开stable diffsuion webui 点击Temporal-kit 页签,再点击预处理pre-processing,上传视频 在工作目录下得到拆分的关键帧,在input目录里 打开图生图,输入正反描述词,其他配置如下 批量生成图片,找到最满意的那一张&#xff0…

如何判别三角形和求10 个整数中最大值?

分享每日小题,不断进步,今天的你也要加油哦!接下来请看题------> 一、已知三条边a,b,c能否构成三角形,如果能构成三角形,判断三角形的类型(等边三角形、等腰三角形或普通三角形 …

【Interconnection Networks 互连网络】Torus 网络拓扑

1. Torus 网络拓扑2. Torus 网络拓扑结构References 1. Torus 网络拓扑 Torus 和 Mesh 网络拓扑,又可以称为 k-ary n-cubes,在规则的 n 维网格中包裹着 N k^n 个节点,每个维度都有 k 个节点,并且最近邻居之间有通道。k-ary n-c…

数据可视化(八):Pandas时间序列——动态绘图,重采样,自相关图,偏相关图等高级操作

Tips:"分享是快乐的源泉💧,在我的博客里,不仅有知识的海洋🌊,还有满满的正能量加持💪,快来和我一起分享这份快乐吧😊! 喜欢我的博客的话,记得…

关于杰理AC695蓝牙模式下按键处理函数处理

一、杰理蓝牙模式下又分为SYS_KEY_EVENT和SYS_BT_EVENT。SYS_KEY_EVEN主要是对按键的控制事件,SYS_BT_EVENT是蓝牙模式协议栈状态事件、hci事件、对箱事件。 二、按键通过key_event *key &event->u.key; u定义了一个union事件的联合体,通过按键k…

【C++ STL序列容器】array 数组

文章目录 【 1. 基本原理 】【 2. array 的创建 】2.1 不赋初值2.2 赋默认值2.3 赋指定值 【 3. array 的成员函数 】实例 【 1. 基本原理 】 array 是在 C 普通数组的基础上添加了一些成员函数和全局函数。在使用上,它 比普通数组更 安全,且效率并没…

Web3钱包开发获取测试币-Base Sepolia(二)

Web3钱包开发获取测试币-Base Sepolia(二) ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/b0c0ac86b04a496087471388532bc54a.png) 基于上篇 Web3钱包开发获取测试币-Polygon Mumbai(一) :https://suwu150.blog.csdn.net/article/details/137949473 我…

呼市经开区建设服务项目水、电能耗监测 数采案例

一、项目背景及需求 项目地点位于内蒙古呼和浩特市,呼市数字经开区建设服务项目。属于企业用能数据采集、能耗监测板块子项目。 针对水、电能耗数据采集,结合现场客观因素制约,数据采集方面存在较大难度。大多数国网电表485接口由于封签限制…

WordPress 告别 MySQL:Docker SQLite WordPress

本篇文章聊聊,如何将这个持续诞生和维护了 21 年的开源软件“脱离数据库”运行,让它能够更加轻量、适合低成本离线运行。 写在前面 2003 年,Michel Valdrighi 基于 b2/cafelog 创建了开源软件 WordPress,并在 GPL 协议下发布。 …

【ARM Trace32(劳特巴赫) 使用介绍 12.1 -- Trace32 读写 64位地址】

请阅读【Trace32 ARM 专栏导读】 文章目录 Trace32 读写 64位地址读 64 位地址写64位地址Trace32 读写 64位地址 在使用TRACE32进行调试时,有时需要读取或操作64位的地址,特别是在处理64位的处理器或操作系统时。以下是如何在TRACE32中读取64位地址的一般方法。 读 64 位地…

Hadoop大数据处理技术-配置连接篇

​2024/4/17 Hadoop学习前的准备 3)连接虚拟机 上一节配置完成了基础的虚拟机配置及网络配置 下面我们开始建立连接 我们为什么要与虚拟机建立链接呢? 连接虚拟机就好像跟亲友联系一样 总得找个便捷又好用的工具才行 Secure CRT就像是一把能打开通向…

高斯过程回归【详细数学推导】

机器学习笔记 第一章 机器学习简介 第二章 感知机 第三章 支持向量机 第四章 朴素贝叶斯分类器 第五章 Logistic回归 第六章 线性回归和岭回归 第七章 多层感知机与反向传播【Python实例】 第八章 主成分分析【PCA降维】 第九章 隐马尔可夫模型 第十章 奇异值分解 第十一章 熵…

AI-数学-高中-39空间向量-2空间向量法(法向量)

原作者视频:【空间向量】【一数辞典】2空间向量法(重要)_哔哩哔哩_bilibili 法向量(高中阶段所有与面的关系,都可以通过法向量去证明和解答): 是空间解析几何的一个概念,垂直于平面…

JAVA学习笔记28(常用类)

1.常用类 1.1 包装类 1.包装类的分类 ​ 1.针对八中基本数据类型相应的引用类型–包装类 ​ 2.有了类的特点,就可以调用类中的方法 2.包装类和基本数据类型的转换 ​ *装箱:基本类型 --> 包装类型 //手动装箱 int n1 100; Integer integer ne…

上市公司-企业数据要素利用水平数据集及参考文献(2010-2022年)

01、数据介绍 企业数据要素利用水平是指企业在其生产经营活动中,对数据的收集、处理、分析和应用的能力及效果。这种利用水平的高低直接反映了企业在数字化时代中的竞争力和创新能力。 本数据参考《中央财经大学学报》史青春(2023)老师的研…

Excel文件解析--超大Excel文件读写

使用POI写入 当我们想在Excel文件中写入100w条数据时,我们用普通的XSSFWorkbook对象写入时会发现,只有在将100w条数据全部加载入内存后才会用write()方法统一写入,这样效率很低,所以我们引入了SXSSFWorkbook进行超大Excel文件的读…

javaWeb项目-网吧网咖管理系统功能介绍

项目关键技术 开发工具:IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架:ssm、Springboot 前端:Vue、ElementUI 关键技术:springboot、SSM、vue、MYSQL、MAVEN 数据库工具:Navicat、SQLyog 1、JAVA简介 JavaSc…

本地环境通过ssh通道连接服务器数据库,实现本地客户端和代码可以访问数据库

使用方法: ssh -p 搭建隧道的端口 -fNL 本地端口:远程ip:远程端口号 搭建隧道的账号搭建隧道的ip 可以增加参数-v,输出更多的信息 ssh -p 搭建隧道的端口 -fNL 本地端口:远程ip:远程端口号 -v 搭建隧道的账号搭建隧道的ip 有时候,测试环境的数据库不允许…

深度卷积神经网络的整体运行流程(以alexnet为例)

0.基础概念(复习一下) 1.小批量随机梯度下降 目的: 希望找到最佳的参数,使损失函数最小。 使损失函数对w求导(b就是x等于1的w),一个小批次的/eta(学习率)*小批次的平均…

hexo配置教程、主题使用及涉及的技术学习

一、背景 最近,一直想做一个属于自己的网站.可以从零开始搭建一个网站,顺便可以把日常中学到的技术用于实战,还可以顺便记录自己的所思所感,记录成长的过程. 方案 一开始的方案是从零开始,模仿常见个人博客的设计,基于vueSpringbootMySQL的去实现网站. 新建项目之后,发现vu…