Springboot+rabbitmq延时队列实现方式

本文介绍了Springboot使用RabbitMQ创建延时队列的实践,包括延时队列的概念及其应用场景,如订单超时、预约提醒等。通过配置application.yml文件,创建队列和实体类,实现消息接收与发送,最后通过测试验证延时队列的功能。
摘要由CSDN通过智能技术生成

Springboot+rabbitmq延时队列实现方式

1.什么是延时队列,延时队列

​ 延时队列顾名思义,即放置在该队列里面的消息是不需要立即消费的,而是等待一段时间之后取出消费。

那么,为什么需要延迟消费呢?我们来看以下的场景

  1. 网上商城下订单后30分钟后没有完成支付,取消订单(如:淘宝、去哪儿网)

  2. 系统创建了预约之后,需要在预约时间到达前一小时提醒被预约的双方参会

  3. 系统中的业务失败之后,需要重试

  ## 2. SpringBoot整合RabbitMQ具体实现

  1.  在 `pom.xml` 中添加 项目所需的依赖 

  ````xml
  <dependencies>
  		<dependency>
  			<groupId>org.springframework.boot</groupId>
  			<artifactId>spring-boot-starter-amqp</artifactId>
  		</dependency>
  		<dependency>
  			<groupId>org.springframework.boot</groupId>
  			<artifactId>spring-boot-starter-web</artifactId>
  		</dependency>
  		<dependency>
  			<groupId>org.projectlombok</groupId>
  			<artifactId>lombok</artifactId>
  		</dependency>
   
  		<dependency>
  			<groupId>org.springframework.boot</groupId>
  			<artifactId>spring-boot-starter-test</artifactId>
  			<scope>test</scope>
  		</dependency>
  	</dependencies>
  
  ````

​ 2. 在appliaction.yml文件中配置rabbitmq相关内容

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
具体编码实现
  1. 配置队列

    package com.lzc.rabbitmq.config;
     
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.amqp.core.*;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
     
    import java.util.HashMap;
    import java.util.Map;
     
    @Configuration
    @Slf4j
    public class DelayRabbitConfig {
     
     
        /**
         * 延迟队列 TTL 名称
         */
        private static final String ORDER_DELAY_QUEUE = "user.order.delay.queue";
        /**
         * DLX,dead letter发送到的 exchange
         * 延时消息就是发送到该交换机的
         */
        public static final String ORDER_DELAY_EXCHANGE = "user.order.delay.exchange";
        /**
         * routing key 名称
         * 具体消息发送在该 routingKey 的
         */
        public static final String ORDER_DELAY_ROUTING_KEY = "order_delay";
     
        public static final String ORDER_QUEUE_NAME = "user.order.queue";
        public static final String ORDER_EXCHANGE_NAME = "user.order.exchange";
        public static final String ORDER_ROUTING_KEY = "order";
     
        /**
         * 延迟队列配置
         * <p>
         * 1、params.put("x-message-ttl", 5 * 1000);
         * 第一种方式是直接设置 Queue 延迟时间 但如果直接给队列设置过期时间,这种做法不是很灵活,(当然二者是兼容的,默认是时间小的优先)
         * 2、rabbitTemplate.convertAndSend(book, message -> {
         * message.getMessageProperties().setExpiration(2 * 1000 + "");
         * return message;
         * });
         * 第二种就是每次发送消息动态设置延迟时间,这样我们可以灵活控制
         **/
        @Bean
        public Queue delayOrderQueue() {
            Map<String, Object> params = new HashMap<>();
            // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
            params.put("x-dead-letter-exchange", ORDER_EXCHANGE_NAME);
            // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
            params.put("x-dead-letter-routing-key", ORDER_ROUTING_KEY);
            return new Queue(ORDER_DELAY_QUEUE, true, false, false, params);
        }
        /**
         * 需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。
         * 这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,
         * 不会转发dog.puppy,也不会转发dog.guard,只会转发dog。
         * @return DirectExchange
         */
        @Bean
        public DirectExchange orderDelayExchange() {
            return new DirectExchange(ORDER_DELAY_EXCHANGE);
        }
        @Bean
        public Binding dlxBinding() {
            return BindingBuilder.bind(delayOrderQueue()).to(orderDelayExchange()).with(ORDER_DELAY_ROUTING_KEY);
        }
     
        @Bean
        public Queue orderQueue() {
            return new Queue(ORDER_QUEUE_NAME, true);
        }
        /**
         * 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。
         * 符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。
         **/
        @Bean
        public TopicExchange orderTopicExchange() {
            return new TopicExchange(ORDER_EXCHANGE_NAME);
        }
     
        @Bean
        public Binding orderBinding() {
            // TODO 如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
            return Bindi
                ngBuilder.bind(orderQueue()).to(orderTopicExchange()).with(ORDER_ROUTING_KEY);
        }
        
        
    
  2. 创建一个Order实体类

    package com.lzc.rabbitmq.dataobject;
     
    import lombok.Data;
     
    import java.io.Serializable;
     
    @Data
    public class Order implements Serializable {
     
     
        private static final long serialVersionUID = -2221214252163879885L;
        
        private String orderId; // 订单id
     
        private Integer orderStatus; // 订单状态 0:未支付,1:已支付,2:订单已取消
     
        private String orderName; // 订单名字
    }
    
  3. 接受者

    
    package com.lzc.rabbitmq.config;
     
    import com.lzc.rabbitmq.dataobject.Order;
    import com.rabbitmq.client.Channel;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.amqp.core.Message;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
     
    import java.util.Date;
     
    @Component
    @Slf4j
    public class DelayReceiver {
     
        @RabbitListener(queues = {DelayRabbitConfig.ORDER_QUEUE_NAME})
        public void orderDelayQueue(Order order, Message message, Channel channel) {
            log.info("###########################################");
            log.info("【orderDelayQueue 监听的消息】 - 【消费时间】 - [{}]- 【订单内容】 - [{}]",  new Date(), order.toString());
            if(order.getOrderStatus() == 0) {
                order.setOrderStatus(2);
                log.info("【该订单未支付,取消订单】" + order.toString());
            } else if(order.getOrderStatus() == 1) {
                log.info("【该订单已完成支付】");
            } else if(order.getOrderStatus() == 2) {
                log.info("【该订单已取消】");
            }
            log.info("###########################################");
        }
    }
    
  4. 发送者

    package com.lzc.rabbitmq.config;
     
    import com.lzc.rabbitmq.dataobject.Order;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.amqp.core.AmqpTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
     
    import java.util.Date;
     
    @Component
    @Slf4j
    public class DelaySender {
     
        @Autowired
        private AmqpTemplate amqpTemplate;
     
        public void sendDelay(Order order) {
            log.info("【订单生成时间】" + new Date().toString() +"【1分钟后检查订单是否已经支付】" + order.toString() );
            this.amqpTemplate.convertAndSend(DelayRabbitConfig.ORDER_DELAY_EXCHANGE, DelayRabbitConfig.ORDER_DELAY_ROUTING_KEY, order, message -> {
                // 如果配置了 params.put("x-message-ttl", 5 * 1000); 那么这一句也可以省略,具体根据业务需要是声明 Queue 的时候就指定好延迟时间还是在发送自己控制时间
                message.getMessageProperties().setExpiration(1 * 1000 * 60 + "");
                return message;
            });
        }
    }
    
  5. 测试,访问http://localhost:8080/sendDelay,查看日志输出

package com.lzc.rabbitmq.controller;
 
import com.lzc.rabbitmq.config.DelaySender;
import com.lzc.rabbitmq.config.Sender;
import com.lzc.rabbitmq.dataobject.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.concurrent.DelayQueue;
 
@RestController
public class TestController {
 
    @Autowired
    private DelaySender delaySender;
 
    @GetMapping("/sendDelay")
    public Object sendDelay() {
        Order order1 = new Order();
        order1.setOrderStatus(0);
        order1.setOrderId("123456");
        order1.setOrderName("小米6");
 
        Order order2 = new Order();
        order2.setOrderStatus(1);
        order2.setOrderId("456789");
        order2.setOrderName("小米8");
 
        delaySender.sendDelay(order1);
        delaySender.sendDelay(order2);
        return "ok";
    }

  1. 日志输出

    【订单生成时间】Mon Jun 18 11:55:36 CST 20181分钟后检查订单是否已经支付】Order(orderId=123456, orderStatus=0, orderName=小米6)
    【订单生成时间】Mon Jun 18 11:55:36 CST 20181分钟后检查订单是否已经支付】Order(orderId=456789, orderStatus=1, orderName=小米8)
     ###########################################
    【orderDelayQueue 监听的消息】 - 【消费时间】 - [Mon Jun 18 11:56:36 CST 2018]- 【订单内容】 - [Order(orderId=123456, orderStatus=0, orderName=小米6)]
    【该订单未支付,取消订单】Order(orderId=123456, orderStatus=2, orderName=小米6)
     ###########################################
     ###########################################
    【orderDelayQueue 监听的消息】 - 【消费时间】 - [Mon Jun 18 11:56:36 CST 2018]- 【订单内容】 - [Order(orderId=456789, orderStatus=1, orderName=小米8)]
    【该订单已完成支付】
     ###########################################
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值