首页 > 其他分享 >Quartz任务调度框架

Quartz任务调度框架

时间:2023-05-31 22:23:28浏览次数:37  
标签:Quartz 框架 31 component id 线程 org 任务调度 example

Quartz任务调度框架

Quartz是一个任务调度框架,用于定时执行任务。

任务调度:系统中有N的任务,分别需要在不同的时刻执行这些任务,这种多任务的执行策略就是任务调度

0 定时任务实现的方法

  1. spring schedule
    • 优点:无需整合spring,作业类中就可以调用业务service
    • 缺点:默认单线程执行任务,当前一个任务执行时间过长时,会使后面的任务延期执行,如果前一个任务的执行时间过长,可能会导致后面的任务执行时间不准确,最差的情况可能会丢任务;不能做数据存储型的定时任务;如果希望并发运行,需要配置线程池
  2. spring-quartz
    • 优点:支持持久化
    • 缺点:配置稍显复杂

定时任务持久化:当前定时任务保存在内存中,每当项目重启的时候,定时任务就会清空并重写加载,原来的一些执行信息就丢失了,我们需要在保存下定时任务的执行信息,下次重启项目的时候我们的定时任务就可以根据上次执行的状态再执行下一次的定时任务。

1.Spring schedule测试

使用默认的单线程

启动类 App.java

package org.example;


import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@Slf4j
@SpringBootApplication
@EnableScheduling  // 启动 spring task
public class App
{
    public static void main ( String[] args )  {
        SpringApplication.run(App.class,args);
    }
}

TaskComponent.java

package org.example.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class TaskComponent {
    
    // 定时任务1 每1秒执行一次,执行时间为10秒
    @Scheduled(cron = "*/1 * * * * ?")
    public void task1() {
        log.info("task1-------当先线程的名字={}, id={}", Thread.currentThread().getName(),Thread.currentThread().getId());
        try {
            Thread.sleep(1000 * 10);
            // int s = 2/0; 				// 模拟任务执行中抛出异常
        } catch (InterruptedException e) {
            log.error("Thread sleep error {}", e.getMessage());
            Thread.currentThread().interrupt();
        }
    }
    
    // 定时任务2 每2秒执行一次
    @Scheduled(cron = "*/2 * * * * ?")
    public void task2() {
        log.info("task2-------当先线程的名字={}, id={}", Thread.currentThread().getName(),Thread.currentThread().getId());
    }
}

运行启动类,观察输出

2023-05-31 20:57:44.009  INFO 44860 --- [   scheduling-1] org.example.component.TaskComponent      : task2-------当先线程的名字=scheduling-1, id=36
2023-05-31 20:57:44.009  INFO 44860 --- [   scheduling-1] org.example.component.TaskComponent      : task1-------当先线程的名字=scheduling-1, id=36
2023-05-31 20:57:54.024  INFO 44860 --- [   scheduling-1] org.example.component.TaskComponent      : task2-------当先线程的名字=scheduling-1, id=36
2023-05-31 20:57:55.016  INFO 44860 --- [   scheduling-1] org.example.component.TaskComponent      : task1-------当先线程的名字=scheduling-1, id=36
2023-05-31 20:58:05.027  INFO 44860 --- [   scheduling-1] org.example.component.TaskComponent      : task2-------当先线程的名字=scheduling-1, id=36
2023-05-31 20:58:06.002  INFO 44860 --- [   scheduling-1] org.example.component.TaskComponent      : task1-------当先线程的名字=scheduling-1, id=36

task2的执行时间为44秒,54秒,05秒

可以看到task2由于task1任务的阻塞,导致task2不能按照预想的每2秒执行一次。

用的时候注意不要将定时任务的时间设置的过于接近。

使用多线程

需要做的事情:

  1. 启动类上加上 @EnableAsync
  2. task 上加上 @Async

运行启动类,观察输出

2023-05-31 21:10:37.025  INFO 28216 --- [         task-1] org.example.component.TaskComponent      : task1-------当先线程的名字=task-1, id=39
2023-05-31 21:10:38.007  INFO 28216 --- [         task-2] org.example.component.TaskComponent      : task1-------当先线程的名字=task-2, id=42
2023-05-31 21:10:38.008  INFO 28216 --- [         task-3] org.example.component.TaskComponent      : task2-------当先线程的名字=task-3, id=43
2023-05-31 21:10:39.005  INFO 28216 --- [         task-4] org.example.component.TaskComponent      : task1-------当先线程的名字=task-4, id=46
2023-05-31 21:10:40.002  INFO 28216 --- [         task-5] org.example.component.TaskComponent      : task2-------当先线程的名字=task-5, id=48
2023-05-31 21:10:40.002  INFO 28216 --- [         task-6] org.example.component.TaskComponent      : task1-------当先线程的名字=task-6, id=49
2023-05-31 21:10:41.014  INFO 28216 --- [         task-7] org.example.component.TaskComponent      : task1-------当先线程的名字=task-7, id=52
2023-05-31 21:10:42.014  INFO 28216 --- [         task-3] org.example.component.TaskComponent      : task1-------当先线程的名字=task-3, id=43
2023-05-31 21:10:42.014  INFO 28216 --- [         task-8] org.example.component.TaskComponent      : task2-------当先线程的名字=task-8, id=55
2023-05-31 21:10:43.015  INFO 28216 --- [         task-5] org.example.component.TaskComponent      : task1-------当先线程的名字=task-5, id=48
2023-05-31 21:10:44.009  INFO 28216 --- [         task-8] org.example.component.TaskComponent      : task2-------当先线程的名字=task-8, id=55

可以看到两个任务的执行时间互不影响,并且线程id也不相同,说明是多线程执行任务。

但是,这种方式,会引发另外的问题,task1会多次执行。我们希望上一个task1未执行完成的时候,下一个task1不执行。

使用线程池

task类注释掉 @Async,并且配置如下的配置类

package org.example.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

@Configuration
@Slf4j
public class TaskConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 为springtask配置线程池
        taskRegistrar.setScheduler(taskExecutor());
    }
    // 创建线程池
    @Bean
    public Executor taskExecutor(){
        return Executors.newScheduledThreadPool(10);
    }
}

2023-05-31 21:24:18.007  INFO 29676 --- [pool-1-thread-1] org.example.component.TaskComponent      : task2-------当先线程的名字=pool-1-thread-1, id=36
2023-05-31 21:24:18.007  INFO 29676 --- [pool-1-thread-1] org.example.component.TaskComponent      : task2 执行完了,线程的名字=pool-1-thread-1, id=36
2023-05-31 21:24:18.008  INFO 29676 --- [pool-1-thread-2] org.example.component.TaskComponent      : task1-------当先线程的名字=pool-1-thread-2, id=37
2023-05-31 21:24:20.002  INFO 29676 --- [pool-1-thread-1] org.example.component.TaskComponent      : task2-------当先线程的名字=pool-1-thread-1, id=36
2023-05-31 21:24:20.002  INFO 29676 --- [pool-1-thread-1] org.example.component.TaskComponent      : task2 执行完了,线程的名字=pool-1-thread-1, id=36
2023-05-31 21:24:22.009  INFO 29676 --- [pool-1-thread-3] org.example.component.TaskComponent      : task2-------当先线程的名字=pool-1-thread-3, id=39
2023-05-31 21:24:22.009  INFO 29676 --- [pool-1-thread-3] org.example.component.TaskComponent      : task2 执行完了,线程的名字=pool-1-thread-3, id=39
2023-05-31 21:24:24.006  INFO 29676 --- [pool-1-thread-1] org.example.component.TaskComponent      : task2-------当先线程的名字=pool-1-thread-1, id=36
2023-05-31 21:24:24.006  INFO 29676 --- [pool-1-thread-1] org.example.component.TaskComponent      : task2 执行完了,线程的名字=pool-1-thread-1, id=36
2023-05-31 21:24:26.006  INFO 29676 --- [pool-1-thread-4] org.example.component.TaskComponent      : task2-------当先线程的名字=pool-1-thread-4, id=44
2023-05-31 21:24:26.006  INFO 29676 --- [pool-1-thread-4] org.example.component.TaskComponent      : task2 执行完了,线程的名字=pool-1-thread-4, id=44
2023-05-31 21:24:28.015  INFO 29676 --- [pool-1-thread-3] org.example.component.TaskComponent      : task2-------当先线程的名字=pool-1-thread-3, id=39
2023-05-31 21:24:28.015  INFO 29676 --- [pool-1-thread-2] org.example.component.TaskComponent      : task1 执行完了,线程的名字=pool-1-thread-2, id=37
2023-05-31 21:24:28.015  INFO 29676 --- [pool-1-thread-3] org.example.component.TaskComponent      : task2 执行完了,线程的名字=pool-1-thread-3, id=39
2023-05-31 21:24:29.011  INFO 29676 --- [pool-1-thread-5] org.example.component.TaskComponent      : task1-------当先线程的名字=pool-1-thread-5, id=49
2023-05-31 21:24:30.010  INFO 29676 --- [pool-1-thread-1] org.example.component.TaskComponent      : task2-------当先线程的名字=pool-1-thread-1, id=36
2023-05-31 21:24:30.010  INFO 29676 --- [pool-1-thread-1] org.example.component.TaskComponent      : task2 执行完了,线程的名字=pool-1-thread-1, id=36
2023-05-31 21:24:32.008  INFO 29676 --- [pool-1-thread-6] org.example.component.TaskComponent      : task2-------当先线程的名字=pool-1-thread-6, id=54
2023-05-31 21:24:32.008  INFO 29676 --- [pool-1-thread-6] org.example.component.TaskComponent      : task2 执行完了,线程的名字=pool-1-thread-6, id=54
2023-05-31 21:24:34.001  INFO 29676 --- [pool-1-thread-6] org.example.component.TaskComponent      : task2-------当先线程的名字=pool-1-thread-6, id=54
2023-05-31 21:24:34.001  INFO 29676 --- [pool-1-thread-6] org.example.component.TaskComponent      : task2 执行完了,线程的名字=pool-1-thread-6, id=54

观察日志输出,可以看出task1是上一个执行结束之后,下一个task1才会继续执行

2.Spring-quartz框架

导入依赖

<!--    quartz 定时调度框架 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>

QuartzTask1.java

package org.example.component;

import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@DisallowConcurrentExecution
public class QuartzTask2  extends QuartzJobBean {
    @Override
    protected  void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("task2任务执行开始,线程id={}", Thread.currentThread().getId());

        log.info("task2任务执行结束,线程id={}", Thread.currentThread().getId());
    }
}

QuartzTask2.java

package org.example.component;

import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@DisallowConcurrentExecution
public class QuartzTask2  extends QuartzJobBean {
    @Override
    protected  void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("task2任务执行开始,线程id={}", Thread.currentThread().getId());

        log.info("task2任务执行结束,线程id={}", Thread.currentThread().getId());
    }
}

QuartzConfig.java

package org.example.config;

import lombok.extern.slf4j.Slf4j;
import org.example.component.QuartzTask1;
import org.example.component.QuartzTask2;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class QuartzConfig {
    @Bean
    public JobDetail task1_job() {
        // JobDetail 需要加入 storeDurably() 不然会报错
        return JobBuilder.newJob(QuartzTask1.class).storeDurably().build();
    }
    @Bean
    public JobDetail task2_job() {
        return JobBuilder.newJob(QuartzTask2.class).storeDurably().build();
    }

    @Bean
    public Trigger task1_trigger() {
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/1 * * * * ?");
        return TriggerBuilder.newTrigger()
                .forJob(task1_job())
                .withIdentity("task1")
                .withSchedule(scheduleBuilder)
                .build();
    }

    @Bean
    public Trigger task2_trigger() {
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/2 * * * * ?");
        return TriggerBuilder.newTrigger()
                .forJob(task2_job())
                .withIdentity("task2")
                .withSchedule(scheduleBuilder)
                .build();
    }
}
2023-05-31 21:53:42.012  INFO 50100 --- [eduler_Worker-2] org.example.component.QuartzTask2        : task2任务执行开始,线程id=25
2023-05-31 21:53:42.012  INFO 50100 --- [eduler_Worker-2] org.example.component.QuartzTask2        : task2任务执行结束,线程id=25
2023-05-31 21:53:44.013  INFO 50100 --- [eduler_Worker-3] org.example.component.QuartzTask2        : task2任务执行开始,线程id=26
2023-05-31 21:53:44.013  INFO 50100 --- [eduler_Worker-3] org.example.component.QuartzTask2        : task2任务执行结束,线程id=26
2023-05-31 21:53:46.010  INFO 50100 --- [eduler_Worker-4] org.example.component.QuartzTask2        : task2任务执行开始,线程id=27
2023-05-31 21:53:46.010  INFO 50100 --- [eduler_Worker-4] org.example.component.QuartzTask2        : task2任务执行结束,线程id=27
2023-05-31 21:53:48.004  INFO 50100 --- [eduler_Worker-5] org.example.component.QuartzTask2        : task2任务执行开始,线程id=28
2023-05-31 21:53:48.004  INFO 50100 --- [eduler_Worker-5] org.example.component.QuartzTask2        : task2任务执行结束,线程id=28
2023-05-31 21:53:50.003  INFO 50100 --- [eduler_Worker-6] org.example.component.QuartzTask2        : task2任务执行开始,线程id=29
2023-05-31 21:53:50.003  INFO 50100 --- [eduler_Worker-6] org.example.component.QuartzTask2        : task2任务执行结束,线程id=29
2023-05-31 21:53:51.823  INFO 50100 --- [eduler_Worker-1] org.example.component.QuartzTask1        : task1任务执行结束,线程id=24
2023-05-31 21:53:51.824  INFO 50100 --- [eduler_Worker-7] org.example.component.QuartzTask1        : task1任务执行开始,线程id=30
2023-05-31 21:53:52.017  INFO 50100 --- [eduler_Worker-8] org.example.component.QuartzTask2        : task2任务执行开始,线程id=31
2023-05-31 21:53:52.017  INFO 50100 --- [eduler_Worker-8] org.example.component.QuartzTask2        : task2任务执行结束,线程id=31
2023-05-31 21:53:54.008  INFO 50100 --- [eduler_Worker-9] org.example.component.QuartzTask2        : task2任务执行开始,线程id=32
2023-05-31 21:53:54.008  INFO 50100 --- [eduler_Worker-9] org.example.component.QuartzTask2        : task2任务执行结束,线程id=32
2023-05-31 21:53:56.012  INFO 50100 --- [duler_Worker-10] org.example.component.QuartzTask2        : task2任务执行开始,线程id=33
2023-05-31 21:53:56.012  INFO 50100 --- [duler_Worker-10] org.example.component.QuartzTask2        : task2任务执行结束,线程id=33
2023-05-31 21:53:58.009  INFO 50100 --- [eduler_Worker-2] org.example.component.QuartzTask2        : task2任务执行开始,线程id=25
2023-05-31 21:53:58.010  INFO 50100 --- [eduler_Worker-2] org.example.component.QuartzTask2        : task2任务执行结束,线程id=25
2023-05-31 21:54:00.009  INFO 50100 --- [eduler_Worker-3] org.example.component.QuartzTask2        : task2任务执行开始,线程id=26
2023-05-31 21:54:00.009  INFO 50100 --- [eduler_Worker-3] org.example.component.QuartzTask2        : task2任务执行结束,线程id=26
2023-05-31 21:54:01.826  INFO 50100 --- [eduler_Worker-7] org.example.component.QuartzTask1        : task1任务执行结束,线程id=30
2023-05-31 21:54:01.827  INFO 50100 --- [eduler_Worker-4] org.example.component.QuartzTask1        : task1任务执行开始,线程id=27
2023-05-31 21:54:02.002  INFO 50100 --- [eduler_Worker-5] org.example.component.QuartzTask2        : task2任务执行开始,线程id=28
2023-05-31 21:54:02.003  INFO 50100 --- [eduler_Worker-5] org.example.component.QuartzTask2        : task2任务执行结束,线程id=28
2023-05-31 21:54:04.011  INFO 50100 --- [eduler_Worker-6] org.example.component.QuartzTask2        : task2任务执行开始,线程id=29
2023-05-31 21:54:04.011  INFO 50100 --- [eduler_Worker-6] org.example.component.QuartzTask2        : task2任务执行结束,线程id=29
2023-05-31 21:54:06.004  INFO 50100 --- [eduler_Worker-1] org.example.component.QuartzTask2        : task2任务执行开始,线程id=24
2023-05-31 21:54:06.004  INFO 50100 --- [eduler_Worker-1] org.example.component.QuartzTask2        : task2任务执行结束,线程id=24
2023-05-31 21:54:08.012  INFO 50100 --- [eduler_Worker-8] org.example.component.QuartzTask2        : task2任务执行开始,线程id=31
2023-05-31 21:54:08.012  INFO 50100 --- [eduler_Worker-8] org.example.component.QuartzTask2        : task2任务执行结束,线程id=31
2023-05-31 21:54:10.011  INFO 50100 --- [eduler_Worker-9] org.example.component.QuartzTask2        : task2任务执行开始,线程id=32
2023-05-31 21:54:10.012  INFO 50100 --- [eduler_Worker-9] org.example.component.QuartzTask2        : task2任务执行结束,线程id=32
2023-05-31 21:54:11.828  INFO 50100 --- [eduler_Worker-4] org.example.component.QuartzTask1        : task1任务执行结束,线程id=27
2023-05-31 21:54:11.828  INFO 50100 --- [duler_Worker-10] org.example.component.QuartzTask1        : task1任务执行开始,线程id=33

通过日志输出,可以看到,quartz达到了,springtask使用线程池的效果。

单个任务的串行执行,使用的注解 @DisallowConcurrentExecution 不允许并行执行

quartz的核心一共有三个类

  • 任务 Job :需要执行的任务逻辑类,需要实现 Job 接口的 execute 方法
  • 触发器 Trigger :执行任务的调度器。例如,每天的00:00执行任务A,那么就需要设置Trigger的属性
  • 调度器 Scheduler : 任务的调度器,将任务 Job和触发器Trigger整合起来,负责基于Trigger设定的时间执行任务Job

标签:Quartz,框架,31,component,id,线程,org,任务调度,example
From: https://www.cnblogs.com/Sun-yuan/p/17447497.html

相关文章

  • datax 抽数据框架
    标签(空格分隔):协作框架一:datax概述1.1datax介绍1.1、什么使dataxDataX是阿里巴巴开源的一个异构数据源离线同步工具,致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。1.2、datax的设计为了解决异构数......
  • 大数据展示框架SuperSet 安装
    标签(空格分隔):协作框架**1.1Superset概述**ApacheSuperset是一个现代的数据探索和可视化平台。它功能强大且十分易用,可对接各种数据源,包括很多现代的大数据分析引擎,拥有丰富的图表展示形式,并且支持自定义仪表盘。**1.2环境说明**本课程使用的服务器操作系统为CentOS7,Sup......
  • 一个由于不同微服务框架混搭导致BeanPostProcessors处理bean异常导致的问题
        前天到昨天晚上,某开发报告了一个问题,我们的一个应用程序接入了腾讯的TSF微服务框架后,使用feign访问接口,会导致token丢失,无法解决。    大体介绍下项目情况,我们的应用使用了某第三方微服务框架,不是源生的springcloud或springcloudalibaba框架,第三方厂家基于s......
  • PAC学习框架
    PAC学习框架是机器学习的基础。它主要用来回答以下几个问题:什么问题是可以高效学习的?什么问题本质上就难以学习?需要多少实例才能完成学习?是否存在一个通用的学习模型?PAC=probablyapproximatelycorrect,很可能接近正确的---------------------什么问题能得到“可能接近正确”的结果......
  • SpringBoot定义优雅全局统一Restful API 响应框架五
    闲话不多说,继续优化全局统一RestfulAPI响应框架做到项目通用接口可扩展。如果没有看前面几篇文章请先看前面几篇SpringBoot定义优雅全局统一RestfulAPI响应框架SpringBoot定义优雅全局统一RestfulAPI响应框架二SpringBoot定义优雅全局统一RestfulAPI响应框架三Sp......
  • 最小编译器和 UI 框架「GitHub 热点速览」
    如果有一个关键词来概述本周的GitHub热门项目的话,大概就是van和sectorc都用到的smallest。只不过一个是前端的响应式框架,一个是搞编译的C编译器。它们除了轻量化这个共同特点之外,还有好用,足以满足你的日常编程所需。说到编程,EasySpider便是一个免去敲代码工作量,用看得......
  • Unity框架与.NET, Mono框架的关系
    什么是C#C#是一种面向对象的编程语言。什么是.NET.NET是一个开发框架,它遵循并采用CIL(CommonIntermediateLanguage)和CLR(CommonLanguageRuntime)两种约定,CIL标准为一种编译标准:将不同编程语言(C#,JS,VB等)使用各自的编译器,按照统一的标准编译成语义一致的CIL中间码,......
  • Quartz使用实践
    Quartz是一个开源的作业调度框架,它允许开发人员根据特定的时间规则来执行任务。Quartz提供了灵活的配置选项和丰富的功能,可以用于定时执行任务、定期重复任务、分布式任务调度等。下面是Quartz的介绍以及使用Java代码实现的入门示例:Quartz的介绍:Quartz是一个功能强大的作业调度......
  • 《可伸缩服务架构-框架与中间件》-00-随笔计划
    初步计划大约花费9*5天时间精细阅读本书。目标输出:每个篇章输出一篇随笔,分析架构和逻辑内容。第一章:分布式发号器(5月31号--6月4号)第二章:消息队列(6月5号--6月9号)第三章:数据库分库分表(6月10号--6月14号)第四章:缓存(6月15号--6月19号)第五章:ES(6月20号--6月24号)第六章:定制任务(6月2......
  • 《可伸缩服务架构-框架与中间件》-01-分布式发号器
    本文主要是设计一款永不重复的高性能分布式发号器。源码地址——码云:https://gitee.com/robertleepeak/vesta-id-generatorVesta是一款通用的ID产生器,互联网俗称统一发号器,它具有全局唯一、粗略有序、可反解和可制造等特性,它支持三种发布模式:嵌入发布模式、中心服务器发布模式、......