RabbitMQ消息的重复消费问题如何解决?

在RabbitMQ中,消息重复消费是一个常见问题,它通常发生在消费者处理消息时出现网络波动、节点故障或消费者自身处理逻辑异常,ACK 失败等情况,都会导致RabbitMQ 不能够正确感知消息已被成功处理,从而重新投递消息。以下是几种常见的解决RabbitMQ消息重复消费问题的方法:

消息幂等性处理(业务上)

  • 幂等性是指对同一操作的多次执行所产生的影响与一次执行的影响相同。在消息处理场景中,意味着无论消息被消费多少次,对业务的最终影响是一致的。
  • 消费者 在业务逻辑中通过记录已处理消息的标识来保证幂等性。比如维护一个内存中的Set集合,每次处理消息前,先检查消息的唯一标识是否已在集合中。如果已存在,则说明该消息已被处理过,直接返回;如果不存在,则处理消息并将标识添加到集合中。
java 复制代码
public class MessageProcessor {
    // 已消费的消息
    private static final Set<String> processedMessages = new HashSet<>();

    public void processMessage(String messageId, String messageContent) {
        if (processedMessages.contains(messageId)) {
            // 消息已处理过,直接返回
            return;
        }
        // 处理消息
        System.out.println("处理消息:" + messageContent);
        // 将消息标识添加到已处理集合
        processedMessages.add(messageId);
    }
}
  • 利用缓存(如Redis)来记录已处理消息的标识。每次消费消息时,先查询缓存中是否已存在该消息标识。如果存在,说明消息已被处理过,直接丢弃;如果不存在,则处理消息,并将消息标识存入缓存。缓存可以设置过期时间,以避免缓存数据无限增长。
  • 或者建立消息去重表,将已经处理过的消息的唯一键记录在数据库,每次去数据库查询是否处理过此消息。

使用 RabbitMQ 的确认机制

  • RabbitMQ提供了两种确认机制,分别是自动确认(autoAck=true)和手动确认(autoAck=false)。自动确认模式下,RabbitMQ在消息发送给消费者后,会立即认为消息已被成功消费,这种模式可能导致消息重复消费。所以需要进行手动确认,消费者处理完消息后,需要显式地调用basicAck方法通知RabbitMQ消息已被成功处理。如果消费者在处理消息过程中出现异常或未发送basicAck,RabbitMQ会认为消息未被成功消费,从而重新投递消息。
java 复制代码
public class ManualAckConsumer {
    private static final String QUEUE_NAME = "normal_ack_queue";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 设置为手动确认模式
        boolean autoAck = false;
        channel.basicConsume(QUEUE_NAME, autoAck, "normal-ack-consumer",
                false, false, null, new DefaultConsumer(channel) {
                    @Override
                    public void handleDelivery(String consumerTag,
                                               Envelope envelope,
                                               AMQP.BasicProperties properties,
                                               byte[] body) throws IOException {
                        String message = new String(body, "UTF - 8");
                        System.out.println(" [x] 收到消息: '" + message + "'");
                        try {
                            // 模拟消息处理
                            Thread.sleep(1000);
                            // 手动确认消息
                            channel.basicAck(envelope.getDeliveryTag(), false);
                            System.out.println(" [x] 消息确认成功");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
    }
}

使用 RabbitMQ 的 Message Deduplication 插件

在消息属性中增加唯一 ID,Message Deduplication 插件基于生产者发送消息时携带的唯一 ID,在 RabbitMQ 服务器端对消息进行去重处理。它会维护一个去重表(通常存储在内存或磁盘,取决于配置),记录已经处理过的消息 ID。当新消息到达时,插件会检查去重表,若发现消息 ID 已存在,则丢弃该消息;否则,将消息 ID 记录到去重表并正常处理消息。

相关推荐
TTBIGDATA1 天前
【Atlas】Atlas Hook 消费 Kafka 报错:GroupAuthorizationException
hadoop·分布式·kafka·ambari·hdp·linq·ranger
m0_687399841 天前
telnet localhost 15672 RabbitMQ “Connection refused“ 错误表示目标主机拒绝了连接请求。
分布式·rabbitmq
陌上丨1 天前
生产环境分布式锁的常见问题和解决方案有哪些?
分布式
新新学长搞科研1 天前
【智慧城市专题IEEE会议】第六届物联网与智慧城市国际学术会议(IoTSC 2026)
人工智能·分布式·科技·物联网·云计算·智慧城市·学术会议
Ronin3051 天前
日志打印和实用 Helper 工具
数据库·sqlite·rabbitmq·文件操作·uuid生成
泡泡以安1 天前
Scrapy分布式爬虫调度器架构设计说明
分布式·爬虫·scrapy·调度器
没有bug.的程序员1 天前
RocketMQ 与 Kafka 深度对垒:分布式消息引擎内核、事务金融级实战与高可用演进指南
java·分布式·kafka·rocketmq·分布式消息·引擎内核·事务金融
上海锟联科技1 天前
250MSPS DAS 在地铁监测中够用吗?——来自上海锟联科技的工程实践
分布式·科技·分布式光纤传感·das解调卡·光频域反射·das
岁岁种桃花儿1 天前
深度解析DolphinScheduler核心架构:搭建高可用Zookeeper集群
linux·分布式·zookeeper
yxy___1 天前
达梦分布式集群DPC_影子和实体副本相互转换(DEM)_yxy
分布式·dem·影子副本