一个关于客户端(消费者)开启自动应答,重启后"未处理消息丢失"的小坑。(主要是对RabbitMQ理解不够)

首先,申明一下: 本文所谓的 "丢失消息" 不是指服务器宕机、重启等原因导致内存中消息丢失,也就是说不是关于消息持久化的问题。

  使用C# 编写测试。

  问题表象:  消费者开启自动应答,某时,消费者掉线(关闭/崩溃等),届时重启消费者,发现消费者未处理完的消息丢失

  条件: 服务器不宕机、不重启,只有一个消费者、一个生产者。

  消息流向:  消息--->生产者--->交换器--->队列--->消费者

  问题的处理: 消费者开启手动应答,若再出现之前情况,消息不丢失。

  先给个代码。

  生产者代码如下:

static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
          //申明广播类型交换器
channel.ExchangeDeclare(exchange: "ex1", type: "fanout");
          //申明队列
channel.QueueDeclare(queue: "test1",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null); int count = 0; while (true)
{
count++;
var body = Encoding.UTF8.GetBytes(count.ToString());
            //向key为 p 的交换器 ex1 上推数据
channel.BasicPublish(exchange: "ex1",
routingKey: "p",
basicProperties: null,
body: body); Console.WriteLine($"send msg {count}");
System.Threading.Thread.Sleep(1000);
} }
}

消费者代码如下(开启自动应答):

static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
          //队列与交换器绑定
channel.QueueBind(queue: "test1",
exchange: "ex1",
routingKey: "p"); var consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body); Console.WriteLine($"收到消息 -- {message}"); System.Threading.Thread.Sleep(2000);
}; channel.BasicConsume(queue: "test1",
autoAck: true,
consumer: consumer); Console.ReadLine();
} }

*交换器、队列必须先申明,两样都存在后才能进行绑定。

生产者向队列推送消息,队列中展现状态为ready的数据就是未被消费的消息。

推送90个消息:

队列中存了90个未应答的消息

打开消费者:

发现现在消息已经全部被自动应答,队列已清空。

再启动消费者:

如预料,空空一片。

小结 :开启消费者(开启自动应答),发现队列中状态为Ready的消息全部被应答,队列中状态为Ready的消息清空,不等消费者处理完这些消息,关闭消费者,然后再开启消费者,消费者不会再收到消息,出现消费者"未处理"完的消息丢失的问题。

同之前先屯90个消息。

然后关闭自动应答。

开启消费者:

消息状态一次性全部变成unacked。 因为没有写手动处理消息的逻辑,所以unacked状态的消息不会变少。

然后关闭消费者:

RabbitMQ 未删除无应答的消息,消息重新转为Ready状态,继续等待连接消费者处理。

再开启消费者:

没有出现丢失未处理完消息的情况。

    小结:开启消费者(关闭自动应答),发现队列中状态为Ready的消息状态全部转变为unacked,队列中状态为ready的消息清空,随消费者应答,队列中状态为unacked的消息逐渐减少,关闭消费者,发现队列中状态为unacked的消息重新改变回ready状态,

      

  结论:

  关闭自动应答可避免这种消息"丢失的情况"。

  另外在开启自动应答 ack=true 的情况下,需要保证一定有消费者在线,才能保证消息都被接收处理。开启手动应答必然消耗更多资源,因为 RabbitMQ 需要根据应答标号去删除队列中对应的消息。

以上仅个人理解,若有错误,欢迎指正~

  

RabbitMQ-消费者"未处理完的消息"丢失的更多相关文章

  1. RabbitMQ处理未被路由的消息

    我们经常使用消息队列进行系统之间的解耦,日志记录等等.但是有时候我们在使用 RabbitMQ时,由于exchange.bindKey.routingKey没有设置正确,导致我们发送给交换器(excha ...

  2. RabbitMQ防止消息丢失

    转载请注明出处 0.目录 RabbitMQ-从基础到实战(1)— Hello RabbitMQ RabbitMQ-从基础到实战(3)— 消息的交换 1.简介 RabbitMQ中,消息丢失可以简单的分为 ...

  3. RabbitMQ,RocketMQ,Kafka 事务性,消息丢失和消息重复发送的处理策略

    消息队列常见问题处理 分布式事务 什么是分布式事务 常见的分布式事务解决方案 基于 MQ 实现的分布式事务 本地消息表-最终一致性 MQ事务-最终一致性 RocketMQ中如何处理事务 Kafka中如 ...

  4. RabbitMQ-从基础到实战(2)— 防止消息丢失

    转载请注明出处 1.简介 RabbitMQ中,消息丢失可以简单的分为两种:客户端丢失和服务端丢失.针对这两种消息丢失,RabbitMQ都给出了相应的解决方案. 2.防止客户端丢失消息 如图,生产者P向 ...

  5. RabbitMQ:消息丢失 | 消息重复 | 消息积压的原因+解决方案+网上学不到的使用心得

    前言 首先说一点,企业中最常用的实际上既不是RocketMQ,也不是Kafka,而是RabbitMQ. RocketMQ很强大,但主要是阿里推广自己的云产品而开源出来的一款消息队列,其实中小企业用Ro ...

  6. rabbitmq 重复ACK导致消息丢失

    rabbitmq 重复确认导致消息丢失 背景 rabbitmq 在应用场景中,大多采用工作队列 work-queue的模式. 在一个常见的工作队列模式中,消费者 worker 将不断的轮询从队列中拉取 ...

  7. RabbitMQ消息丢失问题和保证消息可靠性-消费端不丢消息和HA(二)

    继续上篇文章解决RabbitMQ消息丢失问题和保证消息可靠性(一) 未完成部分,我们聊聊MQ Server端的高可用和消费端如何保证消息不丢的问题? 回归上篇的内容,我们知道消息从生产端到服务端,为了 ...

  8. 解决RabbitMQ消息丢失问题和保证消息可靠性(一)

    原文链接(作者一个人):https://juejin.im/post/5d468591f265da03b810427e 工作中经常用到消息中间件来解决系统间的解耦问题或者高并发消峰问题,但是消息的可靠 ...

  9. 如何处理RabbitMQ 消息堆积和消息丢失问题

    消息堆积 解决方案: 增加消费者或后台相关组件的吞吐能力 增加消费的多线程处理 根据不同的业务实现不同的丢弃任务,选择不同的策略淘汰任务 默认情况下,RabbitMQ消费者为单线程串行消费,设置并行消 ...

随机推荐

  1. Sharepoint JSCOM 列表操作

    SP.SOD.executeFunc('sp.js', 'SP.ClientContext', retrieveListItemsInclude); //确保js文件加载,再执行方法 function ...

  2. 牛客假日团队赛1 A.蹄球锦标赛

    链接: https://ac.nowcoder.com/acm/contest/918/A 题意: 为了准备即将到来的蹄球锦标赛,Farmer John正在训练他的N头奶牛(方便起见,编号为1-N,其 ...

  3. ACM-ICPC 2018 徐州赛区网络预赛 B(dp || 博弈(未完成)

    传送门 题面: In a world where ordinary people cannot reach, a boy named "Koutarou" and a girl n ...

  4. python_魔法方法(五):描述符和定制序列

    描述符(property的原理) 描述符(descripto),用一句话来解释,描述符就是某种特殊的类的实例指派给另一个类的属性.那么什么是特殊类型的类呢?就是至少要在这个类中定义__get__(). ...

  5. (转)linux route命令详解

    linux route命令详解 原文:https://www.cnblogs.com/lpfuture/p/5857738.html   &&   http://blog.csdn.n ...

  6. json数据前台解析 修改check属性用prop()

    jQuery中的$.getJSON( )方法函数主要用来从服务器加载json编码的数据,它使用的是GET HTTP请求.使用方法如下: $.getJSON( url [, data ] [, succ ...

  7. Spark编程环境搭建及WordCount实例

    基于Intellij IDEA搭建Spark开发环境搭建 基于Intellij IDEA搭建Spark开发环境搭——参考文档 ● 参考文档http://spark.apache.org/docs/la ...

  8. 你一直在用的 Spring Boot Starters 究竟是怎么回事

    Spring Boot 对比 Spring MVC 最大的优点就是使用简单,约定大于配置.不会像之前用 Spring MVC 的时候,时不时被 xml 配置文件搞的晕头转向,冷不防还因为 xml 配置 ...

  9. SpringBoot | 第四章:日志配置(转)

    前言 介于平时工作中,对于日志这块没有过多的接触,也就未有过多的了解.故在编写本文时,上官网查看了相关资料,奈何每个字母我都认识,但合起来就有点晕了,英文阅读水平还是有待大大的提高呀.最后觉得还是转载 ...

  10. 并发包阻塞队列之ArrayBlockingQueue

    并发包阻塞队列之ArrayBlockingQueue   jdk1.7.0_79  上一节中对并发包中的非阻塞队列ConcurrentLinkedQueue的入队.出队做了一个简要的分析,本文将对并发 ...