• 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获得真正要运行的任务并调用,同时,纪录任务状态以供管理。

  • 相关阅读:
    【校招】互联网公司校招找工作遇到的坑
    基于RF的轴承预测
    软件评价之中国人自己的编程软件——易语言
    小学数学算数出题程序
    入学到目前为止的学习状况
    关于软件工程课程的三个疑问
    tf.data.Dataset.from_tensor_slices作用
    Lock wait timeout exceeded; try restarting transaction 问题解析
    走进HashMap
    手写实现ArrayList & LinkedList
  • 原文地址:https://www.cnblogs.com/muyuge/p/6152801.html
Copyright © 2020-2023  润新知