SpringBoot:整合quartz实现定时任务-动态任务调度

一、需求说明

什么是动态任务调度?
在上一篇中,我们的Job,JobDetail,Trigger都是硬编码,写死在代码里的

这样不符合实际应用。
实现通过接口来创建、暂停、重启、删除、修改任务等操作。

二、实现动态调度

动态调度的相关功能,都是集中在Scheduler组件中。

1、开发步骤

  • 自定义Job类
  • 自定义任务Bean
    JobBean的作用,是为了,封装所有Job的公共特点,方便后续的Scheduler操作
public class JobBean {
	private String jobName;
	private String jobClass;
	private String cron;}
  • 编写操作所有Job的工具类
    JobUtils
    这里主要演示6个常用操作
    那么,具体的方法内部实现逻辑是,和上一篇顺序一样,JobDetail,Trigger(其中包含cron)
    然后,关联到Scheduler中。
import com.pzj.quartz.entity.JobBean;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;

public class JobUtils {
	public static void createJob(Scheduler scheduler, JobBean jobBean){
		Class<? extends Job> jobClass = null;
		JobDetail jobDetail = null;
		Trigger trigger  = null;
		try {
			jobClass = (Class<? extends Job>) Class.forName(jobBean.getJobClass());


			jobDetail = JobBuilder.newJob(jobClass)
					.withIdentity(jobBean.getJobName())	//唯一标识
					.storeDurably()		//持久化
					.usingJobData("count",1)	//		共享数据初始化
					.build();

			trigger = TriggerBuilder.newTrigger()
					.withIdentity(jobBean.getJobName()+"_trigger")		//设置唯一标识
					.forJob(jobDetail)		//关联创建的JobDetail
					.withSchedule(CronScheduleBuilder.cronSchedule(jobBean.getCron()))		//设置cron
					.build();
			scheduler.scheduleJob(jobDetail,trigger);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}

	}

	public static void pauseJob(Scheduler scheduler,String jobName){
		JobKey jobKey = JobKey.jobKey(jobName);
		try {
			scheduler.pauseJob(jobKey);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	public static void resumeJob(Scheduler scheduler,String jobName){
		JobKey jobKey = JobKey.jobKey(jobName);
		try {
			scheduler.resumeJob(jobKey);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	public static void deleteJob(Scheduler scheduler,String jobName){
		JobKey jobKey = JobKey.jobKey(jobName);
		try {
			scheduler.deleteJob(jobKey);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	public static void runOnceJob(Scheduler scheduler,String jobName){
		JobKey jobKey = JobKey.jobKey(jobName);
		try {
			scheduler.triggerJob(jobKey);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 本案例中,只是实现了cron的修改
	 * 另外一种实现思路就是,先删除旧的任务,在创建新任务。
	 * 实现对job的整体修改。
	 * @param scheduler
	 * @param jobBean
	 */
	public static void modifyJob(Scheduler scheduler,JobBean jobBean){
		//获取任务触发器的唯一标识
		TriggerKey triggerKey = TriggerKey.triggerKey(jobBean.getJobName() + "_trigger");
		//通过唯一标识获取旧的触发器对象
		try {
			CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
			//使用新cron表达式构建新的触发器
			String newCron = jobBean.getCron();
			CronTrigger newTriger = oldTrigger.getTriggerBuilder()
					.withSchedule(CronScheduleBuilder.cronSchedule(newCron))
					.build();
			//调度器更新任务的触发器
			scheduler.rescheduleJob(triggerKey,newTriger);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}

	}
}

然后,在controller接口中,分别调用util的具体方法即可。
这里要注意,每一个功能的测试,是简历在线创建了job任务的基础上进行的。

那么,到此,就实现了动态操作任务调度的效果。

2、出现的问题

在测试修改调度任务的时候,会出现一个问题
任务创建时,cron是2秒一次
修改成5秒一次。

会发现,18,19,20连续三秒都执行了一次。
在这里插入图片描述
问题描述:
这个问题,专业术语叫:misfire
当调度器执行 rescheduleJob(triggerKey,trigger)去更新某个triggerKey对应的新trigger时,会导致新trigger相关联的定时任务立即执行一次(暂停任务亦如此)。

解决办法:
就是在创建Job任务的时候,为CronScheduleBuilder设置MisfirePolicy即可。


    /**
     * 设置定时任务策略
     */
    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(JobBean job, CronScheduleBuilder cb)
            throws TaskException
    {
        switch (job.getMisfirePolicy())
        {
            case ScheduleConstants.MISFIRE_DEFAULT:
                return cb;
            case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
                return cb.withMisfireHandlingInstructionIgnoreMisfires();
            case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
                return cb.withMisfireHandlingInstructionFireAndProceed();
            case ScheduleConstants.MISFIRE_DO_NOTHING:
                return cb.withMisfireHandlingInstructionDoNothing();
            default:
                throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
                        + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);
        }
    }

三、总结

这种quartz框架的实现,就比较符合实际应用场景了。
可以通过接口去操作Job,这样更加的灵活。

但是,这里还没有实现数据库保存Job配置信息的功能,后面继续优化。

开发思路:
这种实现方式,不是通过config配置类来注入。
是通过new对象,创建所需组件。

依然是,Job,JobDetail,Trigger,Scheduler的顺序,依次创建并绑定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值