当前位置: 首页 > news >正文

RabbitMQ生产故障问题分析

1. 问题引发

  由某个服务BI-collector-xx队列出现阻塞,影响很整个rabbitMQ集群服务不可用,多个应用MQ生产者服务出现假死状态,系统影响面较广,业务影响很大。当时为了应急处理,恢复系统可用,运维相对粗暴的把一堆阻塞队列信息清空,然后重启整个集群。

在复盘整个故障过程中,我心中有不少疑惑,至少存在以下几个问题点:

  1. 为什么出现队列阻塞?
  2. 某个队列出现阻塞为什么会影响到其他队列的运行(即多队列间相互影响)?
  3. 某个应用MQ队列出现问题,为什么会导致应用不可用呢?

2. 试验队列阻塞

某天周末在家里,找个测试环境,安装rabbitmq尝试重现这过程,并做模拟测试。

写两个测试应用Demo(假设是两个项目应用)分别有生产者和消费者,并分别使用队列testA和testB。

为了尽可能还原生产的情况,一开始测试使用了同一个vhost,后面分别设置不同vhost。

生产者A,示例代码如下

消费者A

MQ配置

生产者B,每次生产10万条消息

 

消费者B,代码故意写错(模拟出现异常的情况),不是正常的json串导致解释json时抛出异常

先了解一下Rabbitmq客户端启动连接工作过程,通过wireshark抓包分析,如下

 

先对AMQP做一个简单的介绍,请求的AMQP协议方法信息,AMQP协议方法包含类名+方法名+参数,这一列主要展示了类名和方法名

  • Connection.Start请求服务端开始建立连接
  • Channel.Open请求服务端建立信道
  • Queue.Declare声明队列
  • Basic.Consume开始一个消费者,请求指定队列的消息

详细方法可以查看amqp官网https://www.rabbitmq.com/amqp-0-9-1-reference.html

工作过程分析:

Basic.Publish 客户端发送Basic.Publish方法请求,将消息发布到exchangerabbitmq server会根据路由规则转发到队列中;

Basic.Deliver 服务端发送Basic.Deliver方法请求,投递消息到监听队列的客户端消费者;

Basic.Ack 客户端发送Basic.Ack方法请求,告知rabbimq server,消息已接收处理。

两个应用程序启动后,通过rabbitmq管理控制台可以观察一些参数和监控指标

 

 

一开始A应用生产和消费都是正常的。

B消费端错误代码异常,狂刷报错信息

 

经过大概30分钟运行,观察A生产者应用控制台也有出现异常信息

 

查看服务端连接状态出现blocked情况,与生产故障发生情景很类似。

 

此时客户端即本机器,CPU和内存上涨明显,风扇声音很响,明显卡顿,再过30分钟应用基本不可用状态。

分析原因

上面错误代码展示了消费者B无法ack,由于没有进行ack导致队里阻塞。那么问题来了,这是为什么呢?其实这是RabbitMQ的一种保护机制。防止当消息激增的时候,海量的消息进入consumer而引发consumer宕机。

 RabbitMQ提供了一种QOS(服务质量保证)功能,即在非自动确认的消息的前提下,限制信道上的消费者所能保持的最大未确认的数量。可以通过设置prefetchCount实现,自动确认prefetchCount设置无效。

举例说明:可以理解为在consumer前面加了一个缓冲容器,容器能容纳最大的消息数量就是PrefetchCount。如果容器没有满RabbitMQ就会将消息投递到容器内,如果满了就不投递了。当consumer对消息进行ack以后就会将此消息移除,从而放入新的消息。

通过上面的配置发现prefetch初始我只配置了2,并且concurrency配置的只有1,所以当我发送了2条错误消息以后,由于解析失败这2条消息一直没有被ack。将缓冲区沾满了,这个时候RabbitMQ认为这个consumer已经没有消费能力了就不继续给它推送消息了,所以就造成了队列阻塞。

判断队列是否有阻塞的风险。

  当ack模式为manual,并且线上出现了unacked消息,这个时候不用慌。由于QOS是限制信道channel上的消费者所能保持的最大未确认的数量。所以允许出现unacked的数量可以通过channelCount * prefetchCount *消费节点数量得出。

channlCount就是由concurrency,max-concurrency决定的。

  • min = concurrency * prefetch *消费节点数量
  • max = max-concurrency * prefetch *消费节点数量

由此可以得出结论

  • unacked_msg_count < min 队列不会阻塞。但需要及时处理unacked的消息。
  • unacked_msg_count >= min 可能会出现堵塞。
  • unacked_msg_count >= max 队列一定阻塞。
重点注意

1unacked的消息在consumer切断连接后(如重启)再连接,会自动回到队头。

2、若将ack模式改成auto自动,这样会使QOS不生效。会出现大量消息涌入consumer从而可能造成consumer宕机风险。

再回看程序配置,做一些分析和调整

对B消费端问题代码加个try-catch-finally,不管中间有何问题,都进行消息签收ACK。

 

代码调整之后,两个队列正常运行,客户端两个应用也正常运行。

 

 

经过一段时间消费,B消费者端已经把堆积的消息消费完了。

 

3、    第三个问题原因分析

还是查看抓包信息

Basic.Reject 客户端发送Basic.Reject方法请求,表示无法处理消息,拒绝消息,此时的requeue参数为true,将消息返回原来的队列;

Basic.Deliver 服务端调用Basic.Deliver方法,和第一次Basic.Deliver方法不同的是,此时的redeliver参数为true,表示重新投递消息到监听队列的消费者,然后这两步会一直重复下去。

RabbitMQ消息监听程序异常时,consumer会向rabbitmq server发送Basic.Reject,表示消息拒绝接受,由于Spring默认requeue-rejected配置为true,消息会重新入队,然后rabbitmq server重新投递。就相当于死循环了,所以容易导致消费端资源占用过高,特别是TCP连接数、线程数、IO飙升,如果个别程序带事务或数据库操作等连接资源得不到释放也会占满,导致应用假死状态(出现问题的时候,查看问题应用出现大量的connection timeout错误报错日志)。

因此针对性的,有些业务场景(不强调数据强一致性的场景,比如日志收集)可以设置default-requeue-rejected: false即可。

factory.setDefaultRequeueRejected(false);

  会根据异常类型选择直接丢弃或加入dead-letter-exchange中。

消费者端正确的使用手动确认示例结构代码,很重要!

try {// 业务逻辑。
}catch (Exception e){// 输出错误日志。
}finally {// 消息签收。
}

4、    验证队列设置最大长度限制

设置queueLengthLimit队列最大长度限制 x-max-length=5

 

生产者原本想要生产10条消息

 

由于受到队列最大长度限制,实际上只有5条入队列里面。

 

消费者拿出来的消息,仅有5条,从NO.6~NO.10

改变消费者程序,让生产者一直产生消息,消费者消费速度明显赶不上生产者的生产速度

 

 

从消费端来看消息是随机性入队的,队列里面一直最多5条消息,发再多也进不了,消息者和生产者也不会发生什么异常,只是消息会随机性丢失(并没有全部入队)。

运行情况良好,除了消息没有全部入队列 ,没有出现异常情况

 

消费比较慢,本机器CPU和内存各项指标正常,没有异常。

搞一个异常情况出现unack,最大队列长度限制,是不算unack数量的,如下图所示

 

异常之后,此观察MQ监控管理后台

 

生产者不停一直在生产消息,运行30分钟,观察生产者应用也是正常的的,就是消息入不了队列。

 

 

5、  检查实际的业务端代码

再看我们业务系统消费端代码,消费端各种不规范写法都有,以下例举几个典型

1、手动签收有ACK,但是没有try-catch-finally结构,消费端业务代码如下:

2、有try-catch-finally结构,但是deliverTag是一个固定值0,一样的会出问题。

 

3、自动签收确认的,大量消息的时候,容易搞死消费端应用。

 

6. 总结

  • 生产环境不建议使用自动ack模式,这样会使QOS无法生效。
  • 在使用手动ack的时候,需要非常注意消息签收,业务代码使用try-catch-finally处理结构,防止业务代码异常时无法签收。
  • 规范约束mq客户端代码,正确的使用Rabbitmq配置。
  • 不同业务项目设置不同的vhost可以隔离一些影响,提升rabbitmq资源使用。
  • 考虑设置dead-letter-exchange,当设置了 requeue=false时,可以放入dead-letter-exchange,可以快速排查定位问题。
  • Exchange和队列的最大长度限制可以是限制消息的数量(参数:x-max-length),或者是消息的总字节数(总字节数表示的是所有的消息体的字节数,忽略消息的属性和任何头部信息),又或者两者都进行了限制,两者取小值生效,只有处于ready状态的消息被计数未被确认的消息不会被计数受到limit的限制。最大队列设置可以限制生产端,但会造成消息丢失风险,最大消息数量限制,不能完全解决队列阻塞问题。
  • 尽量使用Direct-exchange,Direct 类型的 Exchange 投递消息是最快的。
    • Direct:处理路由键,需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键为“A”,则只有路由键为“A”的消息才被转发,不会转发路由键为"B",只会转发路由键为“A”;
    • Topic:将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”只能匹配一个词;
    • Fanout:不处理路由键。只需要简单的将队列绑定到交换机上。一个发送到该类型交换机的消息都会被广播到与该交换机绑定的所有队列上;
    • Headers:不处理路由键,而是根据发送的消息内容中的 headers 属性进行匹配。在绑定 Queue 与 Exchange 时指定一组键值对;当消息发送到 RabbitMQ 时会取到该消息的 headers 与 Exchange 绑定时指定的键值对进行匹配;如果完全匹配则消息会路由到该队列,否则不会路由到该队列。

相关文章:

RabbitMQ生产故障问题分析

1. 问题引发 由某个服务BI-collector-xx队列出现阻塞&#xff0c;影响很整个rabbitMQ集群服务不可用&#xff0c;多个应用MQ生产者服务出现假死状态&#xff0c;系统影响面较广&#xff0c;业务影响很大。当时为了应急处理&#xff0c;恢复系统可用&#xff0c;运维相对粗暴的把…...

12大常用自动化测试工具,请记得转发收藏!

常用自动化测试工具 1、Appium AppUI自动化测试 Appium 是一个移动端自动化测试开源工具&#xff0c;支持iOS 和Android 平台&#xff0c;支持Python、Java 等语言&#xff0c;即同一套Java 或Python 脚本可以同时运行在iOS 和Android平台&#xff0c;Appium 是一个C/S 架构&…...

Android Studio 的aapt2.exe在哪个目录下

一般在&#xff1a;C:\Users\admin\AppData\Local\Android\Sdk\build-tools\30.0.2&#xff08;不一定是30.0.2&#xff0c;这个得看你的版本&#xff09; 怎么找&#xff1a; 1.打开Android studio...

【pytest】conftest.py使用

1. 创建test_project 目录 test_project/sub/test_sub.py def test_baidu(test_url):print(fsub {test_url}) test_project/conftest.py 设置钩子函数 只对当前目录 和子目录起作用 import pytest #设置测试钩子函数 pytest.fixture() def test_url():return "http…...

SpringBoot集成Prometheus实现监控

SpringBoot配置Prometheus pom.xml 引入监控以及prometheus依赖 <dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId></dependency><dependency><groupId>org.springfram…...

【操作系统笔记十】缓存一致性

CPU 核心之间数据如何传播 高速缓存中的值被修改了&#xff0c;那么怎么同步到内存中呢&#xff1f; ① 写直达&#xff08;Write-Through&#xff09;② 写回&#xff08;Write-Back&#xff09; 写直达&#xff08;Write-Through&#xff09; 简单&#xff0c;但是很慢&am…...

lS1028 + 六网口TSN 硬交换+QNX/Linux实时系统解决方案在轨道交通系统的应用

lS1028 六网口TSN 硬交换QNX/Linux实时系统解决方案在轨道交通系统的应用 以下是在轨道交通应用的实物&#xff1a; CPUNXP LS1028A架构双核Cortex-A72主频1.5GHzRAM2GB DDR4ROM8GB eMMCOSUbuntu20.04供电DC 12V工作温度-40℃~ 80℃ 功能数量参数Display Port≤1路支持DP1.3…...

实现字符串反转函数

实现字符串反转 #include <stdio.h> #include <string.h>void reverse(char *str) {int len = strlen(str);...

抽检监测实施

声明 本文是学习GB-T 42893-2023 电子商务交易产品质量监测实施指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件提供了开展电子商务交易的有形产品质量监测的总则&#xff0c;监测准备、监测实施、监测效果评价 与反馈等过程指导…...

C++中的静态库与动态库

文章目录 静态库构建静态库 动态库构建动态库 它们的不同参考文章 单独提这个 库&#xff0c;我想我们在coding过程中&#xff0c;可能也会知道一两个词&#xff0c;如 标准库、xx库等。库作为一组已编写好、组织好的、可复用的资源接口&#xff0c;可以被用于其他程序。很不…...

UGUI 绘制线段

描述 点击鼠标左键在屏幕上绘制线段 准备 VertexHelper 网格绘制工具类向量、叉乘RectTransformUtility.ScreenPointToLocalPointInRectangleSetVerticesDirtyOnPopulateMesh 思路 鼠标按下&#xff0c;记录线段起点&#xff1b;鼠标持续按下&#xff0c;记录鼠标当前帧的…...

详细学习Mybatis(2)

详细学习Mybatis&#xff08;2&#xff09; 一、Mybatis核心配置文件详细解释1.1 environment&#xff08;环境&#xff09;1.2 事务管理器&#xff08;transactionManager&#xff09;1.3、dataSource&#xff08;数据源&#xff09;1.4、properties1.5、mapper 一、Mybatis核…...

LinkedList与链表

目录 一、Arraylist的缺陷 二、链表 2.1 链表的概念和结构 2.2 链表的实现 三、链表面试题 3.1 删除链表中所有值为val的节点 3.2 反转一个单链表 3.3 链表的中间节点 3.4 将有序链表合并 3.5 输出倒数第k个节点 3.6 链表分割 3.7 链表的回文结构 3.8 找两个链表的公共节…...

纳米软件芯片自动化测试系统测试电源芯片稳压反馈的方法

在一些电源芯片或稳压芯片中&#xff0c;通常内部都会有稳压反馈电路&#xff0c;这些电路可以将输入电压通过内部调整后输出一个稳定的输出电压&#xff0c;以满足电路中的稳定电源需求。也就是说芯片的稳压反馈就是内部稳压反馈电路中的电压。 芯片稳压反馈原理介绍 稳压反馈…...

微信小程序之项目基本结构、页面的基础及宿主环境

文章目录 前言一、基本组成结构基本组成小程序页面的组成部分JSON配置文件作用 二、页面基础pagesWXML和HTML的区别WXSS和CSS的区别小程序中js文件分类 三、小程序宿主环境总结 前言 微信小程序的项目基本结构、页面的基础及宿主环境 一、基本组成结构 基本组成 新建一个微信…...

C/C++鸡尾酒疗法 2023年5月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C鸡尾酒疗法 一、题目要求 1、编程实现 2、输入输出 二、解题思路 1、案例分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 C/C鸡尾酒疗法 2020年6月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 鸡尾酒疗法&#xff0c;原指“高效抗…...

人工智能及大模型简介

一、人工智能介绍 人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;英文缩写为AI。它试图赋予机器智能的能力&#xff0c;使它们能够像人类一样思考、学习和做出决策。它的核心要素是数据、模型和算力。   数据是人工智能的基础&#xff0c;数据的质量和…...

基于springboot消防员招录系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...

手把手教你制作登录、注册界面 SpringBoot+Vue.js(cookie的灵活运用,验证码功能)

一、用户登录界面 实现思路&#xff1a;用户在界面输入用户名和密码传入变量。用post方法传输到后端&#xff0c;后端接收整个实体对象。将用户名提取出。在dao层方法中通过select注解查询&#xff0c;返回数据库对应的数据对象。如果返回为空则return false。不为空则通过比对…...

C++ Qt零基础入门进阶与企业级项目实战教程与学习方法分享

Qt是一个卓越的客户端跨平台开发框架&#xff0c;可以在Windows、Linux、macOS进行客户端开发&#xff0c;无缝切换&#xff0c;一统三端&#xff1b;当然除了桌面端&#xff0c;在移动端的早期&#xff0c;Qt也展现了其多才多艺&#xff0c;在Android和ios也可以使用Qt编写app…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...