quartz源码分析

前言

在上一章quartz的使用-CSDN博客有讲关于quartz的使用,包括常规使用,结合mysql存储做分布式调度和结合springboot使用,本篇文章主要做定时调度的主流程源码分析

定式

public static void main(String[] args) throws Exception{
        SchedulerFactory factory = new StdSchedulerFactory();
        Scheduler scheduler = factory.getScheduler();
 
        JobKey jobKey = new JobKey("triggerTX2", "groupTX1"); // 任务(Job)的唯一标识,如果在创建任务时没有设置该项,由框架生成
        List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobKey);
 
        if (Objects.nonNull(triggersOfJob) && triggersOfJob.size() > 0) {
            scheduler.resumeJob(jobKey);
 
        } else {
            JobDetail jobDetail = JobBuilder.newJob(FirstJob.class)
                    .withIdentity(jobKey)
                    .usingJobData("userName", "zhangsan") // 此处可以添加需要的调度参数,然后会把参数封装到JobExecutionContent中
                    .build();
 
            CronTrigger trigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(jobKey.getName(),jobKey.getGroup()) // 设置标识
                    .usingJobData("address","123")  // 此处可以添加需要的调度参数,然后会把参数封装到JobExecutionContent中
                    .withSchedule(CronScheduleBuilder.cronSchedule("0 0/1 * * * ?"))
                    .startNow()
                    .build();
 
            scheduler.scheduleJob(jobDetail,trigger);
        }
        scheduler.start();
    }

重要的类

Quartz框架中重要的类和作用分别简介如下:

SchedulerFactory

调度器工厂,顾名思义该类是用于生产调度器的,常用的实现是:StdSchedulerFactory 标准调度器工厂

Scheduler

最常用的实现是 org.quartz.impl.StdScheduler 标准调度器,此类持有一个QuartzScheduler实例

Trigger

触发器,用于触发任务的调用,常用的实现是:org.quartz.impl.triggers.CronTriggerImpl,该类包含了触发调度需要的cron表达式信息,和当前触发器的下次,上次调度时间等信息

JobDetail

具体的任务信息,最常用的实现是:org.quartz.impl.JobDetailImpl 该类包含了该任务关联的执行类,该任务的名称,所属任务组,任务执行过程中所需的参数信息等

QuartzScheduler

quartz框架最核心的调度单元,持有参与调度的资源信息(QuartzSchedulerResources实例),调度线程信息(QuartzSchedulerThread实例),调度上下文信息等等

QuartzSchedulerResources 

调度的资源信息,包括quartz.properties配置文件中设置的属性和其他的信息,是quartz的仓库类

QuartzSchedulerThread 

quartz框架发起调度的入口,本质是一个线程。是触发调度的入口

JobRunShell 

本质是一个runnable接口实例,真正的任务执行单元,持有JobDetail详细信息(间接持有)

从上图可知:JobRunShell间接持有JobDetail,也就是持有了任务执行的类相关信息,后续的执行就是通过JobRunShell执行的,详细的执行逻辑:org.quartz.core.JobRunShell#run

ThreadPool

本质是一个线程池,通过该线程池调度JobRunShell实例,实现任务的调度,常见的实现是

org.quartz.simpl.SimpleThreadPool。该类是发起JobRunShell执行的入口,其中有3个重要的列表

其中workers是线程总数,availworkers是可用的线程列表,也就是空闲的线程列表,busyWorkers是正在执行任务的线程列表

入口分析

在上面已经对quartz框架的重要的类和作用做了简单的介绍。这次从定式触发,从入口分析定式调度的整个过程

Scheduler scheduler = factory.getScheduler();

factory的实现类是StdSchedulerFactory,真正的执行逻辑是:

public Scheduler getScheduler() throws SchedulerException {
        if (cfg == null) {
            initialize();
        }

        SchedulerRepository schedRep = SchedulerRepository.getInstance();

        Scheduler sched = schedRep.lookup(getSchedulerName());

        if (sched != null) {
            if (sched.isShutdown()) {
                schedRep.remove(getSchedulerName());
            } else {
                return sched;
            }
        }

        sched = instantiate();

        return sched;
    }

第一步:读取配置文件

其中cfg(PropertiesParser)是一个配置类。通过读取配置文件初始化信息,配置文件的读取顺序是:

1 首先读取系统参数org.quartz.properties配置文件位置

2 如果1未读取到配置文件,读取项目resources目录下的quartz.properties文件

3 如果2位读取到配置文件,直接读取框架的配置文件信息

具体信息参见:org.quartz.impl.StdSchedulerFactory#initialize()

第二步:创建调度仓库实例

SchedulerRepository可以简单理解成一个Map,实际上也确实是一个Map

第三部:创建调度器

这部分代码较长,但是逻辑还是比较简单,以下只是简单的列举部分代码做说明

1  通过配置文件获取对应的配置属性,如果未配置,使用默认的配置属性

 2 构建 QuartzSchedulerResources

3 构建ThreadPool并调用初始化方法

ThreadPool是发起任务调度的线程池,里面有三个线程列表

初始化方法通过配置参数,构建线程池包含的总线程数并调用

而WorkerThread的核心逻辑受两个参数影响:

其中runnable就是需要运行的线程:

也就是:ThreadPool初始化了一组线程并启动了该组线程,该组线程的核心执行逻辑受2个参数的影响:当runnable==null并且run的值是true时,就阻塞循环。 

换言之:如果设置WorkerThread实例中runnable的值,就可以跳出阻塞循环,并执行逻辑!

4 构建JobRunShellFactory

顾名思义,JobRunShellFactory是生产JobRunShell的,而JobRunShell在上面已经简单介绍过,是真正调用任务类核心逻辑的,本质是一个Runnable对象。这里具体是JTAJobRunShellFactory还是JTAAnnotationAwareJobRunShellFactory实现是根据配置:

org.quartz.scheduler.wrapJobExecutionInUserTransaction而定

5 构建QuartzScheduler

注意,上面已经介绍过QuartzScheduler是最核心的调度单元,里面有QuartzSchedulerThread等核心属性,这些属性都在构造方法中体现

而QuartzSchedulerThread就是执行调度的入口

quartzSchedulerThread的核心调用逻辑也受2个参数的控制:paused和halted

QuartzSchedulerThread的业务逻辑如下:查询可用的线程数和触发时间在idlewaitTime以内的触发器列表

 然后通过触发器列表找到对应的任务信息

构建JobRunShell并调用初始化方法

通过ThreadPool执行JobRunShell

实际执行ThreadPool#run(Runnable)方法

这里设置WorkerThread的runnable的值。在上面关于WorkerThread的分析中,有讲到WorkerThread的业务逻辑执行当runnable==null并且run的值是true时,就阻塞循环。这里设置了值,就跳出了阻塞循环,执行核心逻辑。

6 构建框架执行所需的其他组件

包括多个监听器的添加等等逻辑

至此:Quartz的核心逻辑已基本梳理清楚,总结如下:

在构建StdScheduler的过程中,构建了ThreadPool,ThreadPool是一组WorkerThread,WokerThread的核心逻辑是通过runnable==null并且run的值是true时控制业务逻辑的执行。通过构建了QuartzSchedulerThread,而QuartzSchedulerThread的核心逻辑就是:查询最近要触发的触发器列表,通过触发器列表找到任务信息,构建RunJobShell。再通过WorkerThread执行RunJobShell,也就是执行任务的实际业务逻辑

疑问解答

scheduler#start方法做了什么

在上面的讲解中,有提到QuartzSchedulerThread的运行是通过两个属性控制:paused和halted

而scheduler.start()即是修改了paused的值,触发QuartSchedulerThread的核心逻辑调用,从而触发整个调度逻辑

scheduler#scheduleJob做了什么?

源码显示,该方法就是简单的对Job信息和trigger信息做了简单的存储和通过监听器发送消息

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是 SpringBoot 搭配 Quartz 实现动态定时任务的源码: 1. 首先,我们需要引入 Quartz 和 SpringBoot 的依赖: ```xml <!-- Quartz相关依赖 --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.3.2</version> </dependency> <!-- SpringBoot相关依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> ``` 2. 创建定时任务实体类,用于封装定时任务的信息,包括任务名称、任务组、任务类名、任务状态(是否启用)、任务表达式等: ```java @Entity @Table(name = "job_task") @Data public class JobTask implements Serializable { private static final long serialVersionUID = 1L; /** * ID */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** * 任务名称 */ @NotBlank(message = "任务名称不能为空") private String name; /** * 任务分组 */ @NotBlank(message = "任务分组不能为空") private String group; /** * 任务类名 */ @NotBlank(message = "任务类名不能为空") private String className; /** * 任务状态,0:禁用,1:启用 */ @NotNull(message = "任务状态不能为空") private Integer status; /** * 任务表达式 */ @NotBlank(message = "任务表达式不能为空") private String cronExpression; /** * 创建时间 */ private LocalDateTime createTime; /** * 最后一次修改时间 */ private LocalDateTime updateTime; } ``` 3. 创建定时任务的服务类,用于管理定时任务的增删改查等操作,同时也需要实现 `InitializingBean` 接口,在启动应用时加载已存在的定时任务: ```java @Service @AllArgsConstructor public class JobTaskService implements InitializingBean { private final Scheduler scheduler; private final JobTaskRepository jobTaskRepository; /** * 添加任务 * @param jobTask * @return * @throws Exception */ public boolean addJobTask(JobTask jobTask) throws Exception { if (jobTask == null || StringUtils.isBlank(jobTask.getCronExpression())) { return false; } if (StringUtils.isBlank(jobTask.getName()) || StringUtils.isBlank(jobTask.getClassName())) { throw new Exception("任务名称或任务类名不能为空"); } // 判断任务是否已存在 JobKey jobKey = JobKey.jobKey(jobTask.getName(), jobTask.getGroup()); if (scheduler.checkExists(jobKey)) { return false; } // 构建任务实例 JobDetail jobDetail = JobBuilder.newJob(getClass(jobTask.getClassName()).getClass()) .withIdentity(jobTask.getName(), jobTask.getGroup()) .build(); // 构建任务触发器 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobTask.getCronExpression()); CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity(jobTask.getName(), jobTask.getGroup()) .withSchedule(cronScheduleBuilder) .build(); // 注册任务和触发器 scheduler.scheduleJob(jobDetail, trigger); // 如果任务状态为启用,则立即启动任务 if (jobTask.getStatus() == 1) { scheduler.triggerJob(jobKey); } // 保存任务信息 jobTask.setCreateTime(LocalDateTime.now()); jobTask.setUpdateTime(LocalDateTime.now()); jobTaskRepository.save(jobTask); return true; } /** * 修改任务 * @param jobTask * @return * @throws Exception */ public boolean modifyJobTask(JobTask jobTask) throws Exception { if (jobTask == null || StringUtils.isBlank(jobTask.getCronExpression())) { return false; } if (StringUtils.isBlank(jobTask.getName()) || StringUtils.isBlank(jobTask.getClassName())) { throw new Exception("任务名称或任务类名不能为空"); } // 判断任务是否存在 JobKey jobKey = JobKey.jobKey(jobTask.getName(), jobTask.getGroup()); if (!scheduler.checkExists(jobKey)) { return false; } // 修改任务触发器 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobTask.getCronExpression()); CronTrigger newTrigger = TriggerBuilder.newTrigger() .withIdentity(jobTask.getName(), jobTask.getGroup()) .withSchedule(cronScheduleBuilder) .build(); scheduler.rescheduleJob(TriggerKey.triggerKey(jobTask.getName(), jobTask.getGroup()), newTrigger); // 修改任务信息 JobTask oldJobTask = jobTaskRepository.findByNameAndGroup(jobTask.getName(), jobTask.getGroup()); oldJobTask.setClassName(jobTask.getClassName()); oldJobTask.setStatus(jobTask.getStatus()); oldJobTask.setCronExpression(jobTask.getCronExpression()); oldJobTask.setUpdateTime(LocalDateTime.now()); jobTaskRepository.save(oldJobTask); return true; } /** * 删除任务 * @param name * @param group * @return * @throws Exception */ public boolean deleteJobTask(String name, String group) throws Exception { JobKey jobKey = JobKey.jobKey(name, group); if (!scheduler.checkExists(jobKey)) { return false; } scheduler.deleteJob(jobKey); jobTaskRepository.deleteByNameAndGroup(name, group); return true; } /** * 获取所有任务 * @return */ public List<JobTask> getAllJobTask() { return jobTaskRepository.findAll(); } /** * 根据任务名称和分组获取任务信息 * @param name * @param group * @return */ public JobTask getJobTaskByNameAndGroup(String name, String group) { return jobTaskRepository.findByNameAndGroup(name, group); } /** * 获取任务类实例 * @param className * @return * @throws Exception */ private Object getClass(String className) throws Exception { Class<?> clazz = Class.forName(className); return clazz.newInstance(); } /** * 实现 InitializingBean 接口,在启动应用时加载已存在的定时任务 * @throws Exception */ @Override public void afterPropertiesSet() throws Exception { List<JobTask> jobTaskList = jobTaskRepository.findAll(); for (JobTask jobTask : jobTaskList) { if (jobTask.getStatus() == 1) { addJobTask(jobTask); } } } } ``` 4. 创建定时任务的控制器类,用于处理新增、修改、删除等请求: ```java @RestController @AllArgsConstructor @RequestMapping("/job") public class JobTaskController { private final JobTaskService jobTaskService; /** * 添加任务 * @param jobTask * @return * @throws Exception */ @PostMapping public ResponseEntity addJobTask(@RequestBody JobTask jobTask) throws Exception { boolean result = jobTaskService.addJobTask(jobTask); return result ? ResponseEntity.ok("任务添加成功") : ResponseEntity.badRequest().body("任务添加失败"); } /** * 修改任务 * @param jobTask * @return * @throws Exception */ @PutMapping public ResponseEntity modifyJobTask(@RequestBody JobTask jobTask) throws Exception { boolean result = jobTaskService.modifyJobTask(jobTask); return result ? ResponseEntity.ok("任务修改成功") : ResponseEntity.badRequest().body("任务修改失败"); } /** * 删除任务 * @param name * @param group * @return * @throws Exception */ @DeleteMapping("/{name}/{group}") public ResponseEntity deleteJobTask(@PathVariable String name, @PathVariable String group) throws Exception { boolean result = jobTaskService.deleteJobTask(name, group); return result ? ResponseEntity.ok("任务删除成功") : ResponseEntity.badRequest().body("任务删除失败"); } /** * 获取所有任务 * @return */ @GetMapping public ResponseEntity getAllJobTask() { List<JobTask> jobTaskList = jobTaskService.getAllJobTask(); return ResponseEntity.ok(jobTaskList); } /** * 根据任务名称和分组获取任务信息 * @param name * @param group * @return */ @GetMapping("/{name}/{group}") public ResponseEntity getJobTaskByNameAndGroup(@PathVariable String name, @PathVariable String group) { JobTask jobTask = jobTaskService.getJobTaskByNameAndGroup(name, group); return ResponseEntity.ok(jobTask); } } ``` 5. 创建定时任务的启动类,用于启动 SpringBoot 应用: ```java @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } /** * 注册定时任务调度器 * @return * @throws SchedulerException */ @Bean public SchedulerFactoryBean schedulerFactoryBean() throws SchedulerException { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); Properties properties = new Properties(); properties.put("org.quartz.scheduler.instanceName", "ChitGPTScheduler"); properties.put("org.quartz.threadPool.threadCount", "10"); schedulerFactoryBean.setQuartzProperties(properties); schedulerFactoryBean.setStartupDelay(5); return schedulerFactoryBean; } /** * 注册定时任务实例 * @return */ @Bean public Scheduler scheduler() { return schedulerFactoryBean().getScheduler(); } } ``` 以上就是 SpringBoot 搭配 Quartz 实现动态定时任务的源码,希望能对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值