一、生产者可靠性:确保消息 "发得出、送得到"
生产者可靠性核心解决两个问题:网络 / 服务异常时的重连 、确认消息确实被 MQ 接收。
1. 生产者重连(自动重连机制)
原理
RabbitMQ 客户端(Java 的 AMQP 客户端 / Spring AMQP)与 MQ 服务端建立 TCP 连接后,若因网络波动、MQ 宕机、连接超时等原因导致连接断开,客户端会通过 "连接状态监听 + 重连策略" 自动尝试重建连接,避免生产者因单次连接失败而永久无法发消息。
工作流程

关键配置


2. 生产者确认(Publisher Confirm)
原理
生产者发送消息后,MQ 会给生产者返回 "确认回执"(Confirm),生产者只有收到回执,才确认消息已被 MQ 接收;若未收到回执,说明消息可能丢失,需重试。RabbitMQ 提供三种确认模式:
- 普通确认(单条同步) :发一条等一条的确认,效率低;
- 批量确认:批量发送后等批量确认,效率高但可能丢失批量内的部分消息;
- 异步确认:发送消息后不阻塞,通过回调处理确认结果,效率最高(推荐)。
工作流程(异步确认,主流方案)

代码实现(Spring AMQP 异步确认)
yaml
spring:
rabbitmq:
publisher-returns: true
publisher-confirm-type: correlated
typescript
@Configuration
public class RabbitPublisherConfig {
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 开启生产者确认
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
// 确认成功:correlationData是消息唯一标识,可用于记录消息状态
System.out.println("消息发送成功,ID:" + correlationData.getId());
} else {
// 确认失败:触发重试或写入死信表
System.out.println("消息发送失败,原因:" + cause + ",ID:" + correlationData.getId());
// 这里可调用重试逻辑,或把消息存入数据库后续补偿
}
});
// 开启消息路由失败通知(比如交换机不存在、路由键匹配不到队列)
rabbitTemplate.setReturnsCallback(returnedMessage -> {
System.out.println("消息路由失败:" + returnedMessage.getMessage());
});
// 必须设置为true,否则路由失败时消息直接丢弃,不会触发returnsCallback
rabbitTemplate.setMandatory(true);
return rabbitTemplate;
}
}
// 生产者发送消息示例
@Service
public class ProducerService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(String msg) {
// 生成唯一消息ID,用于确认回调
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
// 发送消息:交换机、路由键、消息体、唯一标识
rabbitTemplate.convertAndSend("demo.exchange", "demo.key", msg, correlationData);
}
}
二、MQ 自身可靠性:确保消息 "存得住、不丢失"
MQ 自身可靠性核心是数据持久化 (避免 MQ 宕机丢失消息)和LazyQueue(优化海量消息存储,避免内存溢出)。
1. 数据持久化
原理
RabbitMQ 默认消息只存内存,宕机后全部丢失;持久化机制通过 "三层持久化"(交换机、队列、消息)将数据写入磁盘,MQ 重启后可恢复。
- 交换机持久化:声明交换机时标记
durable=true,MQ 重启后交换机仍存在; - 队列持久化:声明队列时标记
durable=true,队列的元数据(名称、路由规则)持久化; - 消息持久化:发送消息时标记
deliveryMode=2(持久化),消息内容写入磁盘。
工作流程

代码实现(三层持久化)
typescript
@Configuration
public class RabbitQueueConfig {
// 1. 声明持久化交换机
@Bean
public DirectExchange durableExchange() {
// durable=true:持久化;autoDelete=false:不自动删除
return new DirectExchange("demo.exchange", true, false);
}
// 2. 声明持久化队列
@Bean
public Queue durableQueue() {
// durable=true:持久化;exclusive=false:非独占;autoDelete=false:不自动删除
return QueueBuilder.durable("demo.queue").build();
}
// 3. 绑定交换机和队列
@Bean
public Binding binding(DirectExchange durableExchange, Queue durableQueue) {
return BindingBuilder.bind(durableQueue).to(durableExchange).with("demo.key");
}
}
// 发送持久化消息(Spring AMQP默认deliveryMode=2,无需手动设置)
@Service
public class ProducerService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendPersistentMsg(String msg) {
rabbitTemplate.convertAndSend("demo.exchange", "demo.key", msg, message -> {
// 显式设置消息持久化(可选,Spring默认已设置)
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
});
}
}
2. LazyQueue(惰性队列)
原理
默认队列(Classic Queue)会优先将消息存入内存,仅在内存不足时刷盘;而 LazyQueue(惰性队列)优先将消息写入磁盘,只有消费者消费时才加载到内存,核心解决 "海量消息堆积导致 MQ 内存溢出" 的问题。适用场景:消息堆积量大、消费速度慢(比如秒杀场景的延迟消费、批量处理)。

工作流程

代码实现(声明 LazyQueue)
typescript
@Configuration
public class RabbitLazyQueueConfig {
@Bean
public Queue lazyQueue() {
// 通过QueueBuilder声明惰性队列
return QueueBuilder.durable("demo.lazy.queue")
.lazy() // 核心:标记为LazyQueue
.build();
}
@Bean
public Binding lazyBinding(DirectExchange durableExchange, Queue lazyQueue) {
return BindingBuilder.bind(lazyQueue).to(durableExchange).with("lazy.key");
}
}
注意:LazyQueue 的缺点是消费延迟略高(需要从磁盘加载),但内存占用极低,适合消息堆积场景;普通队列适合低延迟、高吞吐的实时消费场景。
三、消费者可靠性:确保消息 "收得到、处理完"
消费者可靠性核心解决两个问题:确认消息已处理完成 、处理失败时自动重试。
1. 消费者确认(Consumer ACK)
原理
消费者接收消息后,必须主动向 MQ 发送 "确认回执"(ACK),MQ 只有收到 ACK 才会删除消息;若消费者宕机 / 处理失败未发送 ACK,MQ 会将消息重新分发给其他消费者(避免消息丢失)。RabbitMQ 提供三种确认模式:

工作流程(手动确认,生产环境必用)

代码实现(Spring AMQP 手动确认)
scss
@Configuration
public class RabbitConsumerConfig {
@Bean
public SimpleMessageListenerContainer listenerContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueueNames("demo.queue");
// 核心:关闭自动确认,开启手动确认
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
// 设置消费者数量、并发数等
container.setConcurrentConsumers(2);
container.setMaxConcurrentConsumers(5);
// 消费逻辑
container.setMessageListener((ChannelAwareMessageListener) (message, channel) -> {
try {
// 1. 解析消息
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("消费消息:" + msg);
// 2. 执行业务逻辑(比如入库、调用接口)
// doBusiness();
// 3. 手动确认:第二个参数multiple=false表示只确认当前消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
// 4. 处理失败:否定确认,重新投递(第三个参数requeue=true)
// 注意:若业务是幂等的,可重新投递;若非幂等,需先保证幂等再重试
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
// 若不想重新投递,设置requeue=false,消息会进入死信队列
// channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
}
});
return container;
}
}
2. 失败重试机制
原理

消费者处理消息失败时,通过 "重试策略" 自动重新处理消息,避免消息直接进入死信队列;重试次数耗尽后,再将消息移入死信队列(DLQ),便于后续人工排查。Spring AMQP 的重试机制基于RetryTemplate,支持 "固定间隔重试""指数退避重试" 等策略。
工作流程

代码实现(失败重试 + 死信队列)
typescript
@Configuration
public class RabbitRetryConfig {
// 1. 声明死信交换机和死信队列
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange("demo.dlx.exchange", true, false);
}
@Bean
public Queue dlxQueue() {
return QueueBuilder.durable("demo.dlx.queue").build();
}
@Bean
public Binding dlxBinding(DirectExchange dlxExchange, Queue dlxQueue) {
return BindingBuilder.bind(dlxQueue).to(dlxExchange).with("dlx.key");
}
// 2. 声明业务队列,并绑定死信交换机
@Bean
public Queue businessQueue() {
return QueueBuilder.durable("demo.business.queue")
// 绑定死信交换机
.deadLetterExchange("demo.dlx.exchange")
// 死信路由键
.deadLetterRoutingKey("dlx.key")
.build();
}
// 3. 配置消费者重试策略
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
// 关闭自动确认
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
// 开启重试
factory.setRetryTemplate(retryTemplate());
// 重试耗尽后是否重新入队(设为false,让消息进入死信队列)
factory.setDefaultRequeueRejected(false);
return factory;
}
// 自定义重试策略(指数退避:重试间隔越来越长)
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate template = new RetryTemplate();
// 重试次数:最大3次
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3);
template.setRetryPolicy(retryPolicy);
// 指数退避策略:初始间隔1秒,每次乘以2,最大间隔10秒
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(1000);
backOffPolicy.setMultiplier(2);
backOffPolicy.setMaxInterval(10000);
template.setBackOffPolicy(backOffPolicy);
return template;
}
}
// 消费者监听示例
@Component
public class ConsumerService {
@RabbitListener(queues = "demo.business.queue", containerFactory = "rabbitListenerContainerFactory")
public void consume(Message message, Channel channel) throws IOException {
try {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("消费消息:" + msg);
// 模拟业务处理失败
int a = 1 / 0;
// 处理成功,手动确认
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
// 抛出异常触发重试,重试耗尽后消息进入死信队列
throw new RuntimeException("处理消息失败", e);
}
}
// 监听死信队列,处理重试耗尽的消息
@RabbitListener(queues = "demo.dlx.queue")
public void consumeDlx(Message message) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("处理死信消息(重试耗尽):" + msg);
// 这里可记录日志、告警、人工介入处理
}
}
总结
- 生产者可靠性:重连靠 "连接监听 + 重试策略" 保证链路不中断,生产者确认靠 MQ 回执确保消息送达 MQ,异步确认是生产环境最优选择;
- MQ 自身可靠性:三层持久化(交换机 + 队列 + 消息)保证宕机不丢数据,LazyQueue 优先存磁盘解决海量消息内存溢出问题;
- 消费者可靠性:手动 ACK 确保消息处理完成后才删除,失败重试(配合死信队列)避免消息直接丢失,重试耗尽后需人工兜底。
核心原则:RabbitMQ 的可靠性是 "分层保障",需同时做好生产者、MQ、消费者三层的可靠性配置,缺一不可;生产环境禁用自动 ACK、禁用非持久化消息,确保每一步都有确认和兜底机制。

