RabbitMQ如何保证消息可靠性

目录

[一、RabbitMQ可靠性其实是 三个阶段的问题:](#一、RabbitMQ可靠性其实是 三个阶段的问题:)

二、生产者如何保证消息可靠投递

1.开启生产者确认机制

2.ConfirmCallback(确认是否到达交换机)

[3 发送失败重试(最多3次)](#3 发送失败重试(最多3次))

4、ReturnCallback(防止消息路由失败)

三、MQ如何保证消息不丢失

[1 队列持久化](#1 队列持久化)

[2 交换机持久化](#2 交换机持久化)

[3 消息持久化](#3 消息持久化)

四、消费者如何保证消息不丢

[1 开启手动ACK](#1 开启手动ACK)

[2 消费者代码](#2 消费者代码)

五、如何防止消息重复消费

[1 Redis去重(最常见)](#1 Redis去重(最常见))

[2 数据库唯一索引](#2 数据库唯一索引)

六.消费消息失败写入死信队列

1.私信配置

1.1死信交换机

1.2死信队列

1.3绑定死信队列

2.业务队列配置

3.消费代码

4.死信队列消费者


RabbitMQ是一个流行的开源消息代理,它提供了可靠的消息传递机制,广泛应用于分布式系统和微服务架构中。在现代应用中,确保消息的可靠性至关重要,以防止消息丢失和重复处理。本文将详细探讨RabbitMQ如何通过多种机制保证消息的可靠性,并提供相关的最佳实践。

一、RabbitMQ可靠性其实是 三个阶段的问题

生产者 → MQ → 消费者

必须保证:

1 生产者发送消息不丢

2 MQ存储消息不丢

3 消费者消费消息不丢

因此完整方案:

生产者可靠投递

  • MQ持久化

  • 消费者可靠消费

  • 幂等控制

二、生产者如何保证消息可靠投递

生产者主要解决:

复制代码
消息发送失败
消息未到达交换机
消息未路由到队列

需要使用两个机制:

复制代码
Publisher Confirm
Return Callback

1.开启生产者确认机制

复制代码
spring:
  rabbitmq:
    publisher-confirm-type: correlated
    publisher-returns: true

2.ConfirmCallback(确认是否到达交换机)

如果消息 没有到达交换机 会触发。

复制代码
@Component
public class RabbitConfirmCallback implements RabbitTemplate.ConfirmCallback {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {

        if (ack) {

            System.out.println("消息成功到达交换机: " + correlationData.getId());

        } else {

            System.out.println("消息发送失败: " + cause);

            // 重试机制
            retrySend(correlationData);
        }
    }
}

3 发送失败重试(最多3次)

这个阶段是发送到交换机要重试;

复制代码
public void retrySend(CorrelationData correlationData){

    ReliableMessage msg = messageCache.get(correlationData.getId());

    int retry = msg.getRetryCount();

    if(retry < 3){

        msg.setRetryCount(retry + 1);

        rabbitTemplate.convertAndSend(
                msg.getExchange(),
                msg.getRoutingKey(),
                msg.getMessage(),
                new CorrelationData(msg.getId())
        );

    }else{

        // 持久化数据库
        failMessageService.save(msg);
    }
}

4、ReturnCallback(防止消息路由失败)

场景:

交换机存在

但没有队列匹配 routingKey

复制代码
rabbitTemplate.setReturnsCallback(returned -> {

    System.out.println("消息路由失败");

    System.out.println("exchange=" + returned.getExchange());

    System.out.println("routingKey=" + returned.getRoutingKey());

});

三、MQ如何保证消息不丢失

MQ层面要解决:

复制代码
RabbitMQ重启
服务器崩溃

需要 持久化机制

1 队列持久化

复制代码
@Bean
public Queue orderQueue(){

    return new Queue("order.queue", true);
}

参数:

true = durable

2 交换机持久化

复制代码
@Bean
public DirectExchange orderExchange(){

    return new DirectExchange("order.exchange", true, false);
}

3 消息持久化

发送消息时设置:

复制代码
MessageProperties props = new MessageProperties();

props.setDeliveryMode(MessageDeliveryMode.PERSISTENT);

四、消费者如何保证消息不丢

消费者必须使用:

复制代码
手动ACK

1 开启手动ACK

复制代码
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual

2 消费者代码

复制代码
@RabbitListener(queues = "order.queue")
public void receive(Message message, Channel channel) throws Exception {

    long tag = message.getMessageProperties().getDeliveryTag();

    try {

        String msg = new String(message.getBody());

        System.out.println("收到消息: " + msg);

        // 执行业务
        process(msg);

        // 手动确认
        channel.basicAck(tag,false);

    } catch (Exception e) {

        // 消费失败
        channel.basicNack(tag,false,true);
    }
}

解释:

方法 作用
basicAck 确认消息
basicNack 拒绝消息
requeue=true 重新入队

五、如何防止消息重复消费

RabbitMQ保证:

至少消费一次

但不能保证:

只消费一次

所以必须做:

幂等控制

1 Redis去重(最常见)

复制代码
public void process(String msg){

    String msgId = getMsgId(msg);

    if(redisTemplate.hasKey(msgId)){
        return;
    }

    // 执行业务
    createOrder(msg);

    redisTemplate.opsForValue().set(msgId,"1");
}

2 数据库唯一索引

订单号 unique

ALTER TABLE orders ADD UNIQUE (order_no);

如果重复消费:数据库直接拒绝

六.消费消息失败写入死信队列

消费者消费消息失败有两种解决方式,一种是重试消费,另一种就是写入死信队列,死信队列的监听者来处理消息;

1.私信配置

1.1死信交换机

复制代码
@Bean
public DirectExchange deadLetterExchange() {
    return new DirectExchange("dlx.exchange", true, false);
}

1.2死信队列

复制代码
@Bean
public Queue deadLetterQueue() {
    return new Queue("order.dlq", true);
}

1.3绑定死信队列

复制代码
@Bean
public Binding deadLetterBinding() {
    return BindingBuilder.bind(deadLetterQueue())
            .to(deadLetterExchange())
            .with("dlx.routingKey");
}

2.业务队列配置

关键配置:

复制代码
x-dead-letter-exchange
x-dead-letter-routing-key

代码:

复制代码
@Bean
public Queue orderQueue() {

    Map<String, Object> args = new HashMap<>();

    // 指定死信交换机
    args.put("x-dead-letter-exchange", "dlx.exchange");

    // 指定死信routingKey
    args.put("x-dead-letter-routing-key", "dlx.routingKey");

    return new Queue("order.queue", true, false, false, args);
}

3.消费代码

重点在:

复制代码
basicNack(tag,false,false)

第三个参数 false 表示 不重新入队,MQ就会投递到死信队列。

复制代码
@RabbitListener(queues = "order.queue")
public void receive(Message message, Channel channel) throws Exception {

    long tag = message.getMessageProperties().getDeliveryTag();

    try {

        String msg = new String(message.getBody());

        System.out.println("收到消息: " + msg);

        // 模拟业务异常
        int i = 1 / 0;

        // 成功确认
        channel.basicAck(tag, false);

    } catch (Exception e) {

        System.out.println("消费失败,进入死信队列");

        // false = 不重新入队
        channel.basicNack(tag, false, false);
    }
}

4.死信队列消费者

死信队列可以专门监听:

复制代码
@RabbitListener(queues = "order.dlq")
public void deadLetterConsumer(Message message) {

    String msg = new String(message.getBody());

    System.out.println("死信消息: " + msg);

    // 可以做:
    // 1 记录日志
    // 2 存数据库
    // 3 人工处理
}
相关推荐
zcmodeltech2 小时前
智能矿井沙盘模型多系统协同控制系统设计:基于STM32与Modbus RTU的感知-传输-控制一体化方案
服务器·数据库·分布式·stm32·单片机·嵌入式硬件
wWYy.3 小时前
基于Raft分布式Kv存储:RPC调用
分布式·rpc
笨蛋不要掉眼泪3 小时前
RabbitMQ消息队列:SpringAMQP
java·分布式·rabbitmq
发量惊人的中年网工4 小时前
SD-WAN如何赋能工业物联网?打造工厂网络远程连接和数据传输新方案
运维·网络·分布式·物联网
段一凡-华北理工大学4 小时前
AI推动工业智能化转型~系列文章05:特征工程:工业 AI 的第一生产力
数据库·人工智能·分布式·搜索引擎·特征工程·高炉智能化
斯普润布特5 小时前
Kafka KRaft 三节点 ARM64 Docker 部署
分布式·kafka
笨蛋不要掉眼泪6 小时前
RabbitMQ消息队列:交换机机制
java·分布式·rabbitmq
zcmodeltech6 小时前
智慧农业沙盘模型物联网控制系统设计:基于STM32与Modbus RTU的传感器-执行器闭环方案
服务器·分布式·stm32·嵌入式硬件·物联网·能源
想你依然心痛6 小时前
HarmonyOS 5.0智慧农业开发实战:构建分布式农业物联网与区块链农产品溯源系统
人工智能·分布式·物联网·区块链·智慧农业·harmonyos·开发实战
重庆小透明17 小时前
带你从不同视角了解三大MQ
java·学习·spring·kafka·rabbitmq·rocketmq