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 防消费者处理失败丢消息。四者结合,消息可靠性基本稳了。