面试高频:为什么不用定时任务实现关闭订单?

0f0cba11809b23f42991233f60e26170.jpeg

在电商、支付等领域,往往会有这样的场景,用户下单后放弃支付了,那这笔订单会在指定的时间段后进行关闭操作,细心的你一定发现了像某宝、某东都有这样的逻辑,而且时间很准确,误差在1s内;那他们是怎么实现的呢?

一般的做法有如下几种:

  • 定时任务关闭订单
  • rocketmq延迟队列
  • rabbitmq死信队列
  • 时间轮算法
  • redis过期监听

一、定时任务关闭订单(最low)

一般情况下,最不推荐的方式就是关单方式就是定时任务方式,原因我们可以看下面的图来说明。

371f0873a0b3645bd314c307a6ce6c88.jpeg

我们假设,关单时间为下单后10分钟,定时任务间隔也是10分钟;通过上图我们看出,如果在第1分钟下单,在第20分钟的时候才能被扫描到执行关单操作,这样误差达到10分钟,这在很多场景下是不可接受的,另外需要频繁扫描主订单号造成网络IO和磁盘IO的消耗,对实时交易造成一定的冲击,所以PASS。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

二、rocketmq延迟队列方式

延迟消息生产者把消息发送到消息服务器后,并不希望被立即消费,而是等待指定时间后才可以被消费者消费,这类消息通常被称为延迟消息。在RocketMQ开源版本中,支持延迟消息,但是不支持任意时间精度的延迟消息,只支持特定级别的延迟消息。消息延迟级别分别为1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h ,共18个级别。

发送延迟消息(生产者)

/**
 * 推送延迟消息
 * @param topic 
 * @param body 
 * @param producerGroup 
 * @return boolean
 */

public boolean sendMessage(String topic, String body, String producerGroup) {
     try {
        Message recordMsg =  new Message(topic, body.getBytes());
        producer.setProducerGroup(producerGroup);

         //设置消息延迟级别,我这里设置14,对应就是延时10分钟
         // "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h"
        recordMsg.setDelayTimeLevel( 14);
         // 发送消息到一个Broker
        SendResult sendResult = producer.send(recordMsg);
         // 通过sendResult返回消息是否成功送达
        log.info( "发送延迟消息结果:======sendResult:{}", sendResult);
        DateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
        log.info( "发送时间:{}", format.format( new Date()));

         return  true;
    }  catch (Exception e) {
        e.printStackTrace();
        log.error( "延迟消息队列推送消息异常:{},推送内容:{}", e.getMessage(), body);
    }
     return  false;
}

消费延迟消息(消费者)

/**
 * 接收延迟消息
 * 
 * @param topic
 * @param consumerGroup
 * @param messageHandler
 */

public void messageListener(String topic, String consumerGroup, MessageListenerConcurrently messageHandler) {
    ThreadPoolUtil.execute(() -> {
         try {
            DefaultMQPushConsumer consumer =  new DefaultMQPushConsumer();
            consumer.setConsumerGroup(consumerGroup);
            consumer.setVipChannelEnabled( false);
            consumer.setNamesrvAddr(address);
             //设置消费者拉取消息的策略,*表示消费该topic下的所有消息,也可以指定tag进行消息过滤
            consumer.subscribe(topic,  "*");
             //消费者端启动消息监听,一旦生产者发送消息被监听到,就打印消息,和rabbitmq中的handlerDelivery类似
    &am
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值