rabbitmq 重复ACK导致消息丢失

简介: rabbitmq 重复ACK导致消息丢失 太重要了所以转载,作为私用,希望原作者不要怪我 rabbitmq 重复确认导致消息丢失 背景 rabbitmq 在应用场景中,大多采用工作队列 work-queue的模式。

rabbitmq 重复ACK导致消息丢失

太重要了所以转载,作为私用,希望原作者不要怪我

rabbitmq 重复确认导致消息丢失

背景

rabbitmq 在应用场景中,大多采用工作队列 work-queue的模式。

在一个常见的工作队列模式中,消费者 worker 将不断的轮询从队列中拉取最新消息,当队列负载压力增大时允许添加多个worker 进行处理。
然而执行一个任务可能需要相当的时长,这是由业务特性所决定的;如果 worker执行任务过程中出现异常甚至宕机,此时消息便会丢失,这是简单消息队列难以解决的问题。

rabbitmq 采用了消息确认机制来防止此类问题,在该机制中,worker需要向 MQ Server 返回 ACK响应以表示消息已确认处理;
在以下情况下,rabbitmq 会对消息进行重新投递:
1 client 未响应ACK, 主动关闭 Channel;
2 client 未响应ACk, 网络异常断开;

消息的重发机制没有超时限制,只要client 不响应ACK,那么会一直投递;
如果启用了消息持久化机制,那么消息将有进一步的保障。

问题描述及分析

1 客户端为简化应答处理,可以设置自动应答选项,如:

  boolean autoAck = false;
  channel.basicConsume(TASK_QUEUE_NAME, autoAck, consumer);

2 如果不启用自动应答,需要应用代码手动进行应答:

    try {
          doWork(message);
        } finally {
          logger.info(" xxx work done");
          channel.basicAck(envelope.getDeliveryTag(), false);
        }

3 当两种方案同时存在

由于客户端的编码失误,先启用了自动应答选项,又在应用代码执行了应答的代码:
     // enable autoAck
     boolean autoAck = true;
     consumerChannel.basicConsume(queueName, autoAck, this);

     //...

     // snipper from Consumer.handleDelivery method
     // send ack to server  
     try {
            consumerChannel.basicAck(deliveryTag, true);
        } catch (Exception e) {
     }

多了一次确认,应用代码貌似一切如常。 但在频繁进行消息收发测试时发现 消息存在随机性丢失处理的情况!
检查 rabbitmq server日志发现以下异常:

  {amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'}
  ...
  {amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'}
  ...
  {amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'}
  ...

提示未知的 delivery tag=1,该字段为MQ server 用于消息确认的标记,服务端因无法识别而打印错误。
另外一个现象则是,连续收发消息 5次,其中丢失消息处理1次,而 rabbitmq server错误日志出现 4次!

经过分析,发现问题原因所在:
rabbitmq 为每一个channel维护了一个delivery tag的计数器,这里采用正向自增,新消息投递时自增,当消息响应时自减;
在连续收发的场景中,由于消息发送的间隔较短,部分消息因 consumer的重复确认被rabbitmq 当做已处理而丢弃。

解决方案

取消consumer 的自动应答机制,仅保留手动应答的处理,问题解决。

参考资料


关于 rabbitmq 消息确认机制:
http://www.rabbitmq.com/confirms.html#when



相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
消息中间件 存储 Kubernetes
k8s1.20版本部署RabbitMQ集群(持久化)——2023.05
k8s1.20版本部署RabbitMQ集群(持久化)——2023.05
738 1
|
2月前
|
消息中间件 数据采集 数据库
小说爬虫-02 爬取小说详细内容和章节列表 推送至RabbitMQ 消费ACK确认 Scrapy爬取 SQLite
小说爬虫-02 爬取小说详细内容和章节列表 推送至RabbitMQ 消费ACK确认 Scrapy爬取 SQLite
25 1
|
消息中间件 NoSQL 关系型数据库
【Kubernetes部署Shardingsphere、Mycat、Mysql、Redis、中间件Rocketmq、Rabbitmq、Nacos】
【Kubernetes部署Shardingsphere、Mycat、Mysql、Redis、中间件Rocketmq、Rabbitmq、Nacos】
273 0
|
消息中间件
rabbitmq消息的确认机制ack
rabbitmq消息的确认机制ack
|
消息中间件 存储 安全
|
消息中间件
RabbitMQ手动ACK与死信队列
RabbitMQ手动ACK与死信队列
263 0
|
消息中间件 存储 前端开发
硬核!SpringBoot集成RabbitMQ消息队列搭建与ACK消息确认入门
硬核!SpringBoot集成RabbitMQ消息队列搭建与ACK消息确认入门
479 0
硬核!SpringBoot集成RabbitMQ消息队列搭建与ACK消息确认入门
|
消息中间件 JavaScript 前端开发
JavaScript 连接消息(RabbitMQ)
JavaScript 连接消息(RabbitMQ)
JavaScript 连接消息(RabbitMQ)
|
消息中间件 NoSQL 关系型数据库
RabbitMQ消息丢失、积压、重复等解决方案
RabbitMQ消息丢失、积压、重复等解决方案
RabbitMQ消息丢失、积压、重复等解决方案
|
消息中间件 存储 NoSQL
springcloud:springboot整合RabbitMQ|RabbitMQ保证消息可靠性(三)
上一章我们讲解了rabbitmq的四种交换机类型、七种通讯方式。本章我们将整合springboot来向大家完整演示rabbitmq的使用,并说明如何保证消息的可靠性。
612 0
springcloud:springboot整合RabbitMQ|RabbitMQ保证消息可靠性(三)