博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
rabbitmq 重复ACK导致消息丢失
阅读量:6797 次
发布时间:2019-06-26

本文共 1815 字,大约阅读时间需要 6 分钟。

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 消息确认机制:

img_9b09a36f6de95886f52ce82fa1e89c88.jpe

作者:

出处: , 如果喜欢我的文章,请关注我的公众号

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出  如有问题, 可留言咨询.

你可能感兴趣的文章
《 线性代数及其应用 (原书第4版)》——2.8 R^n的子空间
查看>>
《Web异步与实时交互——iframe AJAX WebSocket开发实战》—— 1.1 基于HTTP协议的Web交互...
查看>>
LFCS 系列第十三讲:如何配置并排除 GNU 引导加载程序(GRUB)故障
查看>>
《HttpClient 官方文档》第五章 Fluent API
查看>>
初创公司如何快速低耗实现数据化运营
查看>>
《循序渐进学Docker》——导读
查看>>
《树莓派开发实战(第2版)》——1.8 使用复合视频显示器/TV
查看>>
编码之道:取个好名字很重要
查看>>
《树莓派开发实战(第2版)》——1.5 通过NOOBS刷写microSD卡
查看>>
《Python Cookbook(第3版)中文版》——1.7 让字典保持有序
查看>>
在 Linux 中设置 sudo 的十条 sudoers 实用配置
查看>>
Linux 有问必答:如何在 Linux 中永久修改 USB 设备权限
查看>>
《第三方JavaScript编程》——7.2 跨站脚本
查看>>
《师兄教你找工作——100场面试 20个offer背后的求职秘密》一导读
查看>>
《C++面向对象高效编程(第2版)》——第3章3.1 类概念的基础
查看>>
《 Python树莓派编程》——第2章 轻松掌握Linux 2.1 开始使用树莓派的Linux
查看>>
MySQL使用初步—mysql数据库的基本命令
查看>>
如何配置 MongoDB 副本集
查看>>
《Python核心编程(第二版)》——1.5 运行Python
查看>>
Node.js Undocumented(1)
查看>>