如何保证 RabbitMQ中 消息传输的可靠性?

如何保证消息不丢失

1. 网络异常导致消息丢失
  1. 做好方法的容错(try - catch ),发送消息可能会因为网络原因失败,失败后记录到数据库。
  2. 做好日志记录,每个发出去消息的状态都应该被记录。
    -- 消息记录表
    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;
    
  3. 重试,如果消息没有发送成功,定期去数据库扫描未发送成功的消息进行重发。
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. 消费者消费能力不足或者消费者宕机,导致消息积压
  1. 分布式系统中,可以上线更多的消费者,增加消费端的消费能力。
  2. 上线专门的消息队列服务,将消息批量取出来,存入数据库。离线慢慢处理。
2. 发送者发送消息的流量过大,导致消息积压

可以考虑具体的业务场景,避免过大流量到达消息服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值