前言
之前有篇文章说的是:下单但未支付的订单怎么做延迟取消,具体文章链接如下:
【笔记】下单但未支付的订单倒计时自动取消逻辑实现_阿小冰的博客-优快云博客_订单倒计时实现方案【笔记】下单但未支付的订单倒计时自动取消逻辑实现https://blog.youkuaiyun.com/qq_38377525/article/details/124385040这篇文章主要讲了集中常见方案,其中最后一个方案是利用MQ做延迟,只是在上面的文章中没有过多解释,只是提供了思路,那我们这篇文章主要讲一下实现过程
介绍
看图,这是在京东上下的一笔订单,只是订单创建成功了,但未支付,一般来说都是半小时倒计时,京东这里是两个小时,如果两个小时还是没有支付,那这笔订单就会自动取消
RabbitMQ的TTL机制
介绍
TTL,全称Time to Live,翻译过来就是过期时间
思路
RabbitMQ可以对消息和队列这两个角度来设置TTL
- 通过Queue属性来设置,队列中所有消息都有相同的过期时间
- 对消息自身进行单独设置,每条消息的TTL可以不相同
疑问 1
这两个方案能不能一块使用
解答1
可以,的那是如果这两种方法一起使用的话,那TTL就会取这两个方法中较小数值为主要的过期时间
疑问2
为什么要以最小数值为准?
解答2
一般来说,消息在队列中的存在时间只要超过设置的TTL时,就会变成死信。也就是消费者默认就无法在获取到该信息
关于死信相关知识,请移驾到下面的链接~【RabbitMQ】死信队列_阿小冰的博客-优快云博客【RabbitMQ】死信队列
https://blog.youkuaiyun.com/qq_38377525/article/details/124987975
设置规则
- 如果不设置TTL,则表示消息不会过期
- 如果TTL设置为0的话,就表示除非此时可以直接将消息投递到消费者进行消费,否则该消息会被立即丢弃
TTL相关的参数单位都是毫秒ms
SpringBoot模拟TTL场景
1、添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
2、添加配置
spring.application.name=springboot_rabbitmq spring.rabbitmq.host=127.0.0.1 spring.rabbitmq.virtual-host=/ spring.rabbitmq.username=guest spring.rabbitmq.password=guest spring.rabbitmq.port=5672
3、添加mq配置类
@Configuration public class RabbitConfig { /** * 定义TTL,对于该队列中的消息,设置都等待10s * * @return */ @Bean public Queue queueTTLWaiting() { Map<String, Object> props = new HashMap<>(); props.put("x-message-ttl", 10000); return new Queue("queue.pay.ttl-waiting", false, false, false, props); } /** * 普通队列,没有过期设置 * @return */ @Bean public Queue queueWaiting() { return new Queue("queue.pay.waiting", false, false, false); } /** * 使用该交换器,进来的消息都有相同的过期时间 * @return */ @Bean public Exchange exchangeTTLWaiting() { return new DirectExchange("exchange.pay.ttl-waiting", false, false); } /** * 使用该交换器,需要对每个消息设置过期时间 * @return */ @Bean public Exchange exchangeWaiting() { return new DirectExchange("exchange.pay.waiting", false, false); } /** * 绑定TTL队列相关配置 * @return */ @Bean public Binding bindingTTLWaiting() { return BindingBuilder.bind(queueTTLWaiting()).to(exchangeTTLWaiting()).with("pay.ttl-waiting").noargs(); } /** * 正常绑定 * @return */ @Bean public Binding bindingWaiting(){ return BindingBuilder.bind(queueWaiting()).to(exchangeWaiting()).with("pay.waiting").noargs(); } }
4、编写接口
@RestController public class PayController { @Autowired private AmqpTemplate amqpTemplate; /** * 发送一个队列统一过期时间的消息 * * @return */ @RequestMapping("/pay/queue-ttl") public String sendMsg() { amqpTemplate.convertAndSend("exchange.pay.ttl-waiting", "pay.ttl-waiting", "发送一个TTL的通道消息!"); return "OK"; } /** * 对每个消息设置过期时间 * * @return * @throws UnsupportedEncodingException */ @RequestMapping("/pay/msg-ttl") public String sendTTLMsg() throws UnsupportedEncodingException { MessageProperties properties = new MessageProperties(); properties.setExpiration("5000"); Message message = new Message("发送了一个有过期日期的msg".getBytes("utf-8"), properties); amqpTemplate.convertAndSend("exchange.pay.waiting", "pay.waiting", message); return "msg-ttl-ok"; } }
5、测试接口
①/pay/queue-ttl:效果是只要走这个交换器的消息都会设置10s的过期日期
我们请求一下接口,查看一下对应交换器的指标,通过指标图可以看出来
我们设置的是10s的过期时间,所以过了10s之后,这个数据就会清零
同时在这一列,我们也不难看出,当前观察的这一列是一个TTL队列
②/pay/msg-ttl:效果是看到一个5s的过期时间的消息
我们请求一下接口,会发现表格中出现了数值,这个是消息数量
我们设置了大概5s的过期时间,5s之后就清空了