`
53873039oycg
  • 浏览: 824901 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

动态配置Quartz定时任务的思路

阅读更多

     本文前置条件:Quartz 2.2.1, Spring 3.1以上版本,Quartz 2以下类似,只是具体类中对于配置动态任务写法不同。本文所说的动态是指把定时任务动态配置在数据库中。
     本文只是简单的写下思路,写的不好请见谅!

    主要思路如下:

      1)首先配置一个定时任务的类,TaskInfo,可以如下

     

/**
 * 定时任务信息
 * 
 */
public class TaskInfo {

	/** 任务id */
	private long jobId;

	/** 定时任务全路径 ,定时任务必须实现AbsTask eg com.task */
	private String jobClass;

	/** 任务名称 */
	private String jobName;

	/** 任务分组 */
	private String jobGroup;

	/** 任务状态 0禁用 1启用 2删除 */
	private String jobStatus;

	/** 任务运行时间表达式 */
	private String cronExpression;

	/** 任务描述 */
	private String desc;

	public long getJobId() {
		return jobId;
	}

	public void setJobId(long jobId) {
		this.jobId = jobId;
	}

	public String getJobClass() {
		return jobClass;
	}

	public void setJobClass(String jobClass) {
		this.jobClass = jobClass;
	}

	public String getJobName() {
		return jobName;
	}

	public void setJobName(String jobName) {
		this.jobName = jobName;
	}

	public String getJobGroup() {
		return jobGroup;
	}

	public void setJobGroup(String jobGroup) {
		this.jobGroup = jobGroup;
	}

	public String getJobStatus() {
		return jobStatus;
	}

	public void setJobStatus(String jobStatus) {
		this.jobStatus = jobStatus;
	}

	public String getCronExpression() {
		return cronExpression;
	}

	public void setCronExpression(String cronExpression) {
		this.cronExpression = cronExpression;
	}

	public String getDesc() {
		return desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

}

    2)要想动态配置,必须先配置一个后台定时任务在一定间隔内扫描表中的数据,且该后台定时任务不能重复配置,彻底点就是禁止改后台定时任务出现在表中

    3)为和后台定时任务区分,可以定义一个抽象类,具体类必须实现该类的方法。抽象类中的方法可以类似于 execute(long taskId),这样好处是可以在数据库中配置一张定时任务运行参数表,不配置参数表也行。

    4)具体功能的类实现抽象类

    5)由于在类中配置定时任务时候,创建JobDetail时候具体任务类必须实现Job接口,如下所示

   

JobDetail jobDetail = JobBuilder.newJob(TaskRegJob.class)
						.withIdentity(task.getJobName(), task.getJobGroup())
						.build();

    所以要一个实现了Job接口的定时任务作为中介,该定时任务以配置的Cron表达式运行,在

  

public void execute(JobExecutionContext context)
			throws JobExecutionException {

    方法内调用配置类的方法。

   6)用于数据库中的数据在后台定时任务运行间隔内可能被删除,可以能被新增或者修改,这里只有删除特殊点,新增只是在配置个TaskRegJob而已,修改则只需要改下定时任务的Cron表达式而已,删除的定时任务可能正在运行,这时候就要想个办法处理,本人认为可以这样处理:

   在TaskScanJob后台定时任务方法内先删除没有正在运行的定时任务,如何得到正在运行的定时任务可以这样做:

  

Scheduler scheduler = context.getScheduler();
		List runningJobs = null;
		try {
			runningJobs = scheduler.getCurrentlyExecutingJobs();
		} catch (SchedulerException e) {
			e.printStackTrace();
		}

    判断是否正在运行:

  

private static boolean isRunning(JobKey jobKey, List runningJobs)
			throws Exception {
		boolean rtn = false;
		for (Iterator iter = runningJobs.iterator(); iter.hasNext();) {
			JobExecutionContext item = (JobExecutionContext) iter.next();
			if ((item.getJobDetail().getKey().equals(jobKey))) {
				rtn = true;
				break;
			}
		}
		return rtn;
	}

    得到当前配置的所有定时任务:

  

GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
			try {
				Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
				for (JobKey jobKey : jobKeys) {

}

    删除定时任务:

   

boolean rtn = scheduler.deleteJob(jobKey);

    删除的定时任务在下次启动时生效

    在TaskRegJob定时任务中,使用taskId去数据库取定时任务信息,如果取不到说明该定时任务已经被删除,只是用于正在运行所以不满足运行完成条件没有被删除,这时候可以这样做:

  

try {
			taskId = data.getLong("TASK_ID");
			task = DB.getTaskById(taskId);
		} catch (Throwable ex) {
			log.error("任务执行异常,任务ID:" + taskId
					+ ",有可能的原因是,此任务已经加入调度,执行时此任务已经被删除", ex);
			try {
				log.info("准备删除当前任务:" + context.getJobDetail().getKey());
				context.getScheduler().deleteJob(
						context.getJobDetail().getKey());
			} catch (SchedulerException e) {
				throw new JobExecutionException("任务删除异常,任务ID:" + taskId
						+ ",有可能的原因是:", ex);
			}
			throw new JobExecutionException("任务执行异常,任务ID:" + taskId
					+ ",有可能的原因是,此任务已经加入调度,执行时此任务已经被删除", ex);
		}

    说明,得到当前定时任务配置的数据方法如下:

 

JobDataMap data = context.getJobDetail().getJobDataMap();

    这样做了之后,该定时任务就在下次启动时删除不会启动。

    得到当前正在运行的普通定时任务(非后台)方法如下:

  

for (iter = runningJobs.iterator(); iter.hasNext();) {
			JobExecutionContext item = (JobExecutionContext) iter.next();
			if ((item.getJobInstance() instanceof TaskRegJob)) {
				log.info("系统中有正在运行的任务,任务ID:"
						+ item.getJobDetail().getJobDataMap()
								.getLongValue("TASK_ID") + ",执行类:"
						+ item.getJobDetail().getJobClass().getName());
			}
}

   总结:

        本文所说的方法:主要是先配置一个后台定时任务扫描数据库,采用另一个定时任务以配置的时间间隔调用具体的方法达到动态配置定时任务的效果。

   主要缺点如下:

       1必须先配置一个后台定时任务。

       2具体类实现抽象类,一个具体类一次只能配置一个定时任务,可以多次配置。也就是说定时任务的方法是固定的。

       全文完,写的不好的地方请指教,有更好的方法请留言。 

分享到:
评论
1 楼 conishyy 2014-05-22  
很不错的文章。正是我需要的,不知道你实现了没?可不可以共享一份给我!谢谢!yangyao86@126.com

相关推荐

Global site tag (gtag.js) - Google Analytics