RabbitMQ--消费者可靠性

本文详细解释了RabbitMQ的消费者确认机制,包括ACK、NACK和Reject的不同作用,以及SpringAMQP中的自动、手动确认模式。同时讨论了消费失败的处理策略,如重试机制和MessageRecoverer的使用,以及保证业务幂等性的两种方案。
摘要由CSDN通过智能技术生成

RabbitMQ--消费者可靠性

一、消费者确认机制

        为了确认消费者是否成功处理消息,RabbitMQ提供了消费者确认机制(Consumer Acknowledgement)。当消费者处理消息结束后,应该向RabbitM0发送一个回执,告知RabbitMQ自己消息处理状态。回执有三种可选值:

  • ack:成功处理消息,RabbitMo从队列中删除该消息
  • nack:消息处理失败,RabbitMo需要再次投递消息
  • reject::消息处理失败并拒绝该消息,RabbitMQ从队列中删除该消息,如消息格式错误等

SpringAMQP已经实现了消息确认功能。并允许我们通过配置文件选择ACK处理方式,有三种方式:

  • none:不处理。即消息投递给消费者后立刻 ack 消息会立刻从MQ删除。非常不安全,不建议使用
  • manual:手动模式。需要自己在业务代码中调用api,发送 ack reject,存在业务入侵,但更灵活
  • auto:自动模式。SpringAMQP利用AOP对我们的消息处理逻辑做了环绕增强,当业务正常执行时则自动返回 ack 。当业务出现异常时,根据异常判断返回不同结果:
    • 如果是业务异常,会自动返回 nack
    • 如果是消息处理或校验异常,自动返回 reject

配置文件方式配置:

spring:
  rabbitmq:
    listener:
      simple :
        prefetch: 1
          acknowledge-mode: auto # none,关闭ack; manual,手动ack; auto: 自动ack

二、消费失败处理策略

失败重试机制出现问题(一直返回nack,一直重试一直不行):

        当消费者出现异常后,消息会不断requeue(重新入队)到队列,再重新发送给消费者,然后再次异常,再次requeue无限循环,导致mg的消息处理飙升,带来不必要的压力。

解决:可以利用Spring的retry机制,在消费者出现异常时利用本地重试,而不是无限制的requeue到mq队列:

spring:
    rabbitmq :
        listener:
          simple:
            prefetch: 1
            retry:
              enabled: true # 开户超时重试机制
              initial-interval: 1000ms # 失败后的初始等待时间
              multiplier: 1 # 失败后下次的等待时长倍数,下次等待时长 = initial-interval *   multiplier                  
              max-attempts: 3 # 最大重试次数
              stateless: true # true无状态;false 有状态。如果业务中包含事务,这里改为false

        配置以上信息后,如果在重试次数用完之后还未成功,没有其他处理策略的情况下,该消息会直接在队列中删除。

问题及处理策略:

        在开启重试模式后,重试次数耗尽,如果消息依然失败,则需要有MessageRecoverer接口来处理,它包含三种不同的实现:

  • RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息。默认就是这种方式
  • ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队
  • RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机

这里使用RepublishMessageRecoverer,重试耗尽后,将失败消息投递到指定的交换机

package com.itheima.consumer.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * 设计重试耗尽后,将失败消息投递到指定的交换机,并且主要配置类启动条件是开启超时重试机制
 */
@Configuration
@ConditionalOnProperty(prefix = "spring.rabbitmq.listener.simple.retry",name = "enabled", havingValue = "true")
public class ErrorConfiguration {

    /**
     * 声明错误交换机
     * @return
     */
    @Bean
    public DirectExchange errorExchage(){
        return new DirectExchange("error.direct");
    }

    /**
     * 声明错误队列
     * @return
     */
    @Bean
    public Queue errorQueue(){
        return new Queue("error.queue");
    }

    /**
     * 配置关联信息
     * @return
     */
    @Bean
    public Binding errorBinding(){
        return BindingBuilder.bind(errorQueue()).to(errorExchage()).with("error");
    }

    /**
     * 配置重试耗尽后,将失败消息投递到指定的交换机
     * @param rabbitTemplate
     * @return
     */
    @Bean
    public MessageRecoverer messageRecoverer(RabbitTemplate rabbitTemplate){
        return new RepublishMessageRecoverer(rabbitTemplate,"error.direct","error");
    }
}

消费者如何保证消息一定被消费?

  • 开启消费者确认机制为auto,由spring确认消息处理成功后返回ack,异常时返回nack
  • 开启消费者失败重试机制,并设置MessageRecoverer,多次重试失败后将消息投递到异常交换机,交由人工处理

三、业务幂等性

        幂等是一个数学概念,用函数表达式来描述是这样的:f(x) = f(f(x))。在程序开发中,则是指同一个业务,执行一次或多次对业务状态的影响是一致的。

方案一:唯一消息id


案例:是给每个消息都设置一个唯一id,利用id区分是否是重复消息:

  1. 每一条消息都生成一 个唯一的id,与消息一起投递给消费者。
  2. 消费者接收到消息后处理自己的业务,业务处理成功后将消息ID保存到数据库。
  3. 如果下次又收到相同消息,去数据库查询判断是否存在,存在则为重复消息放弃处理 。
    @Bean
    public MessageConverter jacksonMessageConvertor(){
        // 1.定义消息转换器
        Jackson2JsonMessageConverter jjmc = new Jackson2JsonMessageConverter();
        //2配置自动创建消息d,用于识别不同消息,也可以在业务中基于ID判断是否是重复消息
        jjmc.setCreateMessageIds(true);
        return jjmc;
    }

 出现的问题:业务方法需要做额外的逻辑(保存消息ID,查ID对比)

方案二:业务判断 

案例:在支付服务与交易服务之间,如何保持订单的支付结果的幂等性。如下图,在支付后修改订单状态为已支付,应该在修改订单状态前先查询订单状态,判断状态是否是未支付。只有未支付订单才需要修改,其他状态不做处理。

 面试题:

如何保证支付服务与交易服务之间的订单状态一致性?

  • 首先,支付服务会正在用户支付成功以后利用MO消息通知交易服务,完成订单状态同步。
  • 其次,为了保证MQ消息的可靠性,我们采用了生产者确认机制、消费者确认、消费者失败重试等策略,确保消息投递和处理的可靠性。同时也开启了MQ的持久化,避免因服务宕机导致消息丢失。
  • 最后,我们还在交易服务更新订单状态时做了业务幂等判断,避免因消息重复消费导致订单状态异常。

如果交易服务消息处理失败,有没有什么兜底方案?

  • 我们可以在交易服务设置定时任务,定期查询订单支付状态。这样即便MQ通知失败,还可以利用定时任务作为兜底方案,确保订单支付状态的最终一致性。
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值