一句话总结
保证消息不丢失需要三道防线:生产者确认(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. 状态机:业务状态单向流转,重复请求被拒绝。