RabbitMQ(四):mandatory、immediate、备份交换器

  • mandatory和immediate是basic.publish方法中的两个参数,他们都有当消息传递不可抵达队列时将消息返回给生产者的能力。备份交换器则可以将未被交换器路由的消息储存起来、不返回给生产者,他们都是保证消息不丢失的策略。

mandatory

当mandatory参数设置为true时,交换器无法根据自身的类型和路由键找到一个符合条件的队列,那么Rabbit MQ会调用basic.return命令将消息返回给生产者。当mandatory参数设置为false时,遇到以上情况消息会直接被丢弃,默认为false。

使用mandatory参数只需两步:

  • basic.publish时指定mandatory参数为true
  • 生产者添加addReturnListener监听器
    代码如下,这里我们故意指定一个错误的路由键
channel.basicPublish("user_exchange", "", true, MessageProperties.PERSISTENT_TEXT_PLAIN, "hello mandatory".getBytes());
channel.addReturnListener(new ReturnCallback() {
    @Override
    public void handle(Return returnMessage) {
        System.out.println(new String(returnMessage.getBody()));
    }
});

immediate

当immediate参数设置为true时,如果交换器在将消息路由到队列时发现队列上并不存在任何消费者,那么这条消息不会存入队列中。当与路由键匹配的所有队列都没有消费者时,该消息会通过basic.return返回给生产者。

概括来说,mandatory告诉服务器至少将该消息路由到一个队列中,否则将消息返回给生产者。immediate参数告诉服务器,如果该消息关联的队列上有消费者、则立即投递;如果所有匹配的队列上都没有消费者,则直接将消息返回给生产者,不用将消息存入队列而等待消费者。

在Rabbit MQ3.0之后的版本开始不支持immediate参数,原因时该参数会影响镜像队列的性能,增加了代码的复杂性。不过可以使用TTL和DLX来替代该参数实现。TTL时消息(队列)过期时间,可以设置TTL为0,表示立即投递;DLX是死信队列,如果队列没有关联消费者,则立即进入死信队列。在3.0之后的版本如果设置immediate参数为true,程序会抛出异常,如下图:immediate参数的异常信息

备份交换器

如果消息在发送的时候不设置mandatory参数,那么消息在未被路由的情况下会丢失,如果设置了mandatory参数,那么在生产者需要添加returnListener的编程逻辑,生产者的代码逻辑将变得复杂。此时可以选择设置一个备份交换器,这样可以将未被路由的消息暂存在备份交换器,等到合适的时候在编写consumer来处理这些消息。

可以在channel.exchangeDeclare方法中添加alternate-exchange参数来实现。代码如下:

    public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setVirtualHost("/");
        factory.setHost(RabbitConstant.host);
        factory.setPort(RabbitConstant.post);
        factory.setUsername(RabbitConstant.username);
        factory.setPassword(RabbitConstant.password);

        try (Connection conn = factory.newConnection();
             Channel channel = conn.createChannel()) {
            Map<String, Object> params = new HashMap<>(4);
            params.put("alternate-exchange", "myAE");
            channel.exchangeDeclare("normal_exchange", "direct", true, false, params);
            channel.exchangeDeclare("myAE", "fanout", true, false, null);
            channel.queueDeclare("normal_queue", true, false, false, null);
            channel.queueBind("normal_queue", "normal_exchange", "normal_key");
            channel.queueDeclare("ae_queue", true, false, false, null);
            channel.queueBind("ae_queue", "myAE", "");
            channel.basicPublish("normal_exchange", "error_routing_key", null, "hello hello".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }

在这个demo中,我们向normal_exchange交换器发送了一条"hello hello"的消息,路由键为error_routing_key,很明显是错误的,normal_exchange只有一个名为normal_key的路由键,当无法路由时,消息会被路由到我们定义的myAE交换其中,myAE交换器是fanout类型,无路由键。在管理页面也可以看到ae_queue队列中多了一条消息。
备份交换器

备份交换器特殊情况

  • 如果设置的备份交换器不存在,客户端和Rabbit MQ服务端都不会有异常出现,此时消息丢失
  • 如果备份交换器没有绑定任何队列,客户端和Rabbit MQ服务端不会出现任何异常,此时消息亦丢失
  • 如果备份交换器没有路由匹配任何队列,客户端和Rabbit MQ服务端不会出现任何异常,此时消息丢失
  • 如果备份交换器和mandatory参数一起使用,则mandatory参数会失效。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值