如何保证消息不丢失
1. 网络异常导致消息丢失
- 做好方法的容错(try - catch ),发送消息可能会因为网络原因失败,失败后记录到数据库。
- 做好日志记录,每个发出去消息的状态都应该被记录。
-- 消息记录表 create table mq_message ( message_id char(32) not null, content text, to_exchange varchar(255) default null, routing_key varchar(255) default null, class_type varchar(255) default null, message_status int(1) default 0 comment '0-新建,1-已发送,2-错误抵达,3-已抵达', create_time datetime default null, update_time datetime default null ) engine=innodb default charset=utf8mb4;
- 重试,如果消息没有发送成功,定期去数据库扫描未发送成功的消息进行重发。
2. Broker宕机,发出去的消息未持久化,导致消息丢失
只有进入到队列的消息,才能被持久化。发出去的消息,未进入队列,此时的 Broker 宕机,消息会丢失。
解决办法:Publisher 必须加入确认回调机制,保证消息消息成功到达队列。Publisher 加入回调机制,如果从 Exchange 发出的消息未抵达 Queue,会执行回调 returnCallback,此时修改数据库中消息的状态。以备重试。
3. 自动ACK状态下,消息丢失
如果队列设置为自动 ACK,那么可能会导致消息丢失。因为在这种模式下,只要消费者接收到消息,队列就会将消息移除。但是,有可能发生,消费者收到消息后,消费者出现异常,没来得及处理消息,导致消息丢失。
解决办法:开启手动 ACK,只有消息被成功消费后,才能被丢弃。其它情况下,让消息重新入队。
Spring Boot整合 rabbitmq的项目中,消息默认情况下是自动 ACK 的。可以在 applicatoin.yml 配置文件中,修改参数,将消息变为手动 ACK。
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual
如何保证消息不重复
查看如下代码
@RabbitHandler
public void handleStockLockedRelease(StockLockedTo to, Message message, Channel channel) throws IOException {
System.out.println("收到库存到期,解锁库存的消息");
try {
skuService.unlockStock(to);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
// 解锁失败,消息重新归队
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
}
}
这是消费者的代码。
1. 消息消费成功,事务已经提交,但是ACK时,机器宕机。
在手动 ACK 模式下,skuService.unlockStock(to);
这行代码成功执行,但是在执行 channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
代码的时候,机器宕机了。这样,消息没有被手动ACK。但是没有被ACK的消息,会重新入队。等机器恢复正常时,又收到了这条消息。因此重复收到消息。
解决办法:skuService.unlockStock(to);
这样代码,调用的接口,应该设计成幂等的。比如,可以根据消息的 id 查询该消息的状态是否被处理,或者根据消息代表的业务含义,查询数据库中相应的业务数据处理的状态,以此来保证业务不会被重复执行。
RabbitMQ 每一个被消费的消息都有一个 redelivered字段,可以根据该字段的值来判断,消息是否被重新投递过来了。以此,也可以以此作为下一步判断的依据。
Boolean redelivered = message.getMessageProperties().getRedelivered();
2. 消息消费失败,由于重试机制,会再次收到消息
这种重复的消息,按照正常流程处理。
如何保证消息不积压
1. 消费者消费能力不足或者消费者宕机,导致消息积压
- 分布式系统中,可以上线更多的消费者,增加消费端的消费能力。
- 上线专门的消息队列服务,将消息批量取出来,存入数据库。离线慢慢处理。
2. 发送者发送消息的流量过大,导致消息积压
可以考虑具体的业务场景,避免过大流量到达消息服务。