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 人工处理
}
相关推荐
2501_9333295516 小时前
企业舆情处置技术实践:基于AI的智能监测与申诉系统架构解析
人工智能·分布式·架构·系统架构
爱丽_21 小时前
Redis 分布式锁:SET NX、过期时间、续租、可重入、Redlock 与坑
数据库·redis·分布式
ok_hahaha1 天前
java从头开始-黑马点评-分布式锁-redis实现基础版
java·redis·分布式
传感器与混合集成电路1 天前
法珀干涉与光栅补偿:井下压力温度一体化光纤监测技术
分布式
@insist1231 天前
数据库系统工程师-分布式数据库与数据仓库核心考点及应用体系
数据库·数据仓库·分布式·软考·数据库系统工程师·软件水平考试
XDHCOM1 天前
TP5框架Redis分布式缓存实战,解决高并发场景下的数据一致性问题
redis·分布式·缓存
Fzuim1 天前
从CLI到分布式智能体:重新理解AI Agent的演进路径与工程现实
人工智能·分布式·ai·agent·agentic
_院长大人_1 天前
Spring Boot 3.3 + Atomikos 分布式事务日志路径配置踩坑记录
spring boot·分布式·后端
Data 实验室1 天前
TaskPyro “小龙虾版本”专业爬虫管理平台来了:AI+分布式+IM 机器人,一套搞定企业级爬虫调度
人工智能·分布式·爬虫
想你依然心痛1 天前
HarmonyOS 5.0教育行业解决方案:基于分布式能力的沉浸式智慧课堂系统
分布式·wpf·harmonyos