spring-boot Quartz 实践
一、基本概念了解
之前做自动化测试平台有个需求,就是系统在每天晚上定时去执行一个测试任务,执行完成后把这个测试结果通过邮件发送出去。要求定时任务是随时可配置,之前有过一个方案,就是在linux使用crontab定时请求系统的一个任务执行接口。虽然可以解决对应的问题,而且某些方面来讲简单粗暴,但是在定时器配置方面着实麻烦,想通过代码方面来解决,于是就考虑用quartz来解决。
通过google大概了解一下quartz的基本概念,知道了quartz的一些基本元素(强烈建议大致了解下quartz的工作流程,之后再动手去实践,会少走很多弯路),quartz主要有以下三个元素构成:
Scheduler:调度器或者说是容器,所有的任务和触发器都是在这个容器中进行注册。
JobDetail:一个可执行的任务
Trigger:任务的处触发条件
工作流程主要是在将JobDetail 和 Trigger组合一起注册到Scheduler容器中,然后通过Scheduler依据Trigger来调度执行JobDetail的内容。
二、简单例子
按照上面的思想,我们做一个最简单的定时器任务,具体如下:
1、创建一个可执行的任务
import org.quartz.Job; import org.quartz.JobExecutionContext; /** * Created by tangrubei on 2018/3/30. */ public class Myjob implements Job { @Override public void execute(JobExecutionContext context) { // 获取上下文内容 String data = (String) context.getMergedJobDataMap().get("data"); // do something System.out.println(data); } }
2、创建容器和触发器,并将任务和触发器一起注册到容器中
import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; /** * Created by tangrubei on 2018/3/30. */ public class MyJobTest { public static void main(String[] args) throws SchedulerException, InterruptedException { // 创建容器工厂类 SchedulerFactory schedulerFactory = new StdSchedulerFactory(); // 创建容器 Scheduler scheduler = schedulerFactory.getScheduler(); // 启动容器 scheduler.start(); // 创建一个可执行的工作任务 JobDetail jobDetail = JobBuilder.newJob(Myjob.class).withIdentity("myfirstJob", "mygroup").build(); // 创建一个触发器 CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("myfirstJobTriger", "mygroup") .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build(); // 设置任务的执行的上下文 jobDetail.getJobDataMap().put("data", "my first job"); // 讲任务和定时器注册到容器中 scheduler.scheduleJob(jobDetail, trigger); } }
三、项目实践:
回到我们最初的需求中来,我们需要实现一个定时的组件,这个组件支持增加、删除、更新任务,同时在系统启动的时候,会自动到配置或者数据库中去load已经配置好的任务。
依据上面的简单实例我们很容易做到从配置中动态增加、删除、更新任务,只是在项目启动的时候去执行这些东西还没有实现,这个时候稍微google一下就能发现我们只要实现ApplicationListener接口就可以实现项目启动的时候就加载这个定时器,直接上代码(我这边的需求是一个任务对应一个触发器,如果一个触发器对应多个任务的话,删除和更新就不能按照我这个来写了)
import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import java.util.HashMap; import java.util.Map; /** * Created by tangrubei on 2017/2/10. */ public class QuartzManagerComponent implements ApplicationListener<ContextRefreshedEvent> { private Scheduler scheduler; private final String groupName = "groupExecute"; // 注入工厂类 @Autowired private SchedulerFactory schedulerFactory; // 系统启动时执行 @Override public void onApplicationEvent(ContextRefreshedEvent event) { try { // 初始化容器 scheduler = schedulerFactory.getScheduler(); // 启动容器 scheduler.start(); // 初始job上下文和注册任务,这里可以改为从数据库或者配置里获取任务,动态加载 Map datamap = new HashMap(); datamap.put("data","my job time"); this.addJob("myjob","0/5 * * * * ?",MyJob.class,datamap); // } catch (SchedulerException e) { e.printStackTrace(); } } // 新增任务 pbulic void addJob(String jobName, String jobTime, Class jobClassz, Map<String, Object> dataMap) throws SchedulerException { JobDetail job = JobBuilder.newJob(jobClassz).withIdentity(jobName, groupName).build(); CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, groupName) .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build(); if (dataMap != null) { dataMap.forEach((k, v) -> { job.getJobDataMap().put(k, v); }); } scheduler.scheduleJob(job, trigger); } // 删除任务 public void deleteJob(String jobName) throws SchedulerException { TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName); if (triggerKey != null) { // 暂停定时器 scheduler.pauseTrigger(triggerKey); // 删除定时器 scheduler.unscheduleJob(triggerKey); JobKey jobKey = JobKey.jobKey(jobName, groupName); // 删除任务 scheduler.deleteJob(jobKey); } } // 更新任务 public void updateJob(String jobName, String jobTime, Class classz, Map<String, Object> dataMap) throws SchedulerException { TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName); this.deleteJob(jobName); this.addJob(jobName, jobTime, classz, dataMap); } }