当前位置: 首页 > article >正文

虚拟电商-延迟任务的设计实现

本文章介绍:什么是延迟任务,延迟任务的设计实现方案

一、什么是延迟任务

什么是延迟任务?

12306下单等待支付业务:
在这里插入图片描述

定时任务与延迟任务的区别:

定时任务往往是固定周期的,有明确的触发时间。而延迟任务一般没有固定的开始时间,它常常是由一个事件触发的,而在这个事件触发之后的一段时间内触发另一个事件,任务可以立即执行,也可以延迟,任务之间也可以建立一定联系;

场景一: 订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单;如果期间下单成功,任务取消

场景二:接口对接出现网络问题,1分钟后重试,如果失败,2分钟重试,直到出现阈值终止
在这里插入图片描述

延迟任务的实现方案:

1:单机版方案:基于jdk中的定时器

2:消息中间件方案

3:自定义分布式延迟任务方案

方案没有好坏之分,和系统架构一样,关键是适合自身业务系统,不过目前IT行业发展阶段,有些市面上开源中间件已经不能满足大厂自身业务,大厂基本都是从新定制开发。

二、延迟任务系统设计

2.1.延迟任务需求分析

延迟任务的概念我们已经阐述,在充吧项目中延迟任务的业务场景有:

场景一: 订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单;如果期间下单成功,任务取消

场景二:接口对接出现网络问题,1分钟后重试,如果失败,2分钟重试,直到出现阈值终止

总结下来:简单的理解延迟任务可以认为是在某个设定的时间之后做某一件事情。

2.2.延迟任务单机版实现方案-Timer定时器

jdk中提供了一个定时器类Timer,它能做到在指定的时间点去执行某一个任务,也能做到定时的周期性的执行某一个任务

1:在src/test/java下创建测试包:com.chongba.schedule.jdk,在测试包下创建测试类TimerTaskTest

public class TimerTaskTest {public static void main(String[] args) {
        Timer timer = new Timer();
        // 1秒之后 执行任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()/1000+"执行了任务");
            }
        },1000L);
        System.out.println(System.currentTimeMillis()/1000);
        
        //执行时间 <=当前时间 则任务立马执行
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()/1000+"执行了任务");
            }
        },new Date(System.currentTimeMillis()-1000L));
        System.out.println(System.currentTimeMillis()/1000);
        
        // 延迟1秒之后执行任务,然后每隔2秒执行任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()/1000+"执行了任务");
            }
        },1000L,2000L);
        System.out.println(System.currentTimeMillis()/1000);
        
        // 执行时间 <=当前时间 则任务立马执行 然后每隔2秒执行任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()/1000+"执行了任务");
            }
        },new Date(System.currentTimeMillis()- 1000L),2000L);
        System.out.println(System.currentTimeMillis()/1000);
        
        
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()/1000+"执行了任务");
            }
        },3000L,1000L);
        System.out.println(System.currentTimeMillis()/1000);
        
        //执行时间 <=当前时间 任务立马执行 并会计算过期该执行的次数并且执行,最后每隔1秒执行一次
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()/1000+"执行了任务");
            }
        },new Date(System.currentTimeMillis() - 3000L),1000L);
        System.out.println(System.currentTimeMillis()/1000);
    }
}class TWO{
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    System.out.println(System.currentTimeMillis()/1000+"执行了任务");
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },0L,2000L);
​
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    System.out.println(System.currentTimeMillis()/1000+"执行了任务");
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },0,2000L);
    }
}

1.schedule() ,2个参数方法: 在执行任务时,如果指定的计划执行时间scheduledExecutionTime <= systemCurrentTime,则task会被立即执行。

  1. schedule() ,3个参数方法: 在执行任务时,如果指定的计划执行时间scheduledExecutionTime <= systemCurrentTime,则task会被立即执行,之后按period参数固定重复执行。

  2. scheduleAtFixedRate() ,3个参数方法: 在执行任务时,如果指定的计划执行时间scheduledExecutionTime<= systemCurrentTime,则task会首先按执行一次;然后按照执行时间、系统当前时间和period参数计算出过期该执行的次数,计算按照: (systemCurrentTime-scheduledExecutionTime)/period,再次执行计算出的次数;最后按period参数固定重复执行。

  3. schedule() 和scheduleAtFixedRate() schedule()方法更注重保持间隔时间的稳定。 scheduleAtFixedRate()方法更注重保持执行频率的稳定。

注意,阿里推荐的编码规约中这样写道
Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行

Timer 优化方案
使用ScheduledExecutorService则没有这个问题。

测试:

1:在测试包jdk下创建测试类TimerTaskTest2

public class TimerTaskTest2 {
​
​
    public static void main(String[] args) {Timer timer = new Timer();
        
        for(int i=0;i<100;i++){
            int finalI = i;
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println(finalI);
                    if(finalI == 20){
                        throw new RuntimeException();
                    }
                }
            },1000L);
        }ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        for(int i=0;i<100;i++){int finalI = i;
            scheduledExecutorService.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println(finalI);
                    if( finalI ==20 ){
                        throw new RuntimeException();
                    }
                }
            },1, TimeUnit.SECONDS);
        }
    }
}

另外对于ScheduledExecutorService还有其他的API

scheduledExecutorService.scheduleAtFixedRate()  // 按照固定频率
scheduledExecutorService.scheduleWithFixedDelay()// 如果任务时间超过固定频率,按照任务实际时间延后

2.3.延迟任务单机版实现方案-DelayQueue

DelayQueue 是一个支持延时获取元素的阻塞队列, 内部采用优先队列 PriorityQueue 存储元素,同时元素必须实现 Delayed 接口;在创建元素时可以指定多久才可以从队列中获取当前元素,只有在延迟期满时才能从队列中提取元素。

在这里插入图片描述

DelayQueue属于排序队列,它的特殊之处在于队列的元素必须实现Delayed接口,该接口需要实现compareTo和getDelay方法

getDelay方法:获取元素在队列中的剩余时间,只有当剩余时间为0时元素才可以出队列。

compareTo方法:用于排序,确定元素出队列的顺序。

实现:

1:在测试包jdk下创建延迟任务元素对象DelayedTask,实现compareTo和getDelay方法,

2:在main方法中创建DelayQueue并向延迟队列中添加三个延迟任务,

3:循环的从延迟队列中拉取任务

public class DelayedTask  implements Delayed{
    
    // 任务的执行时间
    private int executeTime = 0;
    
    public DelayedTask(int delay){
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND,delay);
        this.executeTime = (int)(calendar.getTimeInMillis() /1000 );
    }/**
     * 元素在队列中的剩余时间
     * @param unit
     * @return
     */
    @Override
    public long getDelay(TimeUnit unit) {
        Calendar calendar = Calendar.getInstance();
        return executeTime - (calendar.getTimeInMillis()/1000);
    }/**
     * 元素排序
     * @param o
     * @return
     */
    @Override
    public int compareTo(Delayed o) {
        long val = this.getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        return val == 0 ? 0 : ( val < 0 ? -1: 1 );
    }
​
​
    public static void main(String[] args) {
        DelayQueue<DelayedTask> queue = new DelayQueue<DelayedTask>();
        
        queue.add(new DelayedTask(5));
        queue.add(new DelayedTask(10));
        queue.add(new DelayedTask(15));System.out.println(System.currentTimeMillis()/1000+" start consume ");
        while(queue.size() != 0){
            DelayedTask delayedTask = queue.poll();
            if(delayedTask !=null ){
                System.out.println(System.currentTimeMillis()/1000+" cosume task");
            }
            //每隔一秒消费一次
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }     
    }
}

http://www.kler.cn/a/588203.html

相关文章:

  • 同一子网通信
  • 关于stac和clac的进一步细节及EFLAGS
  • 平方矩阵问题
  • 热key探测技术架构设计与实践
  • 【C/C++】最长回文子串(leetcode T5)
  • docker引擎与docker-compose离线版本下载详细教程
  • 深入TA-Lib:量化技术指标详解
  • 用 Pinia 点燃 Vue 3 应用:状态管理革新之旅
  • Leetcode-132.Palindrome Partitioning II [C++][Java]
  • C++复试笔记(五)
  • Adobe Premiere Pro2023配置要求
  • 矩阵幂(矩阵k次幂)
  • nginx配置反向代理数据库等插件的原理和方式
  • Matlab 基于磁流变阻尼器的半主动车辆座椅悬架模糊控制研究
  • SUSHI交易所:安全生态赋能Meme热潮
  • 豆包大模型-语音实时通话-青青-服务器ECS踩坑过程
  • JavaScript内置对象
  • C++和标准库速成(四)——逻辑比较运算符、三向比较运算符、函数和属性
  • C++初阶——类和对象(二)
  • C语言之文件