为什么要用到延迟消息队列,使用RabbitMQ实现案例!

背景

延迟消息的意思就是当我们的消息发送以后,并不希望马上被消费,需要隔一段时间后才能被消费。为什么会用到这个延迟消息?比如我们常见的下单场景:

我们订单下单之后,一般会有15分钟的支付时间,如果超时未支付,系统自动取消订单,并释放占用的库存。

要实现这个可以做一个定时任务轮训数据库,但是太消耗内存了,这就需要用到我们的延迟队列。

RabbitMQ延迟消息队列

实现原理

RabbitMQ本身没有实现延迟消息,但是我们可以通过他的TTL(Time To Live)和死信(Dead Letter Exchanges)来模拟延迟队列。

  • TTL(Time To Live)——消息的存活时间(其实就是消息在队列里面没有被消费,存活的时间)

  • Dead Letter Exchanges ——死信路由(在某一个设置Dead Letter Exchange的队列中有消息过期了,会自动触发消息的转发,发送到Dead Letter Exchange中去)

结合二者,我们就可以实现一个延迟队列。如图所示:

在这里插入图片描述

  1. 我们创建了一个交换机order-event-exchange,其实就是普通的交换机,用于转发消息。
  2. 创建了一个延迟队列order.delay.queue,设置了消息在队列里面存活30min,并设置了死信路由x-dead-letter-exchange到order-event-exchange、设置死信路由键order.release.order。(注意这个队列永远不会被消费)
  3. 创建了订单释放队列order.release.order.queue,消息在order.delay.queue里面存活30分钟以后,将根据死信路由经交换机order-event-exchange路由到这里,进行消费。

代码实现

  1. 创建交换机order-event-exchange(使用给容器中加组件的方式创建交换机及队列)

        /**
         * TopicExchange
         *
         * @return
         */
        @Bean
        public Exchange orderEventExchange() {
            /*
             *   String name,
             *   boolean durable,
             *   boolean autoDelete,
             *   Map<String, Object> arguments
             * */
            return new TopicExchange("order-event-exchange", true, false);
        }
    
  2. 创建延迟队列order.delay.queue

    /**
     * 死信队列
     *
     * @return
     */
    @Bean
    public Queue orderDelayQueue() {
        /*
            Queue(String name,  队列名字
            boolean durable,  是否持久化
            boolean exclusive,  是否排他
            boolean autoDelete, 是否自动删除
            Map<String, Object> arguments) 属性
         */
        HashMap<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", "order-event-exchange");
        arguments.put("x-dead-letter-routing-key", "order.release.order");
        arguments.put("x-message-ttl", 60000); // 消息过期时间 1分钟
        Queue queue = new Queue("order.delay.queue", true, false, false, arguments);
        return queue;
    }
    
  3. 创建订单释放队列order.release.order.queue

    /**
     * 普通队列
     *
     * @return
     */
    @Bean
    public Queue orderReleaseQueue() {
        Queue queue = new Queue("order.release.order.queue", true, false, false);
        return queue;
    }
    
  4. 接下来进行绑定,延迟队列与交换机进行bind(key:order.create.order),订单释放队列与交换机进行bind(key:order.release.order)。

    @Bean
    public Binding orderCreateBinding() {
        /*
         * String destination, 目的地(队列名或者交换机名字)
         * DestinationType destinationType, 目的地类型(Queue、Exhcange)
         * */
        return new Binding("order.delay.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.create.order",
                null);
    }
    
    @Bean
    public Binding orderReleaseBinding() {
        return new Binding("order.release.order.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.release.order",
                null);
    }
    
  5. 至此,准备工作已完成,现在我们定义好发送消息和接受消息的方法。

    /**
     * 发送消息
     */
    @ResponseBody
    @GetMapping(value = "/test/createOrder")
    public String createOrderTest() {
        //订单下单成功
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(UUID.randomUUID().toString());
        orderEntity.setModifyTime(new Date());
        //给MQ发送消息
        rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",orderEntity);
        return "ok";
    }
    
    /**
     * 接受消息
     * queues:声明需要监听的队列
     * channel:当前传输数据的通道
     */
    @RabbitListener(queues = {"order.release.stock.queue"})
    public void revieveMessage(Message message, OrderReturnReasonEntity content, Channel channel) throws IOException {
        //拿到主体内容
        byte[] body = message.getBody();
        //拿到的消息头属性信息
        MessageProperties messageProperties = message.getMessageProperties();
        System.out.println("接受到的消息...内容" + message + "===内容:" + content);
        // 消息抵达,可靠确认机制
        long deliveryTag = messageProperties.getDeliveryTag();
        channel.basicAck(deliveryTag, false);
    }
    

效果展示

可以看到消息发送以后,在我们延迟队列里面一直没有被消费

在这里插入图片描述

一分钟以后,由延迟队列转发到我们订单释放队列,并且马上被消费,至此大功告成!

在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值