RabbitMQ消息可靠性保障


RabbitMQ中其实没有对消息延迟进行实现,但是我们可以通过TTL以及死信路由来实现消息延迟

1.生产者保证

1.失败通知

生产者发送消息到broker时,要保证消息的可靠性,主要的方案有以下2种

  • 失败通知
  • 发送方确认

1.失败通知:如果出现消息无法投递到队列会出现失败通知,就可以启动失败通知,在原生编程中在发送消息时设置mandatory标志,即可开启故障检测模式
在这里插入图片描述
2.实现方式:
spring中的配置:

pring:
  rabbitmq:
    # 消息在未被队列收到的情况下返回
    publisher-returns: true

java代码中需要发送者实现ReturnCallback接口方可实现失败通知:

public class sendToMessage implements RabbitTemplate.ReturnCallback {
    
    
    
    @Override
    public void returnedMessage(Message message, int i, String s, String s1, String s2) {
    
    
        
    }

    @Override
    public void returnedMessage(ReturnedMessage returned) {
    
    
        RabbitTemplate.ReturnCallback.super.returnedMessage(returned);
    }
}

3.遇到的问题:如果消息正确路由到队列,则发布者不会受到任何通知,带来的问题是无法确保发布消息一定是成功的,因为路由到队列的消息可能会丢失

2.发送方确认

发送方确认是指生产者投递消息后,如果 Broker 接收到消息,则会给生产者一个应答,生产者进行接收应答,用来确认这条消息是否正常的发送到 Broker,这种方式也是消息可靠性投递的核心保障

  • 消息通过交换机exchange被路由到队列queue
  • 将消息发送到broker,即发送到exchage交换机

注意:发送发确认只有出现RabbitMQ内部错误无法投递才会出现发送发确认失败

1 不可路由:当前消息到达交换器后对于发送者确认是成功的
在这里插入图片描述
首先当RabbitMQ交换器不可路由时,消息也根本不会投递到队列中,所以这里只管到交换器的路径,当消息成功送到交换器后,就会进行确认操作

另外在这过程中,生产者收到了确认消息后,那么因为消息无法路由,所以该消息也是无效的,无法投递到队列,所以一般情况下这里会结合失败通知来一同使用,这里一般会进行设置mandatory模式,失败则会调用addReturnListener监听器来进行处理

2.可以路由;只要消息能够到达队列即可进行确认,一般是RabbitMQ发生内部错误才会出现失败
在这里插入图片描述
以路由的消息,要等到消息被投递到所有匹配的队列之后,broker会发送一个确认给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了。

如果消息和队列是可持久化的,那么确认消息会在将消息写入磁盘之后发出,broker回传给生产者的确认消息中delivery-tag域包含了确认消息的序列号

3.实现方式

spring中的配置:

spring:
  rabbitmq:    
    # 开启消息确认机制
    publisher-confirm-type: correlated

java代码中需要发送者实现ConfirmCallback接口方可实现失败通知

public class sendToMessage implements RabbitTemplate.ConfirmCallback {
    
    


    @Override
    public void confirm(CorrelationData correlationData, boolean b, String s) {
    
    
        
    }
}

3. Broker丢失消息

开启RabbitMQ的持久化,也即消息写入后会持久化到磁盘,此时即使mq挂掉了,重启之后也会自动读取之前存储的额数据

1.持久化队列

@Bean
public Queue queue(){
    
    
    return new Queue(queueName,true);
}

2.持久化交换器

@Bean
DirectExchange directExchange() {
    
    
    return new DirectExchange(exchangeName,true,false);
}

3.发送持久化消息

发送消息时,设置消息的deliveryMode=2

注意:如果使用SpringBoot的话,发送消息时自动设置deliveryMode=2,不需要人工再去设置

2.消费方消息可靠性(消费者手动确认)

1.RabbitMQ默认是自动ack的,需要将其修改为手动ack,也即自己的程序确定消息已经处理完成后,手动提交ack,此时如果再遇到消息未处理进程就挂掉的情况,由于没有提交ack,RabbitMQ就不会删除这条消息,而是会把这条消息发送给其他消费者处理,但是消息是不会丢的

2.spring配置文件

spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual  # 手动ack

3.参数介绍

acknowledge-mode: manual就表示开启手动ack,该配置项的其他两个值分别是none和auto

  • auto:消费者根据程序执行正常或者抛出异常来决定是提交ack或者nack,不要把none和auto搞混了
  • manual: 手动ack,用户必须手动提交ack或者nack
  • none: 没有ack机制

4.消费者实现

public class sendToMessage {
    
    

    @RabbitHandler
    public void processOrder(Message massage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
    
    
        channel.basicAck(tag, false);
    }
    
}

3.业务可靠性分析

在这里插入图片描述
1.消息丢失:在这个业务场景中,用户发起打车请求,如果用户消息丢失,对整体业务是没有任何影响的,用户可以再次发起打车操作,这个消息丢失问题概率很低,可以进行简单化设计,如果出现发送失败只需要回退redis中的操作即可。

2.幂等性校验:因为使用了延时队列,对于这个业务来说是不需要进行幂等性校验的,因为第一次超时时如果存在redis用户排名的key就会被删除,下一次redis没有的值在删除一次,这种操作是幂等的,所以不需要考虑幂等性

3.数据回滚:虽然无需做到消息完全不丢失以及消息的幂等性,但是需要考虑如果出现问题,需要将插入Redis的的key值回滚掉,防止影响业务正常判断

猜你喜欢

转载自blog.csdn.net/weixin_44702984/article/details/131634539