RabbitMQ 中关于发送可靠性问题(失败重试、发送方确认机制)

3552 篇文章 117 订阅

一. RabbitMQ 消息发送机制

  1. RabbitMQ 中的消息发送引入了 Exchange (交换机) 的概念。消息的发送首先到达交换机,然后再根据既定的路由规则,由交换机将消息路由到不用的 Queue (队列) 中,再由不同的消费者去消费。

  1. 大致的流程就是这样,所以要确保消息发送的可靠性,主要从两个方面去确认

    • 消息成功到达 Exchange
    • 消息成功到达 Queue
  2. 如果能够确认这两步,那么我们就可以认为消息发送成功。

  3. 如果这两步中任一步骤出现了问题,那么消息就是没有发送成功。此时我们可能要通过重试等方式去重新发送消息,多次重试之后,如果消息还是不能到达,则可能需要人工介入了。

  4. 那么,要确保消息成功发送,我们需要做好三件事:

    • 确认消息到达 Exchange
    • 确认消息到达 Queue
    • 开启定时任务,定时投递那些发送失败的消息
  5. 上面三个步骤,第三步需要我们自己去实现,前两步 RabbitMQ 都有提供解决的方案。那么,如果确保消息成功到达 RabbitMQ 呢?

    (1) 开启事务机制

    (2) 发送方确认机制

  6. 注意:这是两种不同的方案,不可以同时开启,只能二选其一。如果同时开启,则会报错。

二. 开启事务机制

  1. 首先需要配置一个事务管理器

    /**
     * 事务管理器
     */
    @Configuration
    public class TxConfig {
        @Bean
        PlatformTransactionManager platformTransactionManager(ConnectionFactory connectionFactory) {
            return new RabbitTransactionManager(connectionFactory);
        }
    }
    
  2. 然后在生产者上添加事务注解以及设置通信通道为事务模式。我这里还是单元测试中进行,代码是修改上面已经使用过的 test01 进行演示

    	@Autowired
    	RabbitTemplate rabbitTemplate;
    
    	/**
    	 * 开启事务机制:
    	 * 1.配置事务管理器
    	 * 2.使用注解开启事务
    	 * 3.把 rabbit 中消息通道设置为事务模式
    	 * 
    	 * 	@Transactional	开启事务
    	 */
    	@Test
    	@Transactional
    	public void test01(){
    		//设置消息通道为事务模式
    		rabbitTemplate.setChannelTransacted(true);
    		rabbitTemplate.convertAndSend(RabbitConfig.MY_QUEUE_NAME, "Hello World!");
    		//手动设置一个异常
    		int i = 1 / 0;
    	}
    
  3. 开启事务机制就三步:

    • 配置事务管理器
    • 使用 @Transactional 注解开启事务
    • 调用 setChannelTransacted 方法设置消息通道为事务模式,即设置为 true
  4. 当我们开启事务模式之后,RabbitMQ 生产者发送消息会有这样几个步骤:

    (1) 客服端发出请求,将通信管道设置为事务模式

    (2) 服务端给出回复,同意将通信管道设置为事务模式

    (3) 客户端发送消息

    (4) 客户端提交事务

    (5) 服务端给出响应,确认事务提交

  5. 上面那几个步骤中,除了第三步本来就有的,其他四个步骤都是因为开启事务模式后多出来的。所以事实上,事务模式其实效率是有点低的,并非是最佳解决方案。

  6. 在实际开发的过程中,一般都是一些高并发的项目才会使用消息中间件,这个时候并发性能尤为重要。

三. 发送方确认机制(常用)

  1. 首先我们需要在 application.properties 中配置开启发送方确认机制:

    # 消息到达转换器的确认回调
    spring.rabbitmq.publisher-confirm-type=correlated
    # 消息到达队列的回调
    spring.rabbitmq.publisher-returns=true
    

    其中,spring.rabbitmq.publisher-confirm-type 这个属性有三个取值:

    (1) none:表示禁用发布确认模式,默认就是这个

    (2) correlated:表示成功发布消息到交换器后会触发的回调方法

    (3) simple:类似 correlated ,并且支持 waitForConfirms()waitForConfirmsOrDie() 方法的调用

  2. 接着需要创建一个配置类,对两个监听进行配置,即 消息是否到达转换器的监听 和 消息是否到达队列的监听

    @Configuration
    public class RabbitTemplateConfig implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback{
    
        @Autowired
        RabbitTemplate rabbitTemplate;
    
        /**
         * @PostConstruct   当 bean 完成初始化的时候,这个方法就会被调用
         */
        @PostConstruct
        public void init() {
            rabbitTemplate.setConfirmCallback(this);
            rabbitTemplate.setReturnsCallback(this);
        }
    
        /**
         * 如果消息到达了或者没有到达交换机,都会触发该方法
         *
         * @param correlationData
         * @param ack   如果 ack 为 true,表示消息到达了交换机,反之则没有到达
         * @param cause
         */
        @Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            if (ack) {
                System.out.println("成功!消息到达了交换机");
            } else {
                System.out.println("失败!消息未到达交换机");
            }
        }
        
        /**
         * 消息未到达队列,会触发该方法
         *
         * @param returnedMessage
         */
        @Override
        public void returnedMessage(ReturnedMessage returnedMessage) {
            System.out.println("消息未到达队列");
        }
    }
    

    在该配置类中:

    (1) RabbitTemplate.ConfirmCallback 这个接口是用来确定消息是否到达交换器的

    (2) RabbitTemplate.ReturnsCallback 这个则是用来确定消息是否到达队列的,未到达队列时会被调用

    (3) init() 这个方法上加了 @PostConstruct 这个注解,即在 bean 完成初始化的时候调用该方法,完成对 RabbitTemplate 的配置

    (4) 上面的几步可以理解为: RabbitTemplate 这个类只需要加依赖就可以直接注入进来调用,但这个类的方法还不能够满足我的需求,所以在 RabbitTemplate 中配置两个 Callback

  3. 我这里还是在单元测试中进行,也是上面已经使用过的代码 test03

    (1) 第一次测试:发送一个不存在的交换机

    	@Autowired
    	RabbitTemplate rabbitTemplate;
    
    	/**
    	 * 在 DirectConfig.MY_DIRECT_EXCHANGE_NAME 交换机的名字中加上双引号,使该参数变成一个字符串
    	 */
    	@Test
    	public void test03(){
    	    rabbitTemplate.convertAndSend("DirectConfig.MY_DIRECT_EXCHANGE_NAME",DirectConfig.MY_DIRECT_QUEUE_NAME_01,"Hello Queue01");
    	}
    

    注意:第一个参数是字符串,不是变量

    运行之后会有这样一句日志:

(2) 第二次测试:发送一个不存在的队列

	@Autowired
	RabbitTemplate rabbitTemplate;

	/**
	 * 在 DirectConfig.MY_DIRECT_QUEUE_NAME_01 队列的名字中加上双引号,使该参数变成一个字符串
	 */
	@Test
	public void test03(){
	    rabbitTemplate.convertAndSend(DirectConfig.MY_DIRECT_EXCHANGE_NAME,"DirectConfig.MY_DIRECT_QUEUE_NAME_01","Hello Queue01");
	}

注意:此时的第二个参数是字符串,不是变量

运行之后会有这样一句日志:

  1. 如果消息是批量处理,发送成功的回调与监听都是一样的,这里就不演示了。

  2. 相比于事务机制,发送方确认机制下的消息吞吐量会得到极大的提升

四. 失败重试

I. 自带重试机制

  1. 在前面的 事务机制发送方确认机制 都是发送方确认消息发送成功的办法。那么,如果说发送方从一开始就连不上 MQ,那么 Spring Boot 中也有相应的重试机制。

  2. 但是呢,这个重试机制就和 MQ 本身是没有关系的,这是利用 Spring 中的 retry 机制来完成的

  3. application.properties 中进行如下配置:

    # 开启重试机制
    spring.rabbitmq.template.retry.enabled=true
    # 最大重试间隔时间
    spring.rabbitmq.template.retry.max-interval=1000ms
    # 最大重试次数
    spring.rabbitmq.template.retry.max-attempts=5
    # 间隔乘数
    spring.rabbitmq.template.retry.multiplier=1.2
    # 初始化的时间间隔
    spring.rabbitmq.template.retry.initial-interval=1000ms
    
  4. 配置完成后,关闭 RabbitMQ ,然后再次启动上面的 test03

    (1) 这里是 RabbitMQ 的关闭,后面再 docker start some-rabbit 启动即可(注意:some-rabbit 是你安装时候取的名字)

    (2) 启动后,就可以看到后台重试打印的日志。因为在配置中设置了最大重试次数为 5 ,所以这里重试了 5 次之后就结束重试了,抛出了异常。

II. 业务重试

  1. 业务重试主要是针对消息没有到达交换机的情况
  2. 如果消息没有到达交换机,正如上面介绍的,会触发 RabbitTemplate.ConfirmCallback 这个方法的回调,那么我们就可以再这个回调中进行处理了。
  3. 这种情况大多需要结合自己的业务去处理,这里后面再结合具体的业务去说明吧。


 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在实际开发,要保证 RabbitMQ 的消息发送可靠性,可以采取以下几个措施: 1. 持久化消息:通过将消息标记为持久化,即使 RabbitMQ 服务器发生故障,消息也不会丢失。可以在发送消息时设置 delivery_mode 为 2,表示将消息标记为持久化的。 2. 设置确认机制:使用 RabbitMQ 提供的确认机制来确保消息的可靠发送确认机制分为两种模式:发布者确认和消费者确认。 - 发布者确认:在消息发送后,等待 RabbitMQ 返回确认信息,确保消息已经被正确接收和处理。 - 消费者确认:在消费者处理完消息后,发送确认RabbitMQ,告知消息已经被正确处理。 3. 设置消息重试机制:当消息发送失败时,可以通过设置重试机制来重新发送消息。可以通过捕获异常来判断消息发送是否成功,如果失败可以进行重试。 4. 设置备份交换器:可以配置备份交换器来处理消息发送失败的情况。备份交换器会在消息发送失败时将消息路由到指定的备份队列,以便后续处理。 5. 设置高可用集群:通过搭建 RabbitMQ 的集群来提高可用性。集群的多个节点可以相互备份和负载均衡,确保即使某个节点发生故障,其他节点也能够继续提供服务。 6. 监控和报警:设置监控系统来监测 RabbitMQ 的状态,及时发现问题并进行处理。同时设置报警机制,在发生异常情况时及时通知相关人员进行处理。 以上是一些常用的法来保证 RabbitMQ 的消息发送可靠性,根据具体的业务需求和实际情况,可以选择适合的式来确保消息的可靠传输。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值