RabbitMQ的四种交换机使用及死信队列

RabbitMQ的四种交换机使用xulong_87231

一、Direct exchange---直连类型交换机

直连型交换机中绑定queue时需要指定路由key,发送消息时也需要指定路由key。当消息发送到对应的exchange中,rabbitMQ会到找到路由完全匹配的key的queue中。

1、定义exchange、queue、key 

 /**
     * 直连交换机
     * 根据消息发送的key,找到交换机中对应key的queue
     */
    String DIRECT_EXCHANGE = "direct_exchange";
    String DIRECT_EXCHANGE_ONE = "direct_exchange_one";
    String DIRECT_EXCHANGE_ONE_KEY = "direct_exchange_one_key";
    String DIRECT_EXCHANGE_TWO = "direct_exchange_two";
    String DIRECT_EXCHANGE_TWO_KEY = "direct_exchange_two_key";

2、绑定注册exchange、queue

//直连交换机
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange(RabbitmqDefinition.DIRECT_EXCHANGE);
    }
    @Bean
    public Queue directExchangeOneQueue() {
        return new Queue(RabbitmqDefinition.DIRECT_EXCHANGE_ONE, true);
    }
    @Bean
    public Binding directExchangeOneQueueBinding() {
        return BindingBuilder
                .bind(directExchangeOneQueue()).to(directExchange()).with(RabbitmqDefinition.DIRECT_EXCHANGE_ONE_KEY);
    }
    @Bean
    public Queue directExchangeTwoQueue() {
        return new Queue(RabbitmqDefinition.DIRECT_EXCHANGE_TWO, true);
    }
    @Bean
    public Binding directExchangeTwoQueueBinding() {
        return BindingBuilder
                .bind(directExchangeTwoQueue()).to(directExchange()).with(RabbitmqDefinition.DIRECT_EXCHANGE_TWO_KEY);
    }

3、定义消费者

 @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.DIRECT_EXCHANGE_ONE)
    public void directExchangeOne(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue:{},接受到消息:{}", "DIRECT_EXCHANGE_ONE", payload);
    }

    @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.DIRECT_EXCHANGE_TWO)
    public void directExchangeTwo(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue:{},接受到消息:{}", "DIRECT_EXCHANGE_TWO", payload);
    }

4、定义生产者

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Scheduled(fixedRate = 5000)
    public void directExchangeMessage() {
        log.info("EXCHANGE {}", "DIRECT_EXCHANGE");
        Map<String, String> map = new HashMap<>();
        map.put("exchange", "DIRECT_EXCHANGE");
        map.put("key", "direct_exchange_one_key");
        rabbitTemplate.convertAndSend(RabbitmqDefinition.DIRECT_EXCHANGE,RabbitmqDefinition.DIRECT_EXCHANGE_ONE_KEY, JSONObject.toJSONString(map));
        map.put("key", "direct_exchange_two_key");
        rabbitTemplate.convertAndSend(RabbitmqDefinition.DIRECT_EXCHANGE,RabbitmqDefinition.DIRECT_EXCHANGE_TWO_KEY, JSONObject.toJSONString(map));
    }

5、消费者输出结果

当前queue:DIRECT_EXCHANGE_TWO,接受到消息:{"exchange":"DIRECT_EXCHANGE","key":"direct_exchange_two_key"}
当前queue:DIRECT_EXCHANGE_ONE,接受到消息:{"exchange":"DIRECT_EXCHANGE","key":"direct_exchange_one_key"}

二、Fanout Exchange---扇出类型交换机xulong_87231

扇出类型Exchange绑定queue时不需要指定路由key,当消费发送到交换机时,交换机会将消息发送到所以绑定当前 Exchange的queue。

1、定义exchange、queue、key 

 /**
     * 扇形交换机
     * 往改交换机发送的消息,绑定了当前交换机的queue都能接收到消息
     */
    String FANOUT_EXCHANGE = "fanout_exchange";
    String FANOUT_EXCHANGE_ONE = "fanout_exchange_one";
    String FANOUT_EXCHANGE_TWO = "fanout_exchange_two";

2、绑定注册exchange、queue

// 扇形交换机
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(RabbitmqDefinition.FANOUT_EXCHANGE);
    }
    @Bean
    public Queue fanoutExchangeOneQueue() {
        return new Queue(RabbitmqDefinition.FANOUT_EXCHANGE_ONE, true);
    }
    @Bean
    public Binding fanoutExchangeOneQueueBinding() {
        return BindingBuilder
                .bind(fanoutExchangeOneQueue()).to(fanoutExchange());
    }
    @Bean
    public Queue fanoutExchangeTwoQueue() {
        return new Queue(RabbitmqDefinition.FANOUT_EXCHANGE_TWO, true);
    }
    @Bean
    public Binding fanoutExchangeTwoQueueBinding() {
        return BindingBuilder
                .bind(fanoutExchangeTwoQueue()).to(fanoutExchange());
    }

3、定义消费者

@RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.FANOUT_EXCHANGE_ONE)
    public void fanoutExchangeOne(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue:{},接受到消息:{}", "FANOUT_EXCHANGE_ONE", payload);
    }

    @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.FANOUT_EXCHANGE_TWO)
    public void fanoutExchangeTwo(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue:{},接受到消息:{}", "FANOUT_EXCHANGE_TWO", payload);
    }

4、定义生产者

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Scheduled(fixedRate = 10000)
    public void fanoutExchangeMessage() {
        log.info("EXCHANGE {}", "FANOUT_EXCHANGE");
        Map<String, String> map = new HashMap<>();
        map.put("exchange", "FANOUT_EXCHANGE");
        rabbitTemplate.convertAndSend(RabbitmqDefinition.FANOUT_EXCHANGE,"", JSONObject.toJSONString(map));
    }

5、消费者输出结果

当前queue:FANOUT_EXCHANGE_ONE,接受到消息:{"exchange":"FANOUT_EXCHANGE"}
当前queue:FANOUT_EXCHANGE_TWO,接受到消息:{"exchange":"FANOUT_EXCHANGE"}

三、Topic Exchange---主题类型交换机

Topic Exchange与Direct exchange相似,不过Direct exchange消息接受需要全匹配路由key,Topic Exchange只需要模糊匹配,与绑定exchange的路由key相关,当不需要匹配时使用匹配符合*

1、定义exchange、queue、key 

/**
     * 主题交换机
     * 发送到交换机的消息指定规则,交换机将根据对应规则发送到对应的queue中
     * 类似于直连交换机的部分匹配
     */
    String TOPIC_EXCHANGE = "topic_exchange";
    String TOPIC_EXCHANGE_ONE = "topic_exchange_one";
    String TOPIC_EXCHANGE_TWO = "topic_exchange_two";
    String TOPIC_EXCHANGE_THREE = "topic_exchange_three";
    String TOPIC_EXCHANGE_ONE_KEY = "season.*.*";
    String TOPIC_EXCHANGE_TWO_KEY = "*.colour.*";
    String TOPIC_EXCHANGE_THREE_KEY = "*.*.action";

2、绑定注册exchange、queue

// 主题交换机
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(RabbitmqDefinition.TOPIC_EXCHANGE);
    }
    @Bean
    public Queue topicExchangeOneQueue() {
        return new Queue(RabbitmqDefinition.TOPIC_EXCHANGE_ONE, true);
    }
    @Bean
    public Queue topicExchangeTwoQueue() {
        return new Queue(RabbitmqDefinition.TOPIC_EXCHANGE_TWO, true);
    }
    @Bean
    public Queue topicExchangeThreeQueue() {
        return new Queue(RabbitmqDefinition.TOPIC_EXCHANGE_THREE, true);
    }
    @Bean
    public Binding topicExchangeOneQueueBinding() {
        return BindingBuilder
                .bind(topicExchangeOneQueue()).to(topicExchange()).with(RabbitmqDefinition.TOPIC_EXCHANGE_ONE_KEY);
    }
    @Bean
    public Binding topicExchangeTwoQueueBinding() {
        return BindingBuilder
                .bind(topicExchangeTwoQueue()).to(topicExchange()).with(RabbitmqDefinition.TOPIC_EXCHANGE_TWO_KEY);
    }
    @Bean
    public Binding topicExchangeThreeQueueBinding() {
        return BindingBuilder
                .bind(topicExchangeThreeQueue()).to(topicExchange()).with(RabbitmqDefinition.TOPIC_EXCHANGE_THREE_KEY);
    }

3、定义消费者

 @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.TOPIC_EXCHANGE_ONE)
    public void topicExchangeOne(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue:{},接受到消息:{}", "TOPIC_EXCHANGE_ONE", payload);
    }

    @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.TOPIC_EXCHANGE_TWO)
    public void topicExchangeTwo(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue:{},接受到消息:{}", "TOPIC_EXCHANGE_TWO", payload);
    }

    @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.TOPIC_EXCHANGE_THREE)
    public void topicExchangeThree(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue:{},接受到消息:{}", "TOPIC_EXCHANGE_THREE", payload);
    }

4、定义生产者

    @Scheduled(fixedRate = 10000)
    public void topicExchangeMessage() {
        log.info("EXCHANGE {}", "TOPIC_EXCHANGE");
        Map<String, String> map = new HashMap<>();
        map.put("exchange", "TOPIC_EXCHANGE");
        map.put("key", "season.colour.other");
        map.put("message", "春天里的树叶是绿色的");
        rabbitTemplate.convertAndSend(RabbitmqDefinition.TOPIC_EXCHANGE,"season.colour.other", JSONObject.toJSONString(map));
        map.put("key", "other.colour.action");
        map.put("message", "一只白色的兔子在草地上奔跑");
        rabbitTemplate.convertAndSend(RabbitmqDefinition.TOPIC_EXCHANGE,"other.colour.action", JSONObject.toJSONString(map));
        map.put("key", "other.colour.action");
        map.put("message", "冬天的动物似乎都不太愿意动起来,只有在扑食时才会追击猎物");
        rabbitTemplate.convertAndSend(RabbitmqDefinition.TOPIC_EXCHANGE,"season.other.action", JSONObject.toJSONString(map));
    }

5、消费者输出结果

当前queue:TOPIC_EXCHANGE_TWO,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"春天里的树叶是绿色的","key":"season.colour.other"}
当前queue:TOPIC_EXCHANGE_ONE,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"春天里的树叶是绿色的","key":"season.colour.other"}
当前queue:TOPIC_EXCHANGE_THREE,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"一只白色的兔子在草地上奔跑","key":"other.colour.action"}
当前queue:TOPIC_EXCHANGE_TWO,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"一只白色的兔子在草地上奔跑","key":"other.colour.action"}
当前queue:TOPIC_EXCHANGE_ONE,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"冬天的动物似乎都不太愿意动起来,只有在扑食时才会追击猎物","key":"other.colour.action"}
当前queue:TOPIC_EXCHANGE_THREE,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"冬天的动物似乎都不太愿意动起来,只有在扑食时才会追击猎物","key":"other.colour.action"}

四、Headers Exchanges---头部类型交换机

不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。

1、定义exchange、queue、key 

    String HEADERS_EXCHANGE = "headers_exchange";
    String HEADERS_EXCHANGE_ONE = "headers_exchange_one";
    String HEADERS_EXCHANGE_TWO = "headers_exchange_two";

2、绑定注册exchange、queue

    @Bean
    public HeadersExchange headersExchange() {
        return new HeadersExchange(RabbitmqDefinition.HEADERS_EXCHANGE);
    }
    @Bean
    public Queue headersExchangeOneQueue() {
        return new Queue(RabbitmqDefinition.HEADERS_EXCHANGE_ONE);
    }
    @Bean
    public Queue headersExchangeTwoQueue() {
        return new Queue(RabbitmqDefinition.HEADERS_EXCHANGE_TWO);
    }

    /**
     * whereAny 只要符合其中一个请求头,queue可接受消息
     * whereAll 所有请求头符合,queue可以接受消息
     */
    @Bean
    public Binding headersExchangeOneQueueBinding() {
        Map<String, Object> map = new HashMap<>();
        map.put("exchange", "HEADERS_EXCHANGE");
        map.put("bindType", "whereAny");
        return BindingBuilder
                .bind(headersExchangeOneQueue()).to(headersExchange()).whereAny(map).match();
    }
    @Bean
    public Binding headersExchangeTwoQueueBinding() {
        Map<String, Object> map = new HashMap<>();
        map.put("exchange", "HEADERS_EXCHANGE");
        map.put("queue", "two");
        map.put("bindType", "whereAll");
        return BindingBuilder
                .bind(headersExchangeTwoQueue()).to(headersExchange()).whereAll(map).match();
    }

3、定义消费者

    @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.HEADERS_EXCHANGE_ONE)
    public void headersExchangeOne(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue:{},接受到消息:{}", "HEADERS_EXCHANGE_ONE", payload);
    }

    @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.HEADERS_EXCHANGE_TWO)
    public void headersExchangeTwo(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue:{},接受到消息:{}", "HEADERS_EXCHANGE_TWO", payload);
    }

4、定义生产者

    @Scheduled(fixedRate = 10000)
    public void headersExchangeMessage() {
        log.info("EXCHANGE {}", "HEADERS_EXCHANGE");
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setHeader("exchange", "HEADERS_EXCHANGE");
        messageProperties.setHeader("bindType", "whereAny");
        Message messageOne = new Message("第一次发送数据".getBytes(), messageProperties);
        rabbitTemplate.convertAndSend(RabbitmqDefinition.HEADERS_EXCHANGE,null, messageOne);

        messageProperties.setHeader("queue", "two");
        messageProperties.setHeader("bindType", "whereAll");
        Message messageTwo = new Message("第二次发送数据".getBytes(), messageProperties);
        rabbitTemplate.convertAndSend(RabbitmqDefinition.HEADERS_EXCHANGE,null, messageTwo);
    }

5、消费者输出结果

当前queue:HEADERS_EXCHANGE_ONE,接受到消息:第一次发送数据
当前queue:HEADERS_EXCHANGE_TWO,接受到消息:第二次发送数据
当前queue:HEADERS_EXCHANGE_ONE,接受到消息:第二次发送数据

死信队列

死信队列介绍

  • 死信队列:DLX,dead-letter-exchange
  • 利用DLX,当消息在一个队列中变成死信 (dead message) 之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX

什么是死信队列

        顾名思义,死信队列就是当前队列死掉了就是死信队列。当被指定的死信队列因为以下原因,则会将消息转发到对应绑定的队列中

  • 消息被拒绝(basic.reject / basic.nack),并且requeue = false
  • 消息TTL过期
  • 队列达到最大长度

死信队列有什么作用

  • 当消息处理异常,需要后续处理时可以使用死信队列
  • 延迟队列,比如订单提交后30分支未支付订单取消

代码演示

1、定义exchange、queue、key

  • 死信队列包括交换机及队列
  • 死信队列其实就是一个正常的队列
    /**
     * 死信队列相关
     */
    String DXL_EXCHANGE = "dxl_exchange";
    String DXL_QUEUE = "dxl_queue";
    String DXL_KEY = "dxl_key";
    // 业务相关
    String BUSINESS_EXCHANGE = "business_exchange";
    String BUSINESS_QUEUE_ONE = "business_queue_one";
    String BUSINESS_QUEUE_TWO = "business_queue_two";

2、绑定注册exchange、queue

/**
     * 死信队列其实就是一个正常的交换机及队列
     */
    @Bean
    public DirectExchange dxlExchange() {
        return new DirectExchange(RabbitmqDefinition.DXL_EXCHANGE);
    }
    @Bean
    public Queue dxlQueue() {
        return new Queue(RabbitmqDefinition.DXL_QUEUE, true);
    }
    @Bean
    public Binding dxlQueueBinding() {
        return BindingBuilder
                .bind(dxlQueue()).to(dxlExchange()).with(RabbitmqDefinition.DXL_KEY);
    }
    /**
     * 业务交换机及队列
     * BUSINESS_QUEUE_ONE 正常业务处理,参照组
     * BUSINESS_QUEUE_TWO 设置为死信的业务队列
     */
    @Bean
    public FanoutExchange businessExchange() {
        return new FanoutExchange(RabbitmqDefinition.BUSINESS_EXCHANGE);
    }
    @Bean
    public Queue businessQueueOneQueue() {
        return new Queue(RabbitmqDefinition.BUSINESS_QUEUE_ONE, true);
    }
    @Bean
    public Binding businessQueueOneQueueBinding() {
        return BindingBuilder
                .bind(businessQueueOneQueue()).to(businessExchange());
    }
    @Bean
    public Queue businessQueueTwoQueue() {
        Map<String, Object> map = new HashMap<>();
        // 声明当前死信队列绑定的死信交换机,可以是任何类型的交换机
        map.put("x-dead-letter-exchange", RabbitmqDefinition.DXL_EXCHANGE);
        // 声明当前队列的死信路由键
        map.put("x-dead-letter-routing-key", RabbitmqDefinition.DXL_KEY);
        // 设置队列消息的超时时间,单位毫秒,超过时间进入死信队列,也可以在发送消息处设置超时时间,都设置时,以最小超时时间为准
        map.put("x-message-ttl",3000);
        // 生命队列的最大长度,超过长度的消息进入死信队列
        map.put("x-max-length", 10);
        return new Queue(RabbitmqDefinition.BUSINESS_QUEUE_TWO, true, false, false, map);
    }
    @Bean
    public Binding businessQueueTwoQueueBinding() {
        return BindingBuilder
                .bind(businessQueueTwoQueue()).to(businessExchange());
    }

3、定义消费者

  • 做延迟队列业务操作时,可以不设置消费者
  • 如下消费者为模拟业务处理失败,转发到指定的死信队列
    @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.DXL_QUEUE)
    public void dxlQueue(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue为死信队列转发队列:{},接受到消息:{}", "DXL_QUEUE", payload);
    }

    @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.BUSINESS_QUEUE_ONE)
    public void businessQueueOne(Message message, @Payload String payload, Channel channel) {
        log.info("当前queue为正常参照业务:{},接受到消息:{}", "BUSINESS_QUEUE_ONE", payload);
    }

    @RabbitHandler
    @RabbitListener(queues = RabbitmqDefinition.BUSINESS_QUEUE_TWO)
    public void businessQueueTwo(Message message, @Payload String payload, Channel channel) throws IOException {
        log.info("当前queue为业务死信队列:{},接受到消息:{}", "BUSINESS_QUEUE_TWO", payload);
        try {
            String str = null;
            str.equals("测试报错");
        }catch (Exception e) {
            // 第三个参数设置为false,则当前mq消息消费失败不会重新发送到当前mq
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), true, false);
        }

    }

4、定义生产者

    @Scheduled(fixedRate = 10000)
    public void dxlExchangeMessage() {
        Map<String, String> map = new HashMap<>();
        map.put("exchange", "BUSINESS_EXCHANGE");
        map.put("content", "测试业务");
        rabbitTemplate.convertAndSend(RabbitmqDefinition.BUSINESS_EXCHANGE,"", JSONObject.toJSONString(map));
        // 在发送时设置消息超时时间
//        rabbitTemplate.convertAndSend(RabbitmqDefinition.BUSINESS_EXCHANGE,"", JSONObject.toJSONString(map), message -> {
//            // 发送消息时设置消息的超时时间
//            message.getMessageProperties().setExpiration("5000");
//            return message;
//        });
    }

5、消费者输出结果

  • 下方两个输出结果分别为处理业务异常及不配置消费者的延迟队列
2022-04-01 11:10:16.245  INFO 11200 --- [ntContainer#6-1] c.example.demo.rabbitmq.MessageListener  : 当前queue为正常参照业务:BUSINESS_QUEUE_ONE,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}
2022-04-01 11:10:16.245  INFO 11200 --- [ntContainer#9-1] c.example.demo.rabbitmq.MessageListener  : 当前queue为业务死信队列:BUSINESS_QUEUE_TWO,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}
2022-04-01 11:10:16.247  INFO 11200 --- [tContainer#10-1] c.example.demo.rabbitmq.MessageListener  : 当前queue为死信队列转发队列:DXL_QUEUE,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}
2022-04-01 11:11:15.215  INFO 21016 --- [ntContainer#7-1] c.example.demo.rabbitmq.MessageListener  : 当前queue为正常参照业务:BUSINESS_QUEUE_ONE,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}
2022-04-01 11:11:18.218  INFO 21016 --- [ntContainer#4-1] c.example.demo.rabbitmq.MessageListener  : 当前queue为死信队列转发队列:DXL_QUEUE,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值