Quartz任务调度框架整理笔记

4 篇文章 0 订阅

介绍

Quartz是一个任务调度框架。

 

The key interfaces of the Quartz API are:

Scheduler -与调度程序交互的主要API。

Job -由希望由调度程序执行的组件实现的接口。真正执行的逻辑。

JobDetail- 用于定义作业的实例。

Trigger -定义执行给定Job的时间表的组件。

JobBuilder -用于定义/构建定义作业实例的JobDetail实例。

TriggerBuilder -用于定义/构建触发器实例。

 

一个调度程序的生命周期是由它为界的创作,通过SchedulerFactory和其通话关闭()方法。一旦创建了调度器接口,就可以使用添加,删除和列出作业和触发器,并执行其他与调度相关的操作(例如暂停触发器)。但是,调度程序实际上不会对任何触发器(执行作业)执行操作,直到它已使用start()方法启动。

 

关于name和group

JobDetail和Trigger都有name和group。

name是它们在这个sheduler里面的唯一标识。如果我们要更新一个JobDetail定义,只需要设置一个name相同的JobDetail实例即可。

group是一个组织单元,sheduler会提供一些对整组操作的API,比如scheduler.resumeJobs()。

 

 

Trigger

StartTime & EndTime

startTime和endTime指定的Trigger会被触发的时间区间。在这个区间之外,Trigger是不会被触发的。

优先级(Priority)

当scheduler比较繁忙的时候,可能在同一个时刻,有多个Trigger被触发了,但资源不足(比如线程池不足),优先级高的先执行。任何整数值都允许优先级为正数或负数。建议设定一个优先级区间比如1-10,避免你永远都不知道的最大优先级。

 

Misfire(错失触发)策略

类似的Scheduler资源不足的时候,或者机器崩溃重启等,有可能某一些Trigger在应该触发的时间点没有被触发,也就是MissFire了。这个时候Trigger需要一个策略来处理这种情况。每种Trigger可选的策略各不相同。

MisFire的触发是有一个阀值,这个阀值是配置在JobStore的。只有超过这个阀值,才会算MisFire。小于这个阀值,Quartz是会全部重新触发。

 

所有MisFire的策略实际上都是解答两个问题:

已经MisFire的任务还要重新触发吗?

如果发生MisFire,要调整现有的调度时间吗?

 

默认使用的时MISFIRE_INSTRUCTION_SMART_POLICY(智能策略):

如果是只执行一次的调度,使用MISFIRE_INSTRUCTION_FIRE_NOW,忽略已经MisFire的任务,并且立即执行调度。

如果是无限次的调度(repeatCount是无限的),使用MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT,将startTime设置当前时间,重新开始调度任务,忽略已经misfire的任务。

否则,使用MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT,在下一次调度时间点,重新开始调度任务,忽略已经misfire的任务。

具体参考:https://dzone.com/articles/quartz-scheduler-misfire

 

Calendar

用于补充Trigger的时间。可以排除或加入某一些特定的时间点。

以”每10秒执行一次“为例,我们想排除掉每分钟20秒这个时间点。这个时间,就可以用Calendar来实现。

CronCalendar cal = new CronCalendar("20 * * * * ?");
QuartzScheduleMgr.getInstance().addCalendar("cronCal", cal, false, false);
 
Trigger trigger = newTrigger().withIdentity("trigger", "group1") // 定义name/group
        .startNow()// 一旦加入scheduler,立即生效
        .modifiedByCalendar("cronCal")
        .withSchedule(cronSchedule("0/10 * * * * ?")) // 使用CronTrigger
        .build();       
 
// 定义一个JobDetail
JobDetail job = newJob(HelloQuartz.class) // 定义Job类为HelloQuartz类,这是真正的执行逻辑所在
        .withIdentity("job1", "group1") // 定义name/group
        .usingJobData("name", "quartz1") // 定义属性
        .build();
 
// 加入这个调度
QuartzScheduleMgr.scheduleJob(job, trigger);
 
// 启动之
QuartzScheduleMgr.start();

其他calender

HolidayCalendar。指定特定的日期,比如20140613。精度到天。

DailyCalendar。指定每天的时间段(rangeStartingTime,rangeEndingTime),格式是HH:MM[:SS[:mmm]]。也就是最大精度可以到毫秒。

WeeklyCalendar。指定每星期的星期几,可选值比如为java.util.Calendar.SUNDAY。精度是天。

MonthlyCalendar。指定每月的几号。可选值为1-31。精度是天

AnnualCalendar。指定每年的哪一天。使用方式如上例。精度是天。

CronCalendar。指定Cron表达式。精度取决于Cron表达式,也就是最大精度可以到秒。

 

CronTrigger

适合于更复杂的任务,它支持类型于LinuxCron的语法(并且更强大)。基本上它覆盖了SimpleTrigger、CalendarIntervalTrigger、DailyTimeIntervalTrigger的绝大部分能力。

cron格式说明:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/crontrigger.html

 

pom.xml
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

 

简单例子:

HelloQuartz.class

import java.util.Date;
 
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
 
public class HelloQuartz implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDetail detail = context.getJobDetail();
        String name = detail.getJobDataMap().getString("name");
        System.out.println("say hello to " + name + " at " + new Date());
    }
}

 

测试代码:

public void testStartSchedule() {
    try {
        // 创建scheduler
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
 
        Trigger trigger = newTrigger().withIdentity("trigger", "group1") // 定义name/group
                .startNow()// 一旦加入scheduler,立即生效
                .withSchedule(cronSchedule("0/10 * * * * ?")) // 使用CronTrigger
                .build();
 
        // 定义一个JobDetail
        JobDetail job = newJob(HelloQuartz .class) // 定义Job类为HelloQuartz类,这是真正的执行逻辑所在
                .withIdentity("job1", "group1") // 定义name/group
                .usingJobData("name", "quartz1") // 定义属性
                .build();
 
        // 加入这个调度
        scheduler.scheduleJob(job, trigger);
        // 删除相同key的trigger并保存新的trigger
        scheduler.rescheduleJob(trigger.getKey(), trigger);
 
        // 启动之
        scheduler.start();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
 
public void testUpdateSchedule() {
    try {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); 
        Trigger trigger = newTrigger().withIdentity("trigger", "group1") // 定义name/group
                .startNow()// 一旦加入scheduler,立即生效
                .withSchedule(cronSchedule("0/3 * * * * ?")) // 使用CronTrigger
                .build(); 
        // 更新trigger
        scheduler.rescheduleJob(trigger.getKey(), trigger);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
 
public void testPauseSchedule() {
    try {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobKey key = new JobKey("job1", "group1");
        scheduler.pauseJob(key);
        System.out.println("pause");
    } catch (SchedulerException e) {
        e.printStackTrace();
    }
}

 

job的并发性

@DisallowConcurrentExecution是一个注释,可以添加到Job类中,告诉Quartz不要同时执行给定作业定义(指给定作业类)的多个实例。如果运行时间大于Quartz周期,Quartz会让正在运行的job结束以后,立即执行下一个。

@PersistJobDataAfterExecution是一个注释,可以添加到Job类中,告诉Quartz在execute()方法成功完成后(不抛出异常)更新JobDetail的JobDataMap的存储副本,以便下次执行同一作业JobDetail接收更新的值而不是最初存储的值。像 @DisallowConcurrentExecution注解一样,这适用于作业定义实例,而不是作业类实例,尽管它决定让作业类携带该属性,因为它通常会影响类的编码方式(例如,“有状态'需要被执行方法内的代码明确'理解')。

如果使用@PersistJobDataAfterExecution注释,则应该强烈考虑同时使用 @DisallowConcurrentExecution注释,以避免同时执行同一作业的两个实例(JobDetail)时可能会混淆(竞争条件)哪些数据存储在哪里。

 

SchedulerListeners

SchedulerListeners与TriggerListener和JobListeners非常相似,只是它们接收Scheduler本身内的事件通知- 不一定与特定触发器或作业相关的事件。

与调度程序相关的事件包括:添加作业/触发器,删除作业/触发器,调度程序中出现严重错误,调度程序正在关闭的通知等。

scheduler.getListenerManager().addSchedulerListener(mySchedListener);

 

 
其他

job运行时暂停,不会影响当前运行。

 

文档:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/quick-start.html

中文博客:https://www.cnblogs.com/drift-ice/p/3817269.html

 

通用类,函数调用示例:https://blog.csdn.net/luka2008/article/details/8968170
 

 

 

Quart使用工具类
public class QuartzScheduleMgr {
	private static Scheduler scheduler = getInstance();

	/**
	 * 创建一个调度对象
	 * 
	 * @return
	 * @throws SchedulerException
	 */
	public static Scheduler getInstance() {
		if (scheduler == null) {
			SchedulerFactory sf = new StdSchedulerFactory();
			try {
				scheduler = sf.getScheduler();
			} catch (SchedulerException e) {
				e.printStackTrace();
			}
		}
		return scheduler;
	}

	/**
	 * 启动一个调度对象
	 * 
	 * @throws SchedulerException
	 */
	public static void start() throws SchedulerException {
		scheduler.start();
	}

	/**
	 * 检查调度是否启动
	 * 
	 * @return
	 * @throws SchedulerException
	 */
	public static boolean isStarted() throws SchedulerException {
		return scheduler.isStarted();
	}

	/**
	 * 关闭调度信息
	 * 
	 * @param waitForJobsToComplete
	 *            是否等待所有job执行完毕
	 * @throws SchedulerException
	 */
	public static void shutdown(boolean waitForJobsToComplete) throws SchedulerException {
		scheduler.shutdown(waitForJobsToComplete);
	}

	/**
	 * 添加调度的job信息
	 * 
	 * @param jobdetail
	 * @param trigger
	 * @return
	 * @throws SchedulerException
	 */
	public static void scheduleJob(JobDetail jobdetail, Trigger trigger) throws SchedulerException {
		if (!checkJobExists(jobdetail.getKey())) {
			scheduler.scheduleJob(jobdetail, trigger);
			updateTrigger(trigger);
		}
	}

	/**
	 * 暂停一个job任务
	 * 
	 * @param jobkey
	 * @throws SchedulerException
	 */
	public static void pauseJob(JobKey jobKey) throws SchedulerException {
		if (checkJobExists(jobKey)) {
			scheduler.pauseJob(jobKey);
		}
	}

	/**
	 * 恢复一个job任务
	 * 
	 * @param jobKey
	 * @throws SchedulerException
	 */
	public static void resumeJob(JobKey jobKey) throws SchedulerException {
		if (checkJobExists(jobKey)) {
			scheduler.resumeJob(jobKey);
		}
	}

	/**
	 * 删除job并取消其所有触发器
	 * 
	 * @param jobkey
	 * @return
	 * @throws SchedulerException
	 */
	public static boolean deleteJob(JobKey jobKey) throws SchedulerException {
		if (checkJobExists(jobKey)) {
			return scheduler.deleteJob(jobKey);
		}
		return false;
	}

	/**
	 * 检查job是否存在
	 * 
	 * @param jobKey
	 * @return
	 * @throws SchedulerException
	 */
	public static boolean checkJobExists(JobKey jobKey) throws SchedulerException {
		return scheduler.checkExists(jobKey);
	}

	/**
	 * 检查trigger是否存在
	 * 
	 * @param triggerKey
	 * @return
	 * @throws SchedulerException
	 */
	public static boolean checkTriggerExists(TriggerKey triggerKey) throws SchedulerException {
		return scheduler.checkExists(triggerKey);
	}

	/**
	 * 更新指定Trigger(确保新Trigger的key和旧的一致)
	 * 
	 * @param trigger
	 * @return
	 */
	public static Date updateTrigger(Trigger trigger) throws SchedulerException {
		if (checkTriggerExists(trigger.getKey())) {
			return scheduler.rescheduleJob(trigger.getKey(), trigger);
		}
		return null;
	}

	/**
	 * 更新指定job(确保新job的key和旧的一致)
	 * 
	 * @param job
	 * @throws SchedulerException
	 */
	public static void updateJob(JobDetail job) throws SchedulerException {
		if (checkJobExists(job.getKey())) {
			scheduler.addJob(job, true);
		}
	}

	/**
	 * 获取指定Job的所有Trigger
	 * 
	 * @param jobKey
	 * @return
	 * @throws SchedulerException
	 */
	@SuppressWarnings("unchecked")
	public static List<Trigger> getTriggersOfJob(JobKey jobKey) throws SchedulerException {
		return (List<Trigger>) scheduler.getTriggersOfJob(jobKey);
	}

	/**
	 * 获取指定group的所有JobKey
	 * 
	 * @param groupName
	 * @return
	 * @throws SchedulerException
	 */
	public static Set<JobKey> getJobByGroup(String groupName) throws SchedulerException {
		return scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName));
	}

	/**
	 * 获取所有job
	 * 
	 * @return
	 */
	public static Set<JobKey> getAllJobs() throws SchedulerException {
		List<String> groups = scheduler.getJobGroupNames();
		Set<JobKey> jobs = new HashSet<JobKey>();
		for (String groupName : groups) {
			jobs.addAll(getJobByGroup(groupName));
		}
		return jobs;
	}

	/**
	 * 获取当前正在运行的jobkey
	 * 
	 * @return
	 * @throws SchedulerException
	 */
	public static List<JobKey> getCurrentExcutingJobs() throws SchedulerException {
		List<JobKey> keys = new ArrayList<JobKey>();
		List<JobExecutionContext> jobContexts = scheduler.getCurrentlyExecutingJobs();
		for (JobExecutionContext context : jobContexts) {
			keys.add(context.getTrigger().getJobKey());
		}
		return keys;
	}

	/**
	 * 获取trigger状态
	 * 
	 * @param triggerKey
	 * @return
	 * @throws SchedulerException
	 */
	public static TriggerState getTriggerStatus(TriggerKey triggerKey) throws SchedulerException {
		// state的值代表该任务触发器的状态:
		// NONE -1 Trigger已经完成,且不会在执行,或者找不到该触发器,或者Trigger已经被删除
		// NORMAL 0 正常状态(运行或者就绪都是这个状态)
		// PAUSED 1 暂停状态
		// COMPLETE 2 触发器完成,但是任务可能还正在执行中
		// ERROR 3 错误
		// BLOCKED 4 线程阻塞状态
		return scheduler.getTriggerState(triggerKey);
	}
}

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值