zoukankan      html  css  js  c++  java
  • Quartz入门到精通

    Quartz是Java里流行的一种开源任务调度框架。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样简单复杂的日程表。Jobs可以做成标准的Java组件或 EJBs。本文会先大概介绍一下如何使用Quartz,然后重点是介绍实际项目里,通过二次开发,增加任务调度的可管理性和异常处理,使它具备一定的商业任务调度框架的功能(参考此文:http://www.fluxcorp.com/products/flux/technical/quartz.html

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

        如果你觉得本文写的不够详细,可以参考这个文档http://www.ibm.com/developerworks/cn/java/j-quartz/index.html,或者直接去官方网站http://wiki.opensymphony.com/display/QRTZ1/Tutorial 了解更多信息。

    对于实际任务调度来说,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.batch.quartz.annotation.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", "joelli", 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获得真正要运行的任务并调用,同时,纪录任务状态以供管理。

  • 相关阅读:
    IXmlSerializable With WCFData Transfer in Service Contracts
    Difference Between XmlSerialization and BinarySerialization
    Using XmlSerializer (using Attributes like XmlElement , XmlAttribute etc ) Data Transfer in Service Contracts
    Introducing XML Serialization
    Version Tolerant Serialization
    Which binding is bestWCF Bindings
    Data Transfer in Service Contracts
    DataContract KnownTypeData Transfer in Service Contracts
    Using the Message ClassData Transfer in Service Contracts
    DataContract POCO SupportData Transfer in Service Contracts
  • 原文地址:https://www.cnblogs.com/hainange/p/6153225.html
Copyright © 2011-2022 走看看