RabbitMQ之延时队列

RabbitMQ实现订单延时关闭功能
本文介绍了使用RabbitMQ的延时队列来处理订单1分钟未支付自动关闭的需求。通过创建延时队列和死信队列,消息在超时后自动转发到正常队列并被消费。在消费时结合业务逻辑检查订单状态,未支付则删除订单。项目集成涉及mq依赖、配置、生产者、消费者和测试类的设置。

实际工作中,有这样一种需求,订单1分钟未支付直接关闭订单,这时可以考虑用rabbitMQ的延时队列

大概思路:订单创建完成后生产者发送消息到mq延时队列中,一分钟后消息变为死信转发到正常队列中,

而消费者则一直监听正常队列

项目集成mq:

1>导入依赖:


        <!--mq依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

2>yml:(mq安装步骤度娘上很多)

spring:
  rabbitmq:
    host: 192.168.0.161
    port: 5672
    username: admin
    password: admin123

3>mq配置类:(定义正常队列和延时队列的 消息,交换机,route-key以及他们之间的绑定关系)

@Configuration
public class AMQPConfig {
    private static final Logger logger = LoggerFactory.getLogger(AMQPConfig.class);
//    /**
//     * 消息队列(测试)
//     */
//    @Bean
//    public Queue testConfig() {
//        logger.info("=============打开AMQP-"+ AMQPConstant.QUEUES_TEST_CONFIG);
//        return new Queue(AMQPConstant.QUEUES_TEST_CONFIG);
//    }



    /**
     * 正常队列(即消息过期后由延时队列自动转发到的正常队列)
     */
    //正常队列名称常量设置
    public static final String ORDER_QUEUE_NAME = "tao.order.queue";
    public static final String ORDER_EXCHANGE_NAME = "tao.order.exchange";
    public static final String ORDER_ROUTING_KEY = "order";

    //定义正常queue,Exchange,route-key之间的绑定关系
    @Bean
    public Queue orderQueue() {
        return new Queue(ORDER_QUEUE_NAME, true);
    }
    @Bean
    public TopicExchange orderTopicExchange() {
        return new TopicExchange(ORDER_EXCHANGE_NAME);
    }
    @Bean
    public Binding orderBinding() {
        // 如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
        return BindingBuilder.bind(orderQueue()).to(orderTopicExchange()).with(ORDER_ROUTING_KEY);
    }




    /**
     * 延时队列
     */
    //延时队列名称常量相关设置
    private static final String ORDER_DELAY_QUEUE = "tao.order.delay.queue";
    public static final String ORDER_DELAY_EXCHANGE = "tao.order.delay.exchange";
    public static final String ORDER_DELAY_ROUTING_KEY = "order_delay";


    //定义延时queue,Exchange,route-key之间的绑定关系
    //并且设置消息过期后的转发的死信队列以及route-key
    @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);
    }
    @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);
    }

}

4>生产者类:

@Component
@Slf4j
public class DelaySender {

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendDelay(Order order) {
        log.info("==========发送延时消息-----------【订单生成时间】" + new Date().toString() + order.toString() );
        this.amqpTemplate.convertAndSend(AMQPConfig.ORDER_DELAY_EXCHANGE, AMQPConfig.ORDER_DELAY_ROUTING_KEY, order, message -> {
            // 如果配置了 params.put("x-message-ttl", 5 * 1000); 那么这一句也可以省略,具体根据业务需要是声明 Queue 的时候就指定好延迟时间还是在发送自己控制时间
            message.getMessageProperties().setExpiration(1 * 1000 * 60 + "");
            return message;
        });
    }

    public void sendNow(Order order) {
        log.info("==========发送实时消息-----------【订单生成时间】" + new Date().toString() + order.toString() );
        this.amqpTemplate.convertAndSend(AMQPConfig.ORDER_EXCHANGE_NAME, AMQPConfig.ORDER_ROUTING_KEY,order);
    }
}

5>消费者类:

@Component
@Slf4j
public class DelayReceiver {

    @RabbitListener(queues = {AMQPConfig.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("###########################################");
    }
}

6>测试类:

@Api(value="AmqpController",description="amqp测试",tags={"amqp测试"})
@RestController
@RequestMapping("amqp")
public class TestAmqpContorller {

    @Autowired
    private DelaySender delaySender;

    @GetMapping("/sendDelay")
    public Object sendDelay() {
        Order order1 = new Order();
        order1.setOrderStatus(0);
        order1.setOrderId("123456");
        order1.setOrderName("小米6");

        //测试mq发送实时消息
        delaySender.sendNow(order1);
        //测试mq发送延时消息
        delaySender.sendDelay(order1);
        return "ok";
    }
}

访问:http://192.168.0.161:9002/amqp/sendDelay

控制台:

c.he.hsdyc.common.log.GlobalInterceptor  : -------处理开始START------
c.he.hsdyc.common.log.GlobalInterceptor  : 请求Controller: < com.he.hsdyc.controller.TestAmqpContorller >
c.he.hsdyc.common.log.GlobalInterceptor  : 请求方法: < sendDelay > 
c.he.hsdyc.common.log.GlobalInterceptor  : 请求参数:{}
c.he.hsdyc.common.log.GlobalInterceptor  : 请求方式:GET
c.he.hsdyc.common.log.GlobalInterceptor  : 请求路径:http://192.168.0.161:9002/amqp/sendDelay
com.he.hsdyc.config.Queue.DelaySender    : ==========发送实时消息-----------【订单生成时间】Tue Aug 13 09:17:43 CST 2019Order(orderId=123456, orderStatus=0, orderName=小米6)
com.he.hsdyc.config.Queue.DelaySender    : ==========发送延时消息-----------【订单生成时间】Tue Aug 13 09:17:43 CST 2019Order(orderId=123456, orderStatus=0, orderName=小米6)
com.he.hsdyc.config.Queue.DelayReceiver  : ###########################################
com.he.hsdyc.config.Queue.DelayReceiver  : 【orderDelayQueue 监听的消息】 - 【消费时间】 - [Tue Aug 13 09:17:43 CST 2019]- 【订单内容】 - [Order(orderId=123456, orderStatus=0, orderName=小米6)]
com.he.hsdyc.config.Queue.DelayReceiver  : 【该订单未支付,取消订单】Order(orderId=123456, orderStatus=2, orderName=小米6)
com.he.hsdyc.config.Queue.DelayReceiver  : ###########################################
c.he.hsdyc.common.log.GlobalInterceptor  : -------处理完成END------
com.he.hsdyc.config.Queue.DelayReceiver  : ###########################################
com.he.hsdyc.config.Queue.DelayReceiver  : 【orderDelayQueue 监听的消息】 - 【消费时间】 - [Tue Aug 13 09:18:43 CST 2019]- 【订单内容】 - [Order(orderId=123456, orderStatus=0, orderName=小米6)]
com.he.hsdyc.config.Queue.DelayReceiver  : 【该订单未支付,取消订单】Order(orderId=123456, orderStatus=2, orderName=小米6)
com.he.hsdyc.config.Queue.DelayReceiver  : ###########################################

可以看到,同时生产了两个消息,即时消息和延时消息,但是消费者一个是即时收到,另一个是一分钟后收到

 

总结:

启动时需要将mq队列,路由相关(AMQPConfig)以bean的形式注册到spring容器中去,
发送时可根据情况选择发送实时消息还是延时消息,接收时只监听正常队列
延时消息首先会进入死信队列,待ttl过期后会根据绑定的规则自动转发到正常队列中去,从而被消费,
在被消费时可根据自己的业务逻辑做相应的判断,如判断订单的支付状态,若未支付则删除该订单

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值