springboot整合rabbitmq实现延时队列之TTL方式

什么是TTL?
先简单介绍下rabbitmq执行的流程,它和我之前写到的spring boot整合activeMQ不太一样,除了队列(queue)之外还引入了交换机(exchange)的概念。
rabbitmq的交换机有4种模式,我不详细介绍,简单说下大体执行流程:
在这里插入图片描述
①:生产者将消息(msg)和路由键(routekey)发送指定的交换机(exchange)上
②:交换机(exchange)根据路由键(routekey)找到绑定自己的队列(queue)并把消息给它
③:队列(queue)再把消息发送给监听它的消费者(customer)
那么延时队列TTL又是什么呢?这里引入了一个死信(死亡信息)的概念,有死信必定有死亡时间,也就是我们希望延时多久的时间:
在这里插入图片描述
①:生产者将消息(msg)和路由键(routekey)发送指定的死信交换机(delayexchange)上
②:死信交换机(delayexchange)根据路由键(routekey1)找到绑定自己的死信队列(delayqueue)并把消息给它
③:消息(msg)到期死亡变成死信转发给死信接收交换机(receiveexchange)
④:死信接收交换机(receiveexchange)根据路由键(routekey2)找到绑定自己的死信接收队列(receivequeue)并把消息给它
⑤:死信接收队列(receivequeue)再把消息发送给监听它的消费者(customer)
ps:延时队列也叫死信队列。基于TTL模式的延时队列会涉及到2个交换机、2个路由键、2个队列…emmmmm比较麻烦

延时队列的使用场景:
1.订单业务:在电商中,用户下单后30分钟后未付款则取消订单。
2.短信通知:用户下单并付款后,1分钟后发短信给用户。
步骤:
1、在 pom.xml 中添加 spring-boot-starter-amqp的依赖

<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.在 application.yml文件中配置rabbitmq相关内容

spring:

  rabbitmq:

    host: localhost

    port: 5672

    username: guest

    password: guest

3、具体编码实现
1.配置队列

package com.zb.comfig;

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;
/**
* @author 王淑婷
* @Description TODO
* @Date 2020/5/3
* @Version V1.0
*/
@Configuration
@Slf4j
public class DelayRabbitConfig {
    /**
     * 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";

    /**
     * 延迟队列 TTL 名称
     */
    public static final String ORDER_DELAY_QUEUE = "user.order.delay.queue";
    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<String, Object >();
        // 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);
    }
    /**
     *给死信队列绑定交换机
     * @return
     */
    @Bean
    public Binding dlxBinding() {
        return BindingBuilder.bind(delayOrderQueue()).to(orderDelayExchange()).with(ORDER_DELAY_ROUTING_KEY);
    }
    /**
     * 死信接收队列
     * @return
     */
    @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);
    }
    /**
     * 死信交换机绑定消费队列
     * @return
     */
    @Bean
    public Binding orderBinding() {
        // TODO 如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
        return BindingBuilder.bind(orderQueue()).to(orderTopicExchange()).with(ORDER_ROUTING_KEY);
    }
}

2.创建一个Order实体类
在这里插入图片描述
3.发送者

package com.zb.service;


import com.zb.comfig.DelayRabbitConfig;
import com.zb.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;


/**
* @author 王淑婷
* @Description TODO
* @Date 2020/5/3
* @Version V1.0
*/
@Service
@Slf4j
public class OrderService {
    @Autowired
    private AmqpTemplate amqpTemplate;


    public void sendDelay(Order order){
        log.info("【订单生成的时间】:"+new Date().toString()+"【1分钟后检查订单是否已经支付】" +order.toString());
        amqpTemplate.convertAndSend(DelayRabbitConfig.ORDER_DELAY_EXCHANGE, DelayRabbitConfig.ORDER_DELAY_ROUTING_KEY, order, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration(1*1000*60+"");
                return message;
            }
        });
    }
}

4、 接收者

package com.zb.mq;


import com.rabbitmq.client.Channel;
import com.zb.comfig.DelayRabbitConfig;
import com.zb.entity.Order;
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;


/**
* @author 王淑婷
* @Description TODO
* @Date 2020/5/3
* @Version V1.0
*/
@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.getState() == 0) {
            System.out.println("状态为 0 ");
        } else if (order.getState() == 1) {
            System.out.println("状态为 1 ");
        } else if (order.getState() == 2) {
            System.out.println("状态为 2 ");
        }
    }
}

5.测试,访问http://localhost:8080/sendDelay,查看日志输出

package com.zb.controller;


import com.zb.entity.Order;
import com.zb.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


/**
* @author 王淑婷
* @Description TODO
* @Date 2020/5/3
* @Version V1.0
*/
@RestController
public class TestController {
    @Autowired
    private OrderService orderService;


    @GetMapping("/sendDelay")
    public String sendDelay(){
        Order order = new Order();
        order.setOid(1);
        order.setState(2);
        order.setPrice(50);
        order.setTitle("一条测试订单");


        Order order2 = new Order();
        order2.setOid(2);
        order2.setState(1);
        order2.setPrice(40);
        order2.setTitle("yi条测试订单too");


        orderService.sendDelay(order);
        orderService.sendDelay(order2);
        return "ok";
    }

}

6.日志输出
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值