RabbitMQ消息可靠性三板斧

RabbitMQ消息可靠性三板斧

生产环境消息不能丢,三个环节都要保证:

scss 复制代码
生产者 --(Confirm/Return)--> Broker --(持久化)--> 队列 --(手动ACK)--> 消费者

1. 生产者确认(Confirm)

application.yml 开启:

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

配置回调:

typescript 复制代码
@Configuration
public class RabbitConfig implements RabbitTemplate.ConfirmCallback {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(this);
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            System.out.println("消息成功到达 Exchange");
        } else {
            System.err.println("消息丢失: " + cause);
            // TODO: 补偿重发或记录数据库
        }
    }
}

2. 消息退回(Return)

当 Exchange 无法路由到 Queue 时触发:

typescript 复制代码
@Configuration
public class RabbitConfig implements RabbitTemplate.ReturnsCallback {

    @PostConstruct
    public void init() {
        rabbitTemplate.setReturnsCallback(this);
    }

    @Override
    public void returnedMessage(ReturnedMessage returned) {
        System.err.println("路由失败: " + returned.getMessage());
        // TODO: 报警或转存
    }
}

发消息时指定 mandatory = true:

arduino 复制代码
rabbitTemplate.convertAndSend(
    "exchange", "routingKey", msg,
    message -> message,  // MessagePostProcessor
    new CorrelationData("id-001")
);

3. 队列与消息持久化

typescript 复制代码
@Bean
public Queue durableQueue() {
    // durable=true 队列持久化
    return QueueBuilder.durable("durable.queue").build();
}

消息持久化:

ini 复制代码
MessageProperties props = new MessageProperties();
props.setDeliveryMode(MessageDeliveryMode.PERSISTENT); // 2 = 持久化

Message message = new Message("hello".getBytes(), props);
rabbitTemplate.convertAndSend("exchange", "key", message);

4. 消费者手动 ACK

application.yml:

yaml 复制代码
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual  # 手动确认
        prefetch: 1               # 每次只取1条,处理完再取下一条

消费者代码:

java 复制代码
@Component
public class AckConsumer {

    @RabbitListener(queues = "durable.queue")
    public void receive(Message message, Channel channel) throws IOException {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            String body = new String(message.getBody());
            System.out.println("处理: " + body);
            // 业务逻辑...
            
            // 手动确认
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            // 拒绝并重新入队(根据业务决定是否重试)
            channel.basicNack(deliveryTag, false, true);
        }
    }
}

小结:Confirm 防丢失到 Exchange,Return 防路由失败,持久化防 Broker 重启丢数据,手动 ACK 防消费者处理失败丢消息。四者结合,消息可靠性基本稳了。

相关推荐
孟陬1 小时前
Foobar 技术周刊#159:当看不懂代码时,如何高效排障调试。脏页陷阱:一条只读查询,写入数 TB 数据。推测解码:大模型加速利器
前端·人工智能·后端
GoGeekBaird1 小时前
别再给每个 AI 做自我介绍了:我做了一个「个人上下文」管理系统
后端·github·agent
码事漫谈1 小时前
腾讯混元 Hy4 正式版发布了
后端
Mikko71 小时前
Redis 连接数满了服务端有错误信息吗?连接数不增加却命令超时?6.2 实测连接池、超时与大 key 怎么判定
java·spring boot·redis·后端
苍何2 小时前
DeepSeek的Super-agent 设计,和我们的工程实践
后端
Patrick_Wilson3 小时前
第一次读 Go 项目:从两个源码文件弄清 package、import 和 go.mod
后端·go·编程语言
Patrick_Wilson3 小时前
执行 go build . 后,Go 怎样找到代码并生成程序
后端·go·编程语言
用户6919026813393 小时前
Redis 2:Redis 在怎么处理harness记忆
后端
Patrick_Wilson3 小时前
Go 变量不赋值时,为什么也有值
后端·go·编程语言
Patrick_Wilson3 小时前
读 Go 项目里的命令:run、build、test 分别做了什么
后端·go·编程语言