程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用Quartz實現任務調度和調度管理

使用Quartz實現任務調度和調度管理

編輯:關於JAVA

Quartz是Java裡流行的一種開源任務調度框架。Quartz可以用來創建簡單或為運行十個,百個,甚至是好幾萬個Jobs這樣簡單復雜的日程表。Jobs可以做成標准的Java組件或 EJBs.本文會先大概介紹一下如何使用Quartz,然後重點是介紹實際項目裡,通過二次開發,增加任務調度的可管理性和異常處理,使它具備一定的商業任務調度框架的功能

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();

}

}

}

Quartz將任務和時間觸發分開,因此,你還需要指定時間觸發,通常采用Cron方式,如每天早上六點,也可以指定某個固定時間,如2008年8月8號等。

如以下即指定每天早上六點

CronTrigger cronTrigger = new CronTrigger("triggerName", "triggerGroup");

try {

CronExpression cexp = new CronExpression("0 6 * * * ");

cronTrigger.setCronExpression(cexp);

} catch (Exception e) {

e.printStackTrace();

}

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();

}

}

}

也可以增加Stop,Pause,Resume等Annotation,在此略過

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;

private String cron;

private String[] paramenters=null;

className,即為使用Annotation定義的任務。

cron即為Cron時間表達式

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獲得真正要運行的任務並調用,同時,紀錄任務狀態以供管理。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved