使用Quartz实现任务调度和调度管理
导航:上一页|ASP | PHP | JSP | HTML | CSS | XHTML | aJAX | Ruby | JAVA | XML | Python | ColdFusion
Quartz是Java里流行的一种开源任务调度框架。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样简单复杂的日程表。Jobs可以做成标准的Java组件或EJBs。本文会先大概介绍一下如何使用Quartz,然后重点是介绍实际项目里,通过二次开发,增加任务调度的可管理性和异常处理,使它具备一定的商业任务调度框架的功能.
使用[ ]
execute方法[ ]
Quartz要求一个任务必须实现接口Job的execute方法,如下一个简单的Job:
import java.util.Date; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class SimpleJob implements Job { public SimpleJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { try { Thread.sleep(1000*20); } catch (InterruptedException e) { e.printStackTrace(); } } }
Cron方式[ ]
Quartz将任务和时间触发分开,因此,你还需要指定时间触发,通常采用Cron方式,如每天早上六点,也可以指定某个固定时间.
如以下即指定每天早上六点:
CronTrigger cronTrigger = new CronTrigger("triggerName", "triggerGroup"); try { CronExpression cexp = new CronExpression("0 6 * * * "); cronTrigger.setCronExpression(cexp); } catch (Exception e) { e.printStackTrace(); }
Scheduler类[ ]
Scheduler类负责将时间触发指定给JobDetail,简单的来说JobDetail封装了你的任务,并可以提供任务名,所属组,以及附加的一些参数,代码如下:
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
JobDetail job = new JobDetail("jobName", "groupName", SimpleJob.class);
Scheduler.scheduleJob(job, cronTrigger);
Job在被触发的时候,会通过反射实例化SimpleJob.class(因此你的Job必须有一个无参数的构造函数),并调用execute方法。
对于上列的SimpleJob,可以从execute方法输入参数context里获取一些属性,如任务名(如例子里的jobName),所在组(如:groupName).更重要的是,context里可以包含你指定的参数,如我们想让SimpleJob在运行的时候休眠时间为50秒,也可以这么写:
public void execute(JobExecutionContext context) throws JobExecutionException { try { int sleep = context.getJobDetail().getJobDataMap().getInt("sleep"); Thread.sleep(1000*sleep); } catch (InterruptedException e) { e.printStackTrace(); } }
参数Sleep将由调度框架传入,如下:
SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); JobDetail job = new JobDetail("job1", "group1", SimpleJob.class); job.getJobDataMap().put("sleep", 50); Scheduler.scheduleJob(job, trigger);
任务调度[ ]
对于实际任务调度来说,Quartz只是提供了基本功能,摆在我们面前的仍然有一些需求Quartz并没有内置。如
- 任务状态管理:需要查看当前有哪些任务在运行,历史上任务执行情况
- 异常处理:任务出现异常需要告警,或者手工强制执行。
- 任务依赖关系:任务A执行前,任务B必须执行成功。
以下我们将实现这些内容,首先,我们使用Annotation定义任务,如下一个任务
public class SimpleJob{ @Run public void doit() { try { Thread.sleep(1000*20); } catch (InterruptedException e) { e.printStackTrace(); } } }
Annoatoin定义如下:
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Run { }
我倾向于用Annotation而不是接口是因为Annotation更灵活。比如一些传统的任务调度程序入口就是static main(String[] args)方法,只需要用Annotation指示一下,而且,Annoation扩展性也好,如给一个任务命名,可以扩展Annoatoin实现,如下;
@Run(name="com.simpleJob") public void doit(String[] args) { try { Thread.sleep(1000*20); } catch (InterruptedException e) { e.printStackTrace(); } } }
用Annoaton定义任务后,这任务如何加入到Quartz框架里?可以定义个新的Wrapper类,叫着JobWrapper,它是一个标准的Quartz的任务,即实现了接口Job ,当Quartz调用次类的时候,此类会根据DataMap里的BatchDescription描述来调用正确的任务。
BatchDescription很简单,有三个属性
- private String className;className,即为使用Annotation定义的任务。
- private String cron;cron即为Cron时间表达式
- private String[] paramenters=null;paramenters 为任务的参数
JobWrapper是系统的核心,Quartz调用它,它转而调用JobDescription描述的任务,多了这一层,就能很好的管理Job状态,做一些异常处理等更为复杂的任务管理了。代码如下:
public JobWrapper() { // be used to persist to database or other features id = System.currentTimeMillis()+"_"+Thread.currentThread().getId(); stateChanged(id,JobStatus.INIT); JobManager.instance().reg(this) } public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap data = context.getJobDetail().getJobDataMap(); desc = (BatchDescription)data.get("JobData"); runParameters = desc.getParamenter(); try { realJob = Class.forName(desc.getClassName()).newInstance(); } catch (Exception e1) { e1.printStackTrace(); return ; } //look for the method with annotation Run runMethod = getRunMethod(); //reg it ,then can get it later try { stateChanged(id,JobStatus.RUNNING) runMethod.invoke(realJob, runParameters); stateChanged(id,JobStatus.RUNNING) } catch (IllegalArgumentException e) { //ignore e.printStackTrace(); return ; } catch (IllegalAccessException e) { //ignore e.printStackTrace(); return ; } catch (InvocationTargetException e) { Throwable ex = e.getTargetException(); // handle exception ,now just put this exception to some queue stateChanged(id,JobStatus.EXCEPTOIN,ex.getMessage()) ; return ; } } private void stateChanged(String id,JobStatus,String msg){ //此方法可以用来存储任务到数据库,以供查看状态,如: JobDao.save(id,name,JobStatus,msg,new Date()); } private Method getRunMethod() { // first look up the method with run annotation,if not find,check the main method if(runMethod!=null){ return runMethod; } Method[] methods = realJob.getClass().getDeclaredMethods(); for(Method m:methods) { Annotation[] annos = m.getAnnotations(); if(annos!=null&&annos.length!=0) { for(Annotation anno:annos) { //System.out.println(anno.annotationType()); if(anno.annotationType()==com.joelli.Run.class) { return m; } } } } // look for the method public static void main,let ignore it return null; }
最后,然我们看看Quartz如何调用此类
//定义一个任务,类为com.javamonkey.SimpleJob,参数为Null
BatchDescription batchDesc= new BatchDescription("com.javamonkey.SimpleJob","15 0/2 * * * ?",null); JobDetail job1 = new JobDetail(batchDesc.getClassName()+ ".Job", "group", JobWrapper.class); job1.getJobDataMap().put("JobData", batchDesc); CronTrigger cronTrigger = new CronTrigger(batchDesc.getClassName()+ ".Trigger"); CronExpression cexp = new CronExpression("0 6 * * * "); cronTrigger.setCronExpression(cexp); Scheduler.scheduleJob(job1, cronTrigger);
如上代码,Quartz在时间触发后,会实例话JobWrapper.class,并调用Execute方法。JobWrapper会根据BatchDescription获得真正要运行的任务并调用,同时,纪录任务状态以供管理。
相关条目[ ]
参考来源[ ]