RabbitMq发布确认高级

发布确认高级

在生产环境中由于一些不明原因导致rabbitmq重启,在rabbitmq重启期间生产者消息投递失败,导致消息丢失,需要手动处理和恢复。

如何才能进行rabbitmq的消息可靠投递呢?

特别是在这样比较极端的情况,rabbitmq集群不可用的时候,无法投递的消息该去如何处理
RabbitMq发布确认高级_第1张图片
代码的架构图如上

在构建完基础的交换机的配置及路由、队列的配置之后就可以考虑交换机的发布确认了

配置文件

在配置文件中需要添加

在配置文件当中需要添加 spring.rabbitmq.publisher-confirm-type=correlated

  • NONE 禁用发布确认模式,是默认值

  • CORRELATED 发布消息成功到交换器后会触发回调方法

  • SIMPL

    会有两种效果,其一效果和CORRELATED值一样会触发回调方法

    其二在发布消息成功后使用rabbitTemplate 调用 waitForConfirms 或 waitForConfirmsOrDie 方法 等待 broker 节点返回发送结果,根据返回结果来判定下一步的逻辑,要注意的点是 waitForConfirmsOrDie 方法如果返回 false 则会关闭 channel,则接下来无法发送消息到 broker

这里我们使用correlated这种

spring:
  rabbitmq:
    host: 106.14.19.158
    publisher-confirm-type: correlated

交换机确认回调方法

  1. 发消息交换机接收到了 回调
    • correlationData 保存回调消息的id及相关信息
    • 交换机收到消息 ack=true
    • cause null;
  2. 发消息 交换机接收失败了 回调
    • correlationData 保存回调消息的id及相关信息
    • 交换机收到消息 ack=false
    • cause 引起失败的原因
回调接口

该接口是在交换机接受完消息之后会给生产者进行消息的应答

correlationData即为相关数据 可以设置 message routingkey exchange等的信息

ack即为交换机是否应答,true的话为正常应答 false的话即为交换机应答有误

cause即为失败的原因 成功的话cause为null

@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback {
     
    @Autowired
    private  RabbitTemplate rabbitTemplate;
    @PostConstruct
    public void  init(){
     
      rabbitTemplate.setConfirmCallback(this);
    }
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
     
//        String id = correlationData.getId();
//        String s = correlationData.toString();
//        System.err.println(id);
//        System.err.println(s);
        if(ack==true){
     
            System.err.println("交换机成功接收消息");
        }else{
     
            ReturnedMessage returned = correlationData.getReturned();
            System.err.println(correlationData.getReturned().toString());
            System.err.println(new String(correlationData.getReturned().getMessage().getBody()));
        }
    }
}

消息的生产者

@RequestMapping("/fei")
    public void test2(){
     
        CorrelationData correlationData=new CorrelationData();
        correlationData.setReturned(new ReturnedMessage(new Message("feifeifei".getBytes()),0,null,"confirm.exchange","test"));
        rabbitTemplate.convertAndSend("confirm.exchange","test","feifeifei",correlationData);
    }

这里的消息的生产者在向交换机发送消息的时候,设置了相关参数

在项目启动后,手动的在mq的web界面,删除了对应的交换机,即删除了名为"confirm.exchange"的交换机

在发送请求
RabbitMq发布确认高级_第2张图片

交换机的回调接口触发ack为false 打印出了相关数据

此时可以在这里对未发送成功的消息进行进一步的处理

消息回退

Mandatory参数

在仅开启生产者确认机制的情况下,交换机接收到消息后,会直接给消息生产者发送确认消息,如果发现该消息不可以被路由,那么消息会直接被丢弃,此时生产者是不知道这个丢弃事件的,那么如何 让无法被路由的消息帮我想办法处理一下?最起码通知我一声,我好自己处理啊。通过设置 mandatory 参数可以在当消息传递过程中不可达目的地地将消息返回给生产者。

备份交换机

经过上边的设置和回调函数,已经有了对无法投递的消息的感知能力,有机会在生产者的消息无法被投递时发现并处理,但有时候我们并不知道如何处理这些无法路由的消息,最多打个日志,然后触发报警再来手动处理。而通过日志处理这些无法路由的消息是很不优雅的做法,特别是当生产者所在的服务有多台机器的时候,手动复制日志会更加麻烦而且容易出错。而且设置 mandatory 参数会增 加生产者的复杂性,需要添加处理这些被退回的消息的逻辑。如果既不想丢失消息,又不想增加生产者的 复杂性,该怎么做呢?前面在设置死信队列的文章中,我们提到,可以为队列设置死信交换机来存储那些 处理失败的消息,可是这些不可路由消息根本没有机会进入到队列,因此无法使用死信队列来保存消息。 在 RabbitMQ 中,有一种备份交换机的机制存在,可以很好的应对这个问题。什么是备份交换机呢?备份 交换机可以理解为 RabbitMQ 中交换机的“备胎”,当我们为某一个交换机声明一个对应的备份交换机时, 就是为它创建一个备胎,当交换机接收到一条不可路由消息时,将会把这条消息转发到备份交换机中,由 备份交换机来进行转发和处理,通常备份交换机的类型为 Fanout ,这样就能把所有消息都投递到与其绑 定的队列中,然后我们在备份交换机下绑定一个队列,这样所有那些原交换机无法被路由的消息,就会都 进入这个队列了。当然,我们还可以建立一个报警队列,用独立的消费者来进行监测和报警。

架构图如下
RabbitMq发布确认高级_第3张图片
首先先来进行基本的交换机和队列之间的配置

@Configuration
public class MqFei {
     
    @Bean("queue")
    Queue  confirmQueue(){
     
        return QueueBuilder.nonDurable("confirm.queue").build();
    }
    @Bean("backup_exchange")
    FanoutExchange fanoutExchange(){
     
        return new FanoutExchange("backup_exchange");
    }

    @Bean("exchange")
    DirectExchange confirmExchange( ){
     
        return  ExchangeBuilder.directExchange("confirm.exchange")
                .durable(true)
                .withArgument("alternate-exchange","backup_exchange")   //声明了备份交换机
                .build();
    }
    @Bean
    Binding binding(Queue queue,DirectExchange exchange){
     
        return BindingBuilder.bind(queue).to(exchange).with("test");
    }
    //声明备份队列 和警告队列
    @Bean("backup_queue")
    Queue queue4(){
     
        return QueueBuilder.nonDurable("backup.queue").build();
    }
    @Bean("warning_queue")
    Queue queue5(){
     
        return QueueBuilder.nonDurable("warning.queue").build();
    }
    //备份交换机和备份队列、警告队列的绑定
    @Bean
    Binding binding4(FanoutExchange backup_exchange , Queue backup_queue ){
     
        return BindingBuilder.bind(backup_queue).to(backup_exchange);
    }
    @Bean
    Binding binding5(FanoutExchange backup_exchange , Queue warning_queue){
     
        return BindingBuilder.bind(warning_queue).to(backup_exchange);
    }
}

同时交换机的确认回调函数也进行设置,同样的参考上文,要在yaml文件中开启交换机确认的配置

@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback {
     
    @Autowired
    private  RabbitTemplate rabbitTemplate;
    @PostConstruct
    public void  init(){
     
      rabbitTemplate.setConfirmCallback(this);
    }
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
     
        if(ack==true){
     
            System.err.println("交换机成功接收消息");
        }else{
     
            ReturnedMessage returned = correlationData.getReturned();
            System.err.println(correlationData.getReturned().toString());
            System.err.println(new String(correlationData.getReturned().getMessage().getBody()));
            System.out.println(cause);
        }
    }
}

controller中配置对应的测试生产者

@RequestMapping("/fei")
    public void test2(){
     
        CorrelationData correlationData=new CorrelationData();
        correlationData.setReturned(new ReturnedMessage(new Message("feifeifei".getBytes()),0,null,"confirm.exchange","test"));
        rabbitTemplate.convertAndSend("confirm.exchange","test11111","feifeifei",correlationData);
    }

以及最后的消费者

@Component
public class listener {
     
    @RabbitListener(queues = "confirm.queue")
    public void  test(String msg){
     
        System.out.println(msg);
    }
    @RabbitListener(queues = "warning.queue")
    public void test2(Message message){
     
        String msg=new String(message.getBody());
        System.err.println("我不能路由 呜呜呜呜呜"+msg);
    }
}

启动springboot项目后,虽然我们正确的将消息发到了交换机上,但是此时不能正确的进行过路由,按照前边的方法,会进行消息回退

但此时我们配置了,备份交换机,消息会进入备份交换机 通过fanout广播到与之经历绑定的队列上,然后backup.queue的消费者对message进行备份,

warning.queue来进行提醒日志的工作

上述代码的整体的运行结果如下,端口的访问这边使用的是postman也不用加任何参数
RabbitMq发布确认高级_第4张图片

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