调度的任务都必须实现 org.quartz.job 接口,然后实现接口中定义的 execute( ) 方法即可
Trigger 作为执行任务的调度器。如果想要凌晨1点执行备份数据的任务,那么 Trigger 就会设置凌晨1点执行该任务。其中 Trigger 又分为 SimpleTrigger 和 CronTrigger 两种
Scheduler 为任务的调度器,它会将任务 Job 及触发器 Trigger 整合起来,负责基于 Trigger 设定的时间来执行 Job
体系架构
quartz的基本使用
首先导入依赖
org.quartz-scheduler
quartz
2.3.2
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
//创建调度器
Scheduler sched = schedFact.getScheduler();
sched.start();
// define the job and tie it to our HelloJob class
JobDetail job = newJob(HelloJob.class)
.withIdentity("myJob", "group1")
.build();
// Trigger the job to run now, and then every 40 seconds
Trigger trigger = newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(40)
.repeatForever())
.build();
// Tell quartz to schedule the job using our trigger
sched.scheduleJob(job, trigger);
public class HelloJob implements Job {
public HelloJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException
{
System.err.println("Hello! HelloJob is executing.");
}
}
JobDataMap 可用于保存任何数量的(可序列化的)数据对象,在作业实例执行时这些数据对象可供作业实例使用。JobDataMap 是 Java Map 接口的一个实现,并且增加了一些方便的方法来存储和检索原始类型的数据。
// define the job and tie it to our DumbJob class
JobDetail job = newJob(DumbJob.class)
.withIdentity("myJob", "group1") // name "myJob", group "group1"
.usingJobData("jobSays", "Hello World!")
.usingJobData("myFloatValue", 3.141f)
.build();
//获取JobDetail中的数据
public class DumbJob implements Job {
public DumbJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException
{
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String jobSays = dataMap.getString("jobSays");
float myFloatValue = dataMap.getFloat("myFloatValue");
System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
}
Trigger
分为simple Trigger cron Trigger
//Simple Trigger
//为特定时刻构建触发器,然后每十秒重复十次:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.startAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
.forJob(myJob) // identify job with handle to its JobDetail itself
.build();
//构建一个触发器,该触发器将在周三上午 10:42 触发
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(weeklyOnDayAndHourAndMinute(DateBuilder.WEDNESDAY, 10, 42))
.forJob(myJobKey)
.inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
.build();
//或者cron
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 42 10 ? * WED"))
.inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
.forJob(myJobKey)
.build();
在项目中的使用以伪代码表示
//定义抽象类
public abstract class AbstractQuartzJob implements Job {
private static final ThreadLocal threadLocal = new ThreadLocal<>();
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
before(context, job);
doExecute(context, job);
after(context, sysJob, null);
} catch (Exception e) {
log.error("任务执行异常 - :", e);
after(context, job, e);
}
}
/**
* 执行前
*/
protected void before(JobExecutionContext context, Dd sysJob) {
threadLocal.set(new Date());
}
/**
* 执行后
*/
protected void after(JobExecutionContext context, Dd sysJob, Exception e) {
//log
}
}
//具体实现类
//定时任务处理(禁止并发执行)
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
@Override
protected void doExecute(JobExecutionContext context, Dd job) throws Exception {
JobInvokeUtil.invokeMethod(job);
}
}
//定时任务处理(允许并发执行)
public class QuartzJobExecution extends AbstractQuartzJob {
@Override
protected void doExecute(JobExecutionContext context, Dd job) throws Exception {
JobInvokeUtil.invokeMethod(job);
}
}
public class JobInvokeUtil {
/**
* 执行方法
*/
public static void invokeMethod(Dd job) throws Exception {
String invokeTarget = job.getClassAndMethodName();
String beanName = getBeanName(invokeTarget);
String methodName = getMethodName(invokeTarget);
List
public class ScheduleUtils {
/**
* 创建定时任务
*/
public static void createScheduleJob(Scheduler scheduler, Dd job) throws SchedulerException, TaskException {
Class extends Job> jobClass = getQuartzJobClass(job);
// 构建job信息
Long jobId = job.getId();
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobGroup).build();
// 表达式调度构建器 corn
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron());
cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
// 按新的cronExpression表达式构建一个新的trigger trigger key corn表达式
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobGroup)
.withSchedule(cronScheduleBuilder).build();
// 放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
// 判断是否存在
if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(getJobKey(jobId, jobGroup));
}
scheduler.scheduleJob(jobDetail, trigger);
}
}
在项目中使用分布式并发部署定时任务,多台跨JVM,按照常理逻辑每个JVM的定时任务会各自运行,这样就会存在问题,多台分布式JVM机器的应用服务同时干活,一个是加重服务负担,另外一个是存在严重的逻辑问题,比如需要回滚的数据,就回滚了多次,刚好quartz提供很好的解决方案。
集群分布式并发环境中使用QUARTZ定时任务调度,会在各个节点会上报任务,存到数据库中,执行时会从数据库中取出触发器来执行,如果触发器的名称和执行时间相同,则只有一个节点去执行此任务。
如果此节点执行失败,则此任务则会被分派到另一节点执行,中途也会自动检查失效的定时调度,发现不成功的,其他节点立马接过来继续完成定时任务。对应的定时任务调度表比较多,有11个。