- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
RabbitMQ 生产故障问题分析 。
由某一次真实生产环境rabbitMQ故障引发血案,下面复盘问题发生原因以及问题解决方法.
1、 问题引发 。
由某个服务BI-collector-xx队列出现阻塞,影响很整个rabbitMQ集群服务不可用,多个应用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 方法请求,将消息发布到 exchange , rabbitmq 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
队列一定阻塞。 1 、 unacked 的消息在 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、 总结 。
。
写在最后,RabbitMQ集群做为整个平台关键部件,它的好处自然不用再说,但是它也不是万金油,一旦岩机影响很大,后果比较严重。怎么用好它?我们有必要正确深入的认识并使用它,首先得摆好正确的姿势(写正确的客户端代码、严谨的配置),不能随意,否则后果很严重。希望经过此故障经验教训能与君共勉,同时也希望我的总结能够给大家一点帮助和启发,权当抛砖引玉.
最后此篇关于RabbitMQ真实生产故障问题还原与分析的文章就讲到这里了,如果你想了解更多关于RabbitMQ真实生产故障问题还原与分析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在一个网站上工作,该网站在生产中只有 aspx 文件和 bin 目录和文件。任何人都知道这个网站是如何部署的,我通常有我的网站,我也会提交代码。 我的问题 2. 如何在同一台服务器上创建测试网站?我
您好,我认为这应该是一个相当简单的问题,但我对管理 git 不太熟悉。 我使用的是非常流行的 http://nvie.com/posts/a-successful-git-branching-mode
目前我的网站(生产服务器)已经有很多代码了。现在我想开始在我的项目中使用 Git 并为我的团队设置一个暂存服务器。谁能给我任何建议? 这是我脑海中的画面: Production
我目前正在学习 Erlang SO 用户能否提供有关他们的任何 Erlang 应用程序部署的有趣示例? 我想深入了解 Erlang 在过去的电信中的常见用途,以及 Erlang 在开发/部署过程中带来
我关注了Ryan's screencast并部署到 VPS。所以我使用 Unicorn + nginx + github + Ubuntu 12.04 LTS + capistrano。我也使用 i1
我想在 Azure 中维护临时环境和生产环境。每个都应该有自己的 blob 存储和 sql 存储。实现这一目标的最佳方法是什么?设置临时和生产 SQL Server 以及两个 Blob 存储帐户? 最
我无法使用 Electron 打包程序在内置的 Electron 应用程序中打开chrome开发工具。 我已经尝试过mainWindow.webContents.openDevTools(),但这没有
我有一个 Azure 应用程序服务环境。 可以在同一个 ASE 中运行多个应用服务计划(开发、测试和生产)吗? 基本上,我知道他们会共享前端池,我认为这很好,因为那里没有运行应用程序代码,并且它“..
我是 Maven 新手,有 Rails 背景。在较高级别上,如果我正在运行测试、在本地运行应用程序以及在部署到生产环境时,我希望连接到不同的数据库。 这就是我的想法。当我运行 mvn test 时,它
我有一个 Azure 应用程序服务环境。 可以在同一个 ASE 中运行多个应用服务计划(开发、测试和生产)吗? 基本上,我知道他们会共享前端池,我认为这很好,因为那里没有运行应用程序代码,并且它“..
我正在使用 faSTLane\produce 脚本制作一个新应用程序,我收到以下错误消息: in `parse_response': {"data"=>nil, "messages"=>{"warn"
使开发人员能够构建包含私有(private)数据的系统的当前做法是什么?谁能指出这类事情的“最佳实践”指南? 我们这里有一个 Catch-22,因为开发人员需要编写与具有被认为是“私有(private
我有一个连接 Azure SQL Server 的 Azure 云服务。当我第一次设置这个时,我真的不太了解自己在做什么,只是想熟悉 Azure。所以现在我想利用我所拥有的东西并将其转变为可靠的部署结
我是 Cordova 的新手。抱歉,如果这些是业余问题。我想详细了解典型手机应用程序的设置和架构。 我有一个本地版本的 Meteor Cordova 正在运行,它通过 Modulus 连接到远程服务器
我一直在寻找一些在一些 POS(销售点)设备和服务器之间同步数据的选项。 SymmetricDS似乎是具有商业友好许可证的选项之一。作为一个 Codehaus 项目确实保证了一定程度的质量,所以我同意
在 PHP 开发中,可以通过服务器的“环境”变量确定应用程序是在生产环境还是开发环境中运行。 在 tomcat 服务器上是否有类似的变量可用,或者是否有更好的方法将应用程序用于生产和开发? 最佳答案
我正在做一个项目,我需要使用 TwitterAPI 检索 Twitter 消息,处理它们并将它们存储在数据库中。我正在使用 Producer/Consumer BlockingQueue,其中元素的作
这个问题类似于:iPhone development - what is the difference between a development and distribution provision
我正在尝试根据 URL 在 Drupal 中设置环境。例如,如果我访问 mysite.local,它将使用 localdb 并将站点名称更改为“Local Mysite”;如果我转到 mysite.c
我今天一直在阅读 Magento 中的数据库同步。 我目前正在努力解决的一件事是在开发期间和上传到生产期间需要同步什么。现在假设一批更改将包含对数据库和类似代码的更改,下面是我对模型工作流的理解(我目
我是一名优秀的程序员,十分优秀!