每日一博 - 延时任务的多种实现方式解读

Posted 小小工匠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一博 - 延时任务的多种实现方式解读相关的知识,希望对你有一定的参考价值。


Pre

每日一博 - 使用环形队列实现高效的延时消息


延时任务 VS 定时任务

举个例子,开发中常见的延时任务场景:

  • 半小时未支付,取消订单

延时任务和定时任务的几个小区别,梳理下:

  • 定时任务有明确的触发时间,延时任务没有
  • 定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期
  • 定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务

Solutions

DB 轮询

核心思想

通过定时任务扫描,执行业务逻辑。


Demo Code

参考实现如下:

	 <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.2.2</version>
    </dependency>
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
 
public class MyJob implements Job {
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        System.out.println("模拟扫描任务。。。。。");
    }
 
    public static void main(String[] args) throws Exception {
        // 创建任务
        JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
                .withIdentity("job1", "group1").build();
        // 创建触发器 每3秒钟执行一次
        Trigger trigger = TriggerBuilder
                .newTrigger()
                .withIdentity("trigger1", "group3")
                .withSchedule(
                        SimpleScheduleBuilder.simpleSchedule()
                                .withIntervalInSeconds(3).repeatForever())
                .build();
        Scheduler scheduler = new StdSchedulerFactory().getScheduler();
        // 将任务及其触发器放入调度器
        scheduler.scheduleJob(jobDetail, trigger);
        // 调度器开始调度任务
        scheduler.start();
    }
}

优缺点

优点: 简单 (好像也没有其他的优点了 哈哈哈 )

缺点:

  • (1)占用资源,对服务器内存消耗大

  • (2)存在延迟,比如你每隔n分钟扫描一次,那最坏的延迟时间就是n分钟

  • (3)如果表的数据量较大,每隔几分钟这样扫描一次,性能堪忧,DB压力较大


JDK的Delay Queue

核心思想

利用JDK自带的DelayQueue来实现, 无界阻塞队列,该队列只有在延迟期满的时候才能从中获取元素,放入DelayQueue中的对象,必须实现Delayed接口。

  • poll():获取并移除队列的超时元素,没有则返回空
  • take():获取并移除队列的超时元素,如果没有则wait当前线程,直到有元素满足超时条件,返回结果。

Demo Code

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
 
public class TicketDelay implements Delayed {
    
    private String ticketId;
    private long timeout;
 
    OrderDelay(String ticketId, long timeout) {
        this.ticketId= ticketId;
        this.timeout = timeout + System.nanoTime();
    }
 
    public int compareTo(Delayed other) {
        if (other == this)
            return 0;
        OrderDelay t = (OrderDelay) other;
        long d = (getDelay(TimeUnit.NANOSECONDS) - t
                .getDelay(TimeUnit.NANOSECONDS));
        return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
    }
 
    // 返回距离你自定义的超时时间还有多少
    public long getDelay(TimeUnit unit) {
        return unit.convert(timeout - System.nanoTime(),TimeUnit.NANOSECONDS);
    }
 
    void print() {
        System.out.println(orderId+"编号的订单要删除啦。。。。");
    }
}

优缺点


时间轮算法

每日一博 - 使用环形队列实现高效的延时消息

核心思想

Demo Code

优缺点


核心思想

Demo Code

优缺点

以上是关于每日一博 - 延时任务的多种实现方式解读的主要内容,如果未能解决你的问题,请参考以下文章

每日一博 - Review线程池

每日一博 - 使用环形队列实现高效的延时消息

每日一博 - pom文件灰化的处理方式

每日一博 - pom文件灰化的处理方式

每日一博 - 常用负载均衡算法实现

每日一博 - 常用负载均衡算法实现