RabbitMQ备份交换机

有了 mandatory 参数和回退消息,我们获得了对无法投递消息的感知能力,有机会在生产者的消息
无法被投递时发现并处理。但有时候,我们并不知道该如何处理这些无法路由的消息,最多打个日志,然后触发报警,再来手动处理。而通过日志来处理这些无法路由的消息是很不优雅的做法,特别是当生产者所在的服务有多台机器的时候,手动复制日志会更加麻烦而且容易出错。而且设置 mandatory 参数会增加生产者的复杂性,需要添加处理这些被退回的消息的逻辑。如果既不想丢失消息,又不想增加生产者的复杂性,该怎么做呢?

前面在设置死信队列的文章中,我们提到,可以为队列设置死信交换机来存储那些处理失败的消息,可是这些不可路由消息根本没有机会进入到队列,因此无法使用死信队列来保存消息。

在 RabbitMQ 中,有一种备份交换机的机制存在,可以很好的应对这个问题。什么是备份交换机呢?备份交换机可以理解为 RabbitMQ 中交换机的“备胎”,当我们为某一个交换机声明一个对应的备份交换机时,就是为它创建一个备胎,当交换机接收到一条不可路由消息时,将会把这条消息转发到备份交换机中,由备份交换机来进行转发和处理,通常备份交换机的类型为 Fanout ,这样就能把所有消息都投递到与其绑定的队列中,然后我们在备份交换机下绑定一个队列,这样所有那些原交换机无法被路由的消息,就会都进入这个队列了。当然,我们还可以建立一个报警队列,用独立的消费者来进行监测和报警。

代码架构图

RabbitMQ备份交换机_第1张图片

注意点

  • 这里的备份交换机的类型是fanout类型的。
  • 要把备份交换机和业务交换机进行关联起来。
/声明业务 Exchange
    @Bean("confirmExchange")
    public DirectExchange confirmExchange(){
//        return new DirectExchange(CONFIRM_EXCHANGE_NAME);
        return ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME).durable(true).
                withArgument("alternate-exchange",BACKUP_EXCHANGE).build();
    }

配置类代码

package com.dongmu.rabbitmq.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ConfirmConfig {
    //交换机
    public static final String CONFIRM_EXCHANGE_NAME = "confirm.exchange";
    //队列
    public static final String CONFIRM_QUEUE_NAME = "confirm.queue";
    //routingkey
    public static final String CONFIRM_ROUTING_KEY = "key1";
    //备份交换机
    public static final String BACKUP_EXCHANGE = "backup_exchange";
    //备份队列
    public static final String BACKUP_QUEUE = "backup_queue";
    //报警队列
    public static final String WARNING_QUEUE = "warning_queue";

    //声明业务 Exchange
    @Bean("confirmExchange")
    public DirectExchange confirmExchange(){
//        return new DirectExchange(CONFIRM_EXCHANGE_NAME);
        return ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME).durable(true).
                withArgument("alternate-exchange",BACKUP_EXCHANGE).build();
    }
    // 声明确认队列
    @Bean("confirmQueue")
    public Queue confirmQueue(){
        return QueueBuilder.durable(CONFIRM_QUEUE_NAME).build();
    }
    // 声明确认队列绑定关系
    @Bean
    public Binding queueBinding(@Qualifier("confirmQueue") Queue queue,
                                @Qualifier("confirmExchange") DirectExchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with(CONFIRM_ROUTING_KEY);
    }

    //备份交换机
    @Bean
    public FanoutExchange backuoExchange(){
        return new FanoutExchange(BACKUP_EXCHANGE);
    }

    // 声明备份队列
    @Bean("backupQueue")
    public Queue backupQueue(){
        return QueueBuilder.durable(BACKUP_QUEUE).build();
    }
    @Bean("warningQueue")
    public Queue warningQueue(){
        return QueueBuilder.durable(WARNING_QUEUE).build();
    }
    // 声明确认队列绑定关系
    @Bean
    public Binding backupQueueBindingbackuoExchange(@Qualifier("backupQueue") Queue queue,
                                @Qualifier("backuoExchange") FanoutExchange exchange){
        return BindingBuilder.bind(queue).to(exchange);
    }
    @Bean
    public Binding warningQueueBindingbackuoExchange(@Qualifier("warningQueue") Queue queue,
                                                    @Qualifier("backuoExchange") FanoutExchange exchange){
        return BindingBuilder.bind(queue).to(exchange);
    }


}

业务队列的消费者我们之前写过了,还剩下备份队列和告警队列的消费者接口没有写,备份队列的消费者我们不写了,只写警告队列的消费者。

@RabbitListener(queues = ConfirmConfig.WARNING_QUEUE)
    public void receiveWarningQueue(Message message){
        String str = new String(message.getBody());
        log.warn("报警,不可路由的消息,消息内容是:{}",str);
    }

这里注意要把原来的交换机删除,因为交换机的属性改变了。
RabbitMQ备份交换机_第2张图片

为了便于测试,我们把生产者部分的代码改成下面这个样子。其中一个消息可以正常路由,另外一个消息不能正常路由。

//发消息
    @GetMapping("sendMessage/{message}")
    public void sendMessage (@PathVariable("message") String message){

        CorrelationData correlationData = new CorrelationData();
        correlationData.setId("10011");
        rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,
                ConfirmConfig.CONFIRM_ROUTING_KEY+"222",message,correlationData);
        rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,
                ConfirmConfig.CONFIRM_ROUTING_KEY,message,correlationData);

        log.info("发送消息内容:{}",message);
    }

启动服务进行测试
在这里插入图片描述
在这里插入图片描述
可以发现没有正常的路由就会被路由到了备份交换机的备份队列和警告队列。

mandatory 参数与备份交换机可以一起使用的时候,如果两者同时开启,消息究竟何去何从?谁优先
级高,经过上面结果显示答案是备份交换机优先级高。

你可能感兴趣的:(RabbitMQ,rabbitmq,java,分布式)