怎么保证消息不丢失?消息队列的可靠性如何保证?

2025年 阅读约 10 分钟 面试指南 · 消息中间件面试

消息队列可靠性保证全攻略,包含生产者确认、Broker持久化、消费者手动ACK三道防线。

一句话总结

保证消息不丢失需要三道防线:生产者确认(Confirm/同步发送)、Broker持久化(队列+消息持久化)、消费者手动ACK。同时需要处理消息重复消费问题,保证幂等性。

初级理解

三道防线:

1. 生产者端:开启Confirm机制(RabbitMQ)或同步发送+重试(RocketMQ),确保消息成功到达Broker 2. Broker端:开启消息持久化(队列持久化 + 消息持久化),防止宕机丢失 3. 消费者端:关闭自动ACK,业务处理成功后手动ACK;消费失败进入重试队列或死信队列
一句话总结:生产者确认 + Broker持久化 + 消费者手动ACK = 消息不丢失。

中级深入

生产者Confirm机制(RabbitMQ):

rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { if (ack) { log.info("消息发送成功"); } else { log.error("消息发送失败: {}", cause); // 重试或记录到数据库 } });

消费者手动ACK:

@RabbitListener(queues = "stock-queue") public void handleMessage(Message message, Channel channel) { try { // 处理业务逻辑 processMessage(message); // 手动ACK channel.basicAck(deliveryTag, false); } catch (Exception e) { // 消费失败,重新入队或进入死信队列 channel.basicNack(deliveryTag, false, true); } }
注意:消息重复消费是MQ的固有问题,只能通过消费者幂等性来解决。

高级拓展

RocketMQ事务消息:

# 发送半消息 rocketMQTemplate.sendMessageInTransaction("order-topic", message, orderDTO); # 本地事务执行 @Transactional public boolean createOrder(OrderDTO orderDTO) { orderMapper.insert(orderDTO); return true; } # 回查机制 public TransactionCheckResult checkLocalTransaction(Message message) { Order order = orderMapper.selectByOrderNo(message.getOrderNo()); return order != null ? TransactionCheckResult.COMMIT : TransactionCheckResult.ROLLBACK; }
面试加分项:能说出三道防线、Confirm机制、事务消息、幂等性方案,说明你对MQ可靠性有深入理解。

实战场景

场景:订单创建与库存扣减

// 生产者:开启Confirm
rabbitTemplate.convertAndSend("order-exchange", "order.create", order, message -> {
    message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
    return message;
});

// 消费者:手动ACK + 幂等性
@RabbitListener(queues = "stock-queue")
public void handle(OrderDTO order, Channel channel, @Payload long deliveryTag) {
    String key = "processed:" + order.getOrderNo();
    if (redisTemplate.hasKey(key)) {
        channel.basicAck(deliveryTag, false);
        return;
    }
    stockService.deductStock(order.getProductId(), order.getQuantity());
    redisTemplate.opsForValue().set(key, "1", 24, TimeUnit.HOURS);
    channel.basicAck(deliveryTag, false);
}

面试模拟

Q:如何保证消息不丢失?

A:三道防线:1. 生产者端:Confirm机制/同步发送+重试;2. Broker端:消息持久化;3. 消费者端:手动ACK,消费失败进入重试队列或死信队列。

Q:如何保证消费者幂等性?

A:1. 数据库唯一索引:利用订单号等唯一约束;2. Redis Token机制:消费时验证并删除;3. 状态机:业务状态单向流转,重复请求被拒绝。