RabbitMQ之TTL机制
创始人
2024-03-06 00:59:18

        在京东下单,订单创建成功,等待支付,一般会给30分钟的时间,开始倒计时。如果在这段时间内用户没有支付,则默认订单取消。 

该如何实现?

  • 定期轮询(数据库等)
    • 用户下单成功,将订单信息放入数据库,同时将支付状态放入数据库,用户付款更改数据库状态。定期轮询数据库支付状态,如果超过30分钟就将该订单取消。
    • 优点:设计实现简单
    • 缺点:需要对数据库进行大量的IO操作,效率低下。
  • Timer
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");Timer timer = new Timer();TimerTask timerTask = new TimerTask() {@Overridepublic void run() {System.out.println("用户没有付款,交易取消:" + simpleDateFormat.format(new Date(System.currentTimeMillis())));timer.cancel();}};System.out.println("等待用户付款:" + simpleDateFormat.format(new Date(System.currentTimeMillis())));// 10秒后执行timerTasktimer.schedule(timerTask, 10 * 1000);
    • 缺点
      • Timers没有持久化机制
      • Timers不灵活 (只可以设置开始时间和重复间隔,对等待支付貌似够用)
      • Timers 不能利用线程池,一个timer一个线程
      • Timers没有真正的管理计划
  • ScheduledExecutorService
            SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");// 线程工厂ThreadFactory factory = Executors.defaultThreadFactory();// 使用线程池ScheduledExecutorService service = new ScheduledThreadPoolExecutor(10, factory);System.out.println("开始等待用户付款10秒:" + format.format(new Date()));service.schedule(new Runnable() {@Overridepublic void run() {System.out.println("用户未付款,交易取消:" + format.format(new Date()));}// 等待10s 单位秒}, 10, TimeUnit.SECONDS);
    • 优点:可以多线程执行,一定程度上避免任务间互相影响,单个任务异常不影响其它任务。
    • 在高并发的情况下,不建议使用定时任务去做,因为太浪费服务器性能,不建议。
  • RabbitMQ:使用TTL
  • Quartz
  • Redis Zset
  • JCronTab
  • SchedulerX
  • 。。。

TTL,Time to Live 的简称,即过期时间。

RabbitMQ 可以对消息和队列两个维度来设置TTL。

   任何消息中间件的容量和堆积能力都是有限的,如果有一些消息总是不被消费掉,那么需要有一种过期的机制来做兜底。

目前有两种方法可以设置消息的TTL。

  1. 通过Queue属性设置,队列中所有消息都有相同的过期时间。
  2. 对消息自身进行单独设置,每条消息的TTL 可以不同。

        如果两种方法一起使用,则消息的TTL 以两者之间较小数值为准。通常来讲,消息在队列中的生存时间一旦超过设置的TTL 值时,就会变成“死信”(Dead Message),消费者默认就无法再收到该消息。当然,“死信”也是可以被取出来消费的

1、原生API案例

package com.lagou.rabbitmq.demo;import com.rabbitmq.client.*;import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;public class Producer {public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setUri("amqp://root:123456@192.168.80.121:5672/%2f");Connection connection = factory.newConnection();Channel channel = connection.createChannel();Map arguments = new HashMap<>();// 消息队列中消息的过期时间arguments.put("x-message-ttl", 10 * 1000);// 如果消息队列中没有消费者,则10s后消息过期,队列将会被自动删除arguments.put("x-expires", 60 * 1000);channel.queueDeclare("queue.ttl.waiting",true,false,false,arguments);channel.exchangeDeclare("ex.ttl.waiting",BuiltinExchangeType.DIRECT,true,false,false,null);channel.queueBind("queue.ttl.waiting", "ex.ttl.waiting", "key.ttl.waiting");AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder().contentEncoding("utf-8").deliveryMode(2) // 设置消息持久化,2代表设置消息持久化.build();channel.basicPublish("ex.ttl.waiting", "key.ttl.waiting", properties, "等待的订单号".getBytes(StandardCharsets.UTF_8));channel.close();connection.close();}
}

此外,还可以通过命令行方式设置全局TTL,执行如下命令:

rabbitmqctl set_policy TTL ".*" '{"message-ttl":30000}' --apply-to queues

默认规则:

  • 如果不设置TTL,则表示此消息不会过期;
  • 如果TTL设置为0,则表示除非此时可以直接将消息投递到消费者,否则该消息会被立即丢弃;

        注意理解message-ttlx-expires 这两个参数的区别,有不同的含义。但是这两个参数属性都遵循上面的默认规则。一般TTL相关的参数单位都是毫秒(ms)

2、springboot案例

(1)pom.xml添加依赖

org.springframework.bootspring-boot-starter-amqporg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-engineorg.springframework.amqpspring-rabbit-testtest

(2)application.properties添加rabbitmq连接信息

spring.application.name=ttl
spring.rabbitmq.host=node1
spring.rabbitmq.virtual-host=/
spring.rabbitmq.username=root
spring.rabbitmq.password=123456
spring.rabbitmq.port=5672

(3)主入口类

package com.lagou.rabbitmq.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RabbitmqDemo {public static void main(String[] args) {SpringApplication.run(RabbitmqDemo07.class, args);}
}

(4)RabbitConfig类

package com.lagou.rabbitmq.demo.config;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
public class RabbitConfig {@Beanpublic Queue queueTTLWaiting() {Map props = new HashMap<>();// 对于该队列中的消息,设置都等待10sprops.put("x-message-ttl", 10000);Queue queue = new Queue("q.pay.ttl-waiting", false, false, false, props);return queue;}@Beanpublic Queue queueWaiting() {Queue queue = new Queue("q.pay.waiting", false, false, false);return queue;}@Beanpublic Exchange exchangeTTLWaiting() {DirectExchange exchange = new DirectExchange("ex.pay.ttlwaiting", false, false);return exchange;}/*** 该交换器使用的时候,需要给每个消息设置有效期** @return*/@Beanpublic Exchange exchangeWaiting() {DirectExchange exchange = new DirectExchange("ex.pay.waiting", false, false);return exchange;}@Beanpublic Binding bindingTTLWaiting() {return BindingBuilder.bind(queueTTLWaiting()).to(exchangeTTLWaiting()).with("pay.ttl - waiting").noargs();}@Beanpublic Binding bindingWaiting() {return BindingBuilder.bind(queueWaiting()).to(exchangeWaiting()).with("pay.waiting").noargs();}
}

(5)PayController类

package com.lagou.rabbitmq.demo.controller;import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;@RestController
public class PayController {@Autowiredprivate AmqpTemplate rabbitTemplate;@RequestMapping("/pay/queuettl")public String sendMessage() {rabbitTemplate.convertAndSend("ex.pay.ttl-waiting", "pay.ttlwaiting", "发送了TTL-WAITING-MESSAGE");return "queue-ttl-ok";}@RequestMapping("/pay/msgttl")public String sendTTLMessage() throws UnsupportedEncodingException {MessageProperties properties = new MessageProperties();properties.setExpiration("5000");Message message = new Message("发送了WAITINGMESSAGE".getBytes("utf-8"), properties);rabbitTemplate.convertAndSend("ex.pay.waiting", "pay.waiting", message);return "msg-ttl-ok";}
}

相关内容

热门资讯

猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...