精华内容
下载资源
问答
  • Quartz 定时任务调度框架

    万次阅读 2020-10-06 19:40:36
    定时任务调度框架 Quartz 入门案例: 依赖: <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> ...

    Quartz 定时任务调度框架

    入门案例:

    • 依赖:
    <dependency>
    	<groupId>org.quartz-scheduler</groupId>
    	<artifactId>quartz</artifactId>
    	<version>2.3.2</version>
    </dependency>
    <dependency>
    	<groupId>org.quartz-scheduler</groupId>
    	<artifactId>quartz-jobs</artifactId>
    	<version>2.3.2</version>
    </dependency>
    
    • Job类:
    package com.blu.job;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    
    public class HelloJob implements Job{
    
    	@Override
    	public void execute(JobExecutionContext context) throws JobExecutionException {
    		Date date = new Date();
    		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String dateString = dateFormat.format(date);
    		System.out.println("当前时间:"+dateString);
    	}
    
    }
    
    • 测试类:
    package com.blu.test;
    
    import org.quartz.JobBuilder;
    import org.quartz.JobDetail;
    import org.quartz.Scheduler;
    import org.quartz.SimpleScheduleBuilder;
    import org.quartz.Trigger;
    import org.quartz.TriggerBuilder;
    import org.quartz.impl.StdSchedulerFactory;
    import com.blu.job.HelloJob;
    
    public class HelloScheduleTest {
    
    	public static void main(String[] args) throws Exception {
    		//调度器(Scheduler),从工厂中获取
    		Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    		//任务示例(JobDetail)
    		JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "JobGroup1").build();
    		//触发器(Trigger)
    		Trigger trigger = TriggerBuilder.newTrigger()
    					.withIdentity("trigger1", "TriggerGroup1")
    					//立即启动触发器
    					.startNow()
    					//每5s重复执行一次
    					.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5))
    					.build();
    		//让调度器关联任务和触发器
    		scheduler.scheduleJob(jobDetail,trigger);
    		scheduler.start();
    	}
    
    }
    
    • 运行效果:每5s打印一次时间
    当前时间:2020-10-06 19:34:09
    当前时间:2020-10-06 19:34:14
    当前时间:2020-10-06 19:34:19
    当前时间:2020-10-06 19:34:24
    当前时间:2020-10-06 19:34:29
    当前时间:2020-10-06 19:34:34
    当前时间:2020-10-06 19:34:39
    当前时间:2020-10-06 19:34:44
    当前时间:2020-10-06 19:34:49
    当前时间:2020-10-06 19:34:54
    当前时间:2020-10-06 19:34:59
    当前时间:2020-10-06 19:35:04
    

    扩展

    • Job类:
    package com.blu.job;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.quartz.Job;
    import org.quartz.JobDataMap;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.quartz.JobKey;
    import org.quartz.Trigger;
    
    public class HelloJob implements Job{
    	
    	private String message;
    	
    	public void setMessage(String message) {
    		this.message = message;
    	}
    
    	@Override
    	public void execute(JobExecutionContext context) throws JobExecutionException {
    		Date date = new Date();
    		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String dateString = dateFormat.format(date);
    		//获取JobDetail的内容
    		JobKey jobKey = context.getJobDetail().getKey();
    		System.out.println(jobKey.getName());
    		System.out.println(jobKey.getGroup());
    		System.out.println(context.getJobDetail().getJobClass().getName());
    		//从JobDetail对象中获取JobDataMap的数据
    		JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
    		String jobDataMessage = jobDataMap.getString("message");
    		System.out.println(jobDataMessage);
    		//从Trigger对象中获取JobDataMap的数据
    		JobDataMap  triggerDataMap = context.getTrigger().getJobDataMap();
    		String triggerDataMessage = triggerDataMap.getString("message");
    		System.out.println(triggerDataMessage);
    		//获取Trigger中的内容
    		Trigger trigger = context.getTrigger();
    		System.out.println(trigger.getKey().getName());
    		System.out.println(trigger.getKey().getGroup());
    		System.out.println(trigger.getKey().getClass());
    		//初始化job类示例对象时会自动调用setter方法为属性赋值
    		//当trigger与JobDetail设置了重名属性时,setter方法获取的值就是trigger设置的属性值                                                                       
    		System.out.println("通过setter直接获取数据:"+message);
    		//获取其他内容
    		System.out.println("当前任务执行时间"+dateFormat.format(context.getFireTime()));
    		System.out.println("下次任务的执行时间"+dateFormat.format(context.getNextFireTime()));
    		System.out.println("当前时间:"+dateString);
    	}
    
    }
    
    • 测试类:
    package com.blu.test;
    
    import org.quartz.JobBuilder;
    import org.quartz.JobDetail;
    import org.quartz.Scheduler;
    import org.quartz.SimpleScheduleBuilder;
    import org.quartz.Trigger;
    import org.quartz.TriggerBuilder;
    import org.quartz.impl.StdSchedulerFactory;
    
    import com.blu.job.HelloJob;
    
    public class HelloScheduleTest {
    
    	public static void main(String[] args) throws Exception {
    		//调度器(Scheduler),从工厂中获取
    		Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    		//任务示例(JobDetail)
    		JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
    					.withIdentity("job1", "JobGroup1")
    					.usingJobData("message","jobDetail传递的值")
    					.build();
    //		System.out.println(jobDetail.getKey().getName());
    //		System.out.println(jobDetail.getKey().getGroup());
    //		System.out.println(jobDetail.getKey().getClass());
    		//触发器(Trigger)
    		Trigger trigger = TriggerBuilder.newTrigger()
    					.withIdentity("trigger1", "TriggerGroup1")
    					//立即启动触发器
    					.startNow()
    					//每5s重复执行一次
    					.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5))
    					.usingJobData("message","trigger传递的值")
    					.build();
    		//让调度器关联任务和触发器
    		scheduler.scheduleJob(jobDetail,trigger);
    		scheduler.start();
    	}
    
    }
    
    • 运行结果
    job1
    JobGroup1
    com.blu.job.HelloJob
    jobDetail传递的值
    trigger传递的值
    trigger1
    TriggerGroup1
    class org.quartz.TriggerKey
    通过setter直接获取数据:trigger传递的值
    当前任务执行时间2020-10-08 13:52:33
    下次任务的执行时间2020-10-08 13:52:38
    当前时间:2020-10-08 13:52:33
    
    job1
    JobGroup1
    com.blu.job.HelloJob
    jobDetail传递的值
    trigger传递的值
    trigger1
    TriggerGroup1
    class org.quartz.TriggerKey
    通过setter直接获取数据:trigger传递的值
    当前任务执行时间2020-10-08 13:52:38
    下次任务的执行时间2020-10-08 13:52:43
    当前时间:2020-10-08 13:52:38
    
    job1
    JobGroup1
    com.blu.job.HelloJob
    jobDetail传递的值
    trigger传递的值
    trigger1
    TriggerGroup1
    class org.quartz.TriggerKey
    通过setter直接获取数据:trigger传递的值
    当前任务执行时间2020-10-08 13:52:43
    下次任务的执行时间2020-10-08 13:52:48
    当前时间:2020-10-08 13:52:43
    

    @PersistJobDataAfterExecution 有状态的Job和无状态的Job:

    每次执行任务时都会创建一个新的Job实例

    默认的无状态Job每次调用时都会创建一个新的JobDataMap

    而有状态的Job在多次Job调用期间可以在JobDataMap中存储一些状态信息

    • Job类:
    package com.blu.job;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    
    @PersistJobDataAfterExecution
    public class HiJob implements Job{
    	
    	private Integer count;
    
    	public void setCount(Integer count) {
    		this.count = count;
    	}
    
    	@Override
    	public void execute(JobExecutionContext context) throws JobExecutionException {
    		
    		Date date = new Date();
    		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		System.out.println("当前时间:"+ dateFormat.format(date));
    		
    		//累加count,输出count,存入JobDataMap
    		count++;
    		System.out.println(count);
    		context.getJobDetail().getJobDataMap().put("count", count);
    		
    	}
    	
    }
    
    • 测试类:
    package com.blu.test;
    
    import org.quartz.JobBuilder;
    import org.quartz.JobDetail;
    import org.quartz.Scheduler;
    import org.quartz.SimpleScheduleBuilder;
    import org.quartz.Trigger;
    import org.quartz.TriggerBuilder;
    import org.quartz.impl.StdSchedulerFactory;
    
    import com.blu.job.HiJob;
    
    public class HiTest {
    
    	public static void main(String[] args) throws Exception {
    		Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    		JobDetail jobDetail = JobBuilder.newJob(HiJob.class)
    								.usingJobData("count",0)
    								.build();
    		
    		Trigger trigger = TriggerBuilder.newTrigger()
    								.startNow()
    								.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5))
    								.build();
    		
    		scheduler.scheduleJob(jobDetail,trigger);
    		scheduler.start();
    				
    	}
    	
    }
    

    不加 @PersistJobDataAfterExecution 注解时是无状态Job,运行效果:

    当前时间:2020-10-09 11:03:53
    1
    当前时间:2020-10-09 11:03:58
    1
    当前时间:2020-10-09 11:04:03
    1
    当前时间:2020-10-09 11:04:08
    1
    当前时间:2020-10-09 11:04:13
    1
    当前时间:2020-10-09 11:04:18
    1
    

    加上 @PersistJobDataAfterExecution 注解后是有状态Job,运行效果:

    当前时间:2020-10-09 11:06:25
    1
    当前时间:2020-10-09 11:06:30
    2
    当前时间:2020-10-09 11:06:35
    3
    当前时间:2020-10-09 11:06:40
    4
    当前时间:2020-10-09 11:06:45
    5
    当前时间:2020-10-09 11:06:50
    6
    

    其他注解:

    @DisallowConcurrentExecution

    默认的情况下,无论上一次任务是否结束或者完成,只要规定的时间到了,那么下一次就开始。

    使用 @DisallowConcurrentExecution 注解可以保证上一次任务成功结束后,下一次任务才能开始

    展开全文
  • 任务调度框架

    2015-11-03 21:00:33
    Java任务调度框架Quartz教程实例 转载:原文地址:http://blog.csdn.net/yuebinghaoyuan/article/details/9045471 分类: 【J2SE】2013-06-07 11:01 9187人阅读 评论(5) 收藏 举报 ...
     

    Java任务调度框架Quartz教程实例


    转载:原文地址:http://blog.csdn.net/yuebinghaoyuan/article/details/9045471
    分类: 【J2SE】 9187人阅读 评论(5) 收藏 举报
       
     介绍
     Quartz is a full-featured, open source job scheduling service that can be integrated with, or used along side virtually any Java application - from the smallest stand-alone application to the largest e-commerce system. Quartz can be used to create simple or complex schedules for executing tens, hundreds, or even tens-of-thousands of jobs; 
     Quartz框架是一个全功能、开源的任务调度服务,可以集成几乎任何的java应用程序—从小的单片机系统到大型的电子商务系统。Quartz可以执行上千上万的任务调度。

     核心概念
     Quartz核心的概念scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节
     
     Job任务:其实Job是接口,其中只有一个execute方法:
     package org.quartz;
    public abstract interface Job
    {
      public abstract void execute(JobExecutionContext paramJobExecutionContext)
        throws JobExecutionException;
    }
     我们开发者只要实现此接口,实现execute方法即可。把我们想做的事情,在execute中执行即可。
     JobDetail:任务细节,Quartz执行Job时,需要新建个Job实例,但是不能直接操作Job类,所以通过JobDetail来获取Job的名称、描述信息。
     Trigger触发器:执行任务的规则;比如每天,每小时等。
     一般情况使用SimpleTrigger,和CronTrigger,这个触发器实现了Trigger接口。
     对于复杂的时间表达式来说,比如每个月15日上午几点几分,使用CronTrigger
     对于简单的时间来说,比如每天执行几次,使用SimpleTrigger
     scheduler任务调度:是最核心的概念,需要把JobDetail和Trigger注册到scheduler中,才可以执行。

     具体执行步骤:
     下载相应的jar包:http://www.quartz-scheduler.org/
     注意:
     不同的版本的jar包,具体的操作不太相同,但是思路是相同的;比如1.8.6jar包中,JobDetail是个类,直接通过构造方法与Job类关联。SimpleTrigger和CornTrigger是类;在2.0.2jar包中,JobDetail是个接口,SimpleTrigger和CornTrigger是接口
     不同版本测试:
     1.8.6jar包:  
    1. package com.test;  
    2.   
    3. import java.util.Date;  
    4.   
    5. import org.quartz.Job;  
    6. import org.quartz.JobExecutionContext;  
    7. import org.quartz.JobExecutionException;  
    8. /**  
    9.  * 需要执行的任务  
    10.  * @author lhy  
    11.  *  
    12.  */  
    13. public class MyJob implements Job {  
    14.   
    15.     @Override  
    16.     //把要执行的操作,写在execute方法中  
    17.     public void execute(JobExecutionContext arg0) throws JobExecutionException {  
    18.         System.out.println("测试Quartz"+new Date());  
    19.     }  
    20. }  
     使用SimpleTrigger触发器
    1. package com.test;  
    2.   
    3. import java.util.Date;  
    4.   
    5. import org.quartz.JobDetail;  
    6. import org.quartz.Scheduler;  
    7. import org.quartz.SchedulerException;  
    8. import org.quartz.SchedulerFactory;  
    9. import org.quartz.SimpleTrigger;  
    10. import org.quartz.impl.StdSchedulerFactory;  
    11.   
    12. /**  
    13.  * 调用任务的类  
    14.  * @author lhy  
    15.  *  
    16.  */  
    17. public class SchedulerTest {  
    18.    public static void main(String[] args) {  
    19.      
    20.      //通过schedulerFactory获取一个调度器  
    21.        SchedulerFactory schedulerfactory=new StdSchedulerFactory();  
    22.        Scheduler scheduler=null;  
    23.        try{  
    24. //      通过schedulerFactory获取一个调度器  
    25.            scheduler=schedulerfactory.getScheduler();  
    26.              
    27. //       创建jobDetail实例,绑定Job实现类  
    28. //       指明job的名称,所在组的名称,以及绑定job类  
    29.            JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);  
    30.              
    31. //       定义调度触发规则,比如每1秒运行一次,共运行8次  
    32.            SimpleTrigger simpleTrigger=new SimpleTrigger("simpleTrigger","triggerGroup");  
    33. //       马上启动  
    34.            simpleTrigger.setStartTime(new Date());  
    35. //       间隔时间  
    36.            simpleTrigger.setRepeatInterval(1000);  
    37. //       运行次数  
    38.            simpleTrigger.setRepeatCount(8);  
    39.              
    40. //       把作业和触发器注册到任务调度中  
    41.            scheduler.scheduleJob(jobDetail, simpleTrigger);  
    42.              
    43. //       启动调度  
    44.            scheduler.start();  
    45.              
    46.              
    47.        }catch(SchedulerException e){  
    48.            e.printStackTrace();  
    49.        }  
    50.          
    51. }  
    52. }  
     若使用CornTrigger触发器: 
    1. package com.test;  
    2.   
    3. import java.util.Date;  
    4.   
    5. import org.quartz.CronTrigger;  
    6. import org.quartz.JobDetail;  
    7. import org.quartz.Scheduler;  
    8. import org.quartz.SchedulerException;  
    9. import org.quartz.SchedulerFactory;  
    10. import org.quartz.SimpleTrigger;  
    11. import org.quartz.impl.StdSchedulerFactory;  
    12.   
    13. /**  
    14.  * 调用任务的类  
    15.  * @author lhy  
    16.  *  
    17.  */  
    18. public class CronTriggerTest {  
    19.    public static void main(String[] args) {  
    20.      
    21.      //通过schedulerFactory获取一个调度器  
    22.        SchedulerFactory schedulerfactory=new StdSchedulerFactory();  
    23.        Scheduler scheduler=null;  
    24.        try{  
    25. //      通过schedulerFactory获取一个调度器  
    26.            scheduler=schedulerfactory.getScheduler();  
    27.              
    28. //       创建jobDetail实例,绑定Job实现类  
    29. //       指明job的名称,所在组的名称,以及绑定job类  
    30.            JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);  
    31.              
    32. //       定义调度触发规则,每天上午10:15执行  
    33.            CronTrigger cornTrigger=new CronTrigger("cronTrigger","triggerGroup");  
    34. //       执行规则表达式  
    35.            cornTrigger.setCronExpression("0 15 10 * * ? *");  
    36. //       把作业和触发器注册到任务调度中  
    37.            scheduler.scheduleJob(jobDetail, cornTrigger);  
    38.              
    39. //       启动调度  
    40.            scheduler.start();  
    41.              
    42.              
    43.        }catch(Exception e){  
    44.            e.printStackTrace();  
    45.        }  
    46.          
    47. }  
    48. }  
      对于2.0.2jar包如下:
      其中的job类不变,主要是调度类如下:
    1. package com.test;  
    2.   
    3. import java.util.Date;  
    4.   
    5. import org.quartz.CronScheduleBuilder;  
    6. import org.quartz.JobBuilder;  
    7. import org.quartz.JobDetail;  
    8. import org.quartz.Scheduler;  
    9. import org.quartz.SchedulerException;  
    10. import org.quartz.SchedulerFactory;  
    11. import org.quartz.SimpleScheduleBuilder;  
    12. import org.quartz.Trigger;  
    13. import org.quartz.TriggerBuilder;  
    14. import org.quartz.impl.StdSchedulerFactory;  
    15.   
    16. /**  
    17.  * 调用任务的类  
    18.  * @author lhy  
    19.  *  
    20.  */  
    21. public class SchedulerTest {  
    22.    public static void main(String[] args) {  
    23.      
    24.      //通过schedulerFactory获取一个调度器  
    25.        SchedulerFactory schedulerfactory=new StdSchedulerFactory();  
    26.        Scheduler scheduler=null;  
    27.        try{  
    28. //      通过schedulerFactory获取一个调度器  
    29.            scheduler=schedulerfactory.getScheduler();  
    30.              
    31. //       创建jobDetail实例,绑定Job实现类  
    32. //       指明job的名称,所在组的名称,以及绑定job类  
    33.            JobDetail job=JobBuilder.newJob(MyJob.class).withIdentity("job1", "jgroup1").build();  
    34.            
    35.              
    36. //       定义调度触发规则  
    37.              
    38. //      使用simpleTrigger规则  
    39. //        Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")  
    40. //                        .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withRepeatCount(8))  
    41. //                        .startNow().build();  
    42. //      使用cornTrigger规则  每天10点42分  
    43.               Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")  
    44.               .withSchedule(CronScheduleBuilder.cronSchedule("0 42 10 * * ? *"))  
    45.               .startNow().build();   
    46.              
    47. //       把作业和触发器注册到任务调度中  
    48.            scheduler.scheduleJob(job, trigger);  
    49.              
    50. //       启动调度  
    51.            scheduler.start();  
    52.              
    53.              
    54.        }catch(Exception e){  
    55.            e.printStackTrace();  
    56.        }  
    57.          
    58. }  
    59. }  
      上述demo下载:1.8版本demo下载
                    2.0版本demo下载
      
      对于CornExpress讲解如下: 
    字段   允许值   允许的特殊字符    
    秒    0-59    , - * /    
    分    0-59    , - * /    
    小时    0-23    , - * /    
    日期    1-31    , - * ? / L W C    
    月份    1-12 或者 JAN-DEC    , - * /    
    星期    1-7 或者 SUN-SAT    , - * ? / L C #    
    年(可选)    留空, 1970-2099    , - * /    
      
    表达式   意义    
    "0 0 12 * * ?"    每天中午12点触发    
    "0 15 10 ? * *"    每天上午10:15触发    
    "0 15 10 * * ?"    每天上午10:15触发    
    "0 15 10 * * ? *"    每天上午10:15触发    
    "0 15 10 * * ? 2005"    2005年的每天上午10:15触发    
    "0 * 14 * * ?"    在每天下午2点到下午2:59期间的每1分钟触发    
    "0 0/5 14 * * ?"    在每天下午2点到下午2:55期间的每5分钟触发     
    "0 0/5 14,18 * * ?"    在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发     
    "0 0-5 14 * * ?"    在每天下午2点到下午2:05期间的每1分钟触发    
    "0 10,44 14 ? 3 WED"    每年三月的星期三的下午2:10和2:44触发    
    "0 15 10 ? * MON-FRI"    周一至周五的上午10:15触发    
    "0 15 10 15 * ?"    每月15日上午10:15触发    
    "0 15 10 L * ?"    每月最后一日的上午10:15触发    
    "0 15 10 ? * 6L"    每月的最后一个星期五上午10:15触发      
    "0 15 10 ? * 6L 2002-2005"    2002年至2005年的每月的最后一个星期五上午10:15触发    
    "0 15 10 ? * 6#3"    每月的第三个星期五上午10:15触发     
      
    特殊字符   意义    
    *    表示所有值;    
    ?    表示未说明的值,即不关心它为何值;    
    -    表示一个指定的范围;    
    ,    表示附加一个可能值;    
    /    符号前表示开始时间,符号后表示每次递增的值;    
    L("last")    ("last") "L" 用在day-of-month字段意思是 "这个月最后一天";用在 day-of-week字段, 它简单意思是 "7" or "SAT"。 如果在day-of-week字段里和数字联合使用,它的意思就是 "这个月的最后一个星期几" – 例如: "6L" means "这个月的最后一个星期五". 当我们用“L”时,不指明一个列表值或者范围是很重要的,不然的话,我们会得到一些意想不到的结果。    
    W("weekday")    只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第16天即周一触发;如果这个月第15天是周二,那么就在触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日。     
    #    只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用"6#3"指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。     
    C    指和calendar联系后计算过的值。例:在day-of-month 字段用“5C”指在这个月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在这周日或之后包括calendar的第一天  
    展开全文
  • 分布式任务调度框架开发
  • 任务调度框架Quartz

    2013-10-16 19:55:22
    任务调度框架Quartz, 任务调度框架Quartz, 任务调度框架Quartz
  • Quartz,水晶、石英,一个简单朴素有美丽的名字,在Java程序界,Quartz大名鼎鼎,很多Java应用几乎都集成或构建了一个定时任务调度系统,Quartz是一个定时任务调度框架。 何为定时任务调度框架?简而言之,它可以...

    Quartz,水晶、石英,一个简单朴素有美丽的名字,在Java程序界,Quartz大名鼎鼎,很多Java应用几乎都集成或构建了一个定时任务调度系统,Quartz是一个定时任务调度框架。

    何为定时任务调度框架?简而言之,它可以领会我们的意图在未来某个时刻做我们想要做的事情,比如,女友生日那天定时发送短信讨好下(当然,除此之外,你还要买买买…)。

    (本文章分享在CSDN平台,更多精彩请阅读 东陆之滇的csdn博客:http://blog.csdn.net/zixiao217)

    我们的应用程序有些定时任务(例如想在凌晨十二点半统计某个互联网金融公司一款借款APP前一天的借款、还款以及逾期情况)需要在指定时间内执行或者周期性执行某个任务(比如每月最后一天统计这个月的财务报表给财务部门等),这时候我们就需要用到任务调度框架了。Quartz正是一个炙手可热的任务调度框架,它简单易上手,并且可以与Spring集成(这才是重点)。

    现在,我们带着疑问开始认识Quartz…

    基本问题

    Quartz是什么?

    Quartz是一个任务调度框架(库),它几乎可以集成到任何应用系统中。术语”job schedule”似乎为不同的人提供了不同的想法。当你阅读该教程时,你应该能够得到一个坚定的想法关于我们使用这个术语时表达含义,但总之,作业调度是负责执行(或通知)其他软件组件在预定时间执行的服务组件。

    Quartz是非常灵活的,并包含多个使用范例,它们可以单独或一起使用,以实现您所期望的行为,并使您能够以最“自然”的方式来编写您的项目的代码。

    Quartz是非常轻量级的,只需要非常少的配置 —— 它实际上可以被跳出框架来使用,如果你的需求是一些相对基本的简单的需求的话。

    Quartz具有容错机制,并且可以在重启服务的时候持久化(”记忆”)你的定时任务,你的任务也不会丢失。

    虽然通过schedule可以简单实现一些系统任务定时执行,当您学习如何使用它来驱动应用程序的业务流程的流程时,Quartz的全部潜力是可以实现的。

    Quartz又不是什么?

    Quartz不是一个任务队列——虽然它确实可以在一些小规模应用中合理的作为一个任务队列来使用。

    Quartz不是一个网格计算引擎——虽然在某些小规模应用中,这样做确实可以达到应用的要求(定时计算、统计一些数据)。

    Quartz不是一个提供给业务人员的执行服务——它是一个库,很容易集成到应用程序中去做一些未来某时刻可能会一直循环执行的相关的任务。

    从一个软件组件角度来看Quartz是什么?

    Quartz是一个很轻量级的java库,几乎包含了所有定时的功能。主要接口是Schedule,提供了一些简单的操作:安排任务或取消任务,启动或者停止任务。

    如果你想在应用中使用Quartz,应该实现Job接口,包含了一个execute()方法。如果你想在一个任务执行时间到了的时候通知你,组件应该实现TriggerListener 或者JobListener 接口。

    Quartz任务可以在你的应用中启动和执行,可以作为一个独立的应用程序(通过RMI接口),也可是在一个J2EE应用中执行。

    为什么不简单的使用just use java.util.Timer就行了呢?

    从JDK1.3开始,Java通过java.util.Timerjava.util.TimerTask可以实现定时器。为什么要使用Quartz而不是使用Java中的这些标准功能呢?

    原因太多了,这里列举几个:

    • Timers没有持久化机制.
    • Timers不灵活 (只可以设置开始时间和重复间隔,不是基于时间、日期、天等(秒、分、时)的)
    • Timers 不能利用线程池,一个timer一个线程
    • Timers没有真正的管理计划

    还有什么是可以替代Quartz的?

    商业上,你还可以使用 Flux scheduler

    其他问题

    Quartz可以运行多少任务?

    这是一个很难回答的问题…答案基本上是“它取决于使用情况”
    我知道你讨厌这样的答案,所以这里的一些信息:

    首先,你使用的JobStore扮演着一个重要的因素。基于RAM的JobStore(100x)比基于JDBC的JobStore更快近乎100倍。JDBC JobStore速度几乎完全取决于您的数据库的连接速度,使用的数据库系统,以及数据库运行的硬件设备,Quartz几乎全部时间花在数据库上了。当然RAMJobStore存储多少JobTriggers也是有限制的,你使用的空间肯定比数据库的硬盘空间要小了。你可以查看“如何提升JDBC-JobStore的性能”的问题。

    因此,限制JobTriggers可以存储或监听的数量的因素是存储空间的大小(RAM的数量和磁盘空间大小)。

    关于通过RMI使用Quartz的问题

    RMI是有问题的,特别是你如果不清楚通过RMI机制时类是如何加载的话。强烈建议读读所有关于RMI的java API。强烈建议您阅读以下的参考资料:
    一个关于RMI和代码库的极好描述: http://www.kedwards.com/jini/codebase.html。很重要的一点是要意识到“代码”是由客户端使用!

    关于安全管理器的一些信息: http://gethelp.devx.com/techtips/java_pro/10MinuteSolutions/10min0500.asp.
    重要的一点:
    RMI的类装载器将不会从远程位置下载任何类如果没有设置安全管理器的话。

    关于Job的一些问题

    如何控制Job的实例?

    可以查看org.quartz.spi.JobFactoryorg.quartz.Scheduler.setJobFactory(..) 的方法。

    当一个Job完成并移除之后,还能保存吗?

    设置属性:JobDetail.setDurability(true)——当job不再有trigger引用它的时候,Quartz也不要删除job。

    如何保证一个job并发执行?

    实现StatefulJob 而不是Job,查看更多关于StatefulJob 的信息。

    如何停止一个正在执行的Job?

    查看org.quartz.InterruptableJob接口和Scheduler.interrupt(String, String)方法。

    关于Trigger的一些问题

    怎么执行任务链?或者说怎么创建工作流?

    目前还没有直接的或自由的方式通过Quartz链式触发任务。然而,有几种方法,你可以轻易的达到目标。

    方法一:
    使用监听器(TriggerListener,JobListener 或者SchedulerListener),可以通知到某个工作完成,然后可以开始另一个任务。这种方式有一点复杂,你必须告知监听器哪个任务是接着哪个任务的,你可能还会担心这些信息的持久性。可以查看org.quartz.listeners.JobChainingJobListener,已经具有一些这样的功能了。
    方法二:
    创建一个Job,它的JobDataMap 包含下一个Job的名字,当这一个job执行完毕再执行下一个任务。大多数的做法是创建一个基类(通常是抽象类或接口)作为Job,并拥有获得job名称的方法,使用预定义的key从JobDataMap 进行分组。抽象类实现execute()方法委托模板方法例如”doWork()”去执行,它包含了调度后续作业的代码。之后子类要做的只是简单的扩展这个类,包括做自己应该做的工作。持久化job的使用,或者重载addJob(JobDetail, boolean, boolean) 方法(Qartz2.2新增的)帮助应用程序使用适当的数据来定义所有的工作,并没有创建触发器来激发他们(除了一个触发器触发外链中的第一个job)。
    以后,Quartz 将会提供一个更简洁的方式处理这个流程,但是现在你可以考虑前面两种处理方式或其他更好的方式处理工作流。

    为什么我的触发器trigger没有执行?

    常见的原因可能是没有调用Scheduler.start()方法,这个方法它告诉调度程序启动触发器。还有一种可能是trigger或者trigger group被暂停了。

    夏令时和触发器

    CronTrigger SimpleTrigger以自己的方式处理夏令时——每一个方式,都是直观的触发类型。

    首先,了解下什么是夏令时,可以查看:https://secure.wikimedia.org/wikipedia/en/wiki/Daylight_saving_time_around_the_world。一些读者可能没意识到,不同的国家/内容的规则是不同的。举个例子,2005年的夏令时起始于4月3日(美国)而埃及是4月29。同样重要的是要知道,不同地区不仅仅是日期不同,日期转换(前一天和后一天的中国是零点)也是不同的。许多地方移在凌晨两点,但其他地方可能是凌晨1:00,凌晨3点等。

    SimpleTrigger可以让你安排一个任务在任何毫秒级执行。可以每N毫秒执行一次任务。总是每N秒就发生一次,与一天中的时间没有关系。
    CronTrigger可以让你在某些时刻执行任务,是按”公历”时间计算的。在指定的一天中的时间触发,然后计算下一次触发的时间。

    关于JDBCJobStore的一些问题

    怎么提升JDBC-JobStore的性能?

    下面有一些提升JDBC-JobStore性能的方法,其中只有一种是有效的:

    • 使用更快更好的网络
    • 买一个更好的机器
    • 买一个更好的RDBMS

    现在,提供一种简单的但有效的方式:在Quartz表建立索引。

    很多数据库系统都会自动把索引放在主键字段上,许多数据库系统也会对外键也建立索引。确保你的数据库是这样做的,或者手动为表的关键字建立索引。
    最重要的索引的TRIGGER 表的next_fire_timestate字段。最后但不是重要的,为FIRED_TRIGGERS 表的每一个字段设置索引。

    create index idx_qrtz_t_next_fire_time on qrtz_triggers(NEXT_FIRE_TIME);
    create index idx_qrtz_t_state on qrtz_triggers(TRIGGER_STATE);
    create index idx_qrtz_t_nf_st on qrtz_triggers(TRIGGER_STATE,NEXT_FIRE_TIME);
    create index idx_qrtz_ft_trig_name on qrtz_fired_triggers(TRIGGER_NAME);
    create index idx_qrtz_ft_trig_group on qrtz_fired_triggers(TRIGGER_GROUP);
    create index idx_qrtz_ft_trig_name on qrtz_fired_triggers(TRIGGER_NAME);
    create index idx_qrtz_ft_trig_n_g on \
        qrtz_fired_triggers(TRIGGER_NAME,TRIGGER_GROUP);
    create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(INSTANCE_NAME);
    create index idx_qrtz_ft_job_name on qrtz_fired_triggers(JOB_NAME);
    create index idx_qrtz_ft_job_group on qrtz_fired_triggers(JOB_GROUP);
    create index idx_qrtz_t_next_fire_time_misfire on \
        qrtz_triggers(MISFIRE_INSTR,NEXT_FIRE_TIME);
    create index idx_qrtz_t_nf_st_misfire on \
        qrtz_triggers(MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
    create index idx_qrtz_t_nf_st_misfire_grp on \
        qrtz_triggers(MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);

    集群功能最适合于长时间运行和/或密集的工作(在多个节点上分配工作负载),如果你需要扩展到支持成千上万的短运行(例如1秒)的工作,考虑工作集分割使用多个不同的调度器(因此多套表(有不同的前缀))。当你添加多个客户端的时候,使用一个调度程序将会强制使用一个集群锁,一个模式,降低性能。

    如果数据库服务器重新启动,我的数据库连接不会恢复正常

    如果您正在创建连接数据源(通过指定在Quartz属性文件中的连接参数),请确保您有一个指定的连接验证查询:

    org.quartz.dataSource.myDS.validationQuery=select 0 from dual

    这个专门的查询语句对Orable数据库是非常有效的。其他的数据库,可以使用合适的sql。

    如果你的数据源是由您的应用程序服务器管理,确保数据源配置在这样一种方式,它可以检测连接失败。

    关于Transactions事务的一些问题

    使用JobStoreCMT,并且遇到死锁,该怎么办?

    JobStoreCMT 在大量使用,滥用可以导致死锁。不管怎样,我们总是抱怨死锁。到目前为止,该问题已被证明是“用户错误”。如果遇到死锁,下面的列表可能是你需要检查的事情了:

    • 当一个事务执行很长时间时,有些数据库会把它当成死锁。 确保你已经设置了索引 。
    • 确保在你的线程池中至少有两个以上数据库连接。
    • 确保你有一个托管的和非托管的数据源供Quartz使用。
    • 确保你在一个任务中处理的业务是在一个事务中。 处理完记得提交事务。
    • 如果你的 Jobs的 execute()方法 使用schedule, 确保使用UserTransaction或者设置Quartz属性:"org.quartz.scheduler.wrapJobExecutionInUserTransaction=true".

    集群、规模化和高可用特性

    Quartz有什么集群能力?

    Quartz具有可扩展和高可用性功能。你可以在Quartz配置资料中找到答案:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/configuration/

    其他的集群配置,不依赖后台数据库,Terracotta

    展开全文
  • 任务调度框架Quartz(三)任务调度框架Quartz实例详解深入理解Scheduler,Job,Trigger,JobDetail 2016年11月06日 17:24:50 东陆之滇 阅读数:17636 标签: quartz 任务调度 job trigger scheduler 更多 个人分类: ...

    任务调度框架Quartz(三)任务调度框架Quartz实例详解深入理解Scheduler,Job,Trigger,JobDetail

    2016年11月06日 17:24:50 东陆之滇 阅读数:17636 标签: quartz 任务调度 job trigger scheduler 更多

    个人分类: 【任务调度框架Quartz】

    所属专栏: 任务调度框架Quartz

    版权声明:欢迎转载,但是请附上原文链接! https://blog.csdn.net/zixiao217/article/details/53053598

    首先给一个简明扼要的理解: Scheduler 调度程序-任务执行计划表,只有安排进执行计划的任务Job(通过scheduler.scheduleJob方法安排进执行计划),当它预先定义的执行时间到了的时候(任务触发trigger),该任务才会执行。

    上一节中我们的示例中,我们预先安排了一个定时任务:该任务只做一件事,就是打印任务执行时间以及汇报任务已经执行。我们的任务类实现了org.quartz.Job这个接口:public class HelloJob implements Job,才会被安排成定是可执行任务。这一节,我们就详细了解一下Quartz中编程的几个重要接口。

    (本文章分享在CSDN平台,更多精彩请阅读 东陆之滇的csdn博客:http://blog.csdn.net/zixiao217)

    Quartz编程API几个重要接口

    • Scheduler - 用于与调度程序交互的主程序接口。
    • Job - 我们预先定义的希望在未来时间能被调度程序执行的任务类,如上一节的HelloJob类。
    • JobDetail - 使用JobDetail来定义定时任务的实例。
    • Trigger - 触发器,表明任务在什么时候会执行。定义了一个已经被安排的任务将会在什么时候执行的时间条件,比如上一节的实例的每2秒就执行一次。
    • JobBuilder -用于声明一个任务实例,也可以定义关于该任务的详情比如任务名、组名等,这个声明的实例将会作为一个实际执行的任务。
    • TriggerBuilder - 触发器创建器,用于创建触发器trigger实例。

    Scheduler调度程序、SchedulerFactory调度程序工厂

    Scheduler调度程序

    org.quartz.Scheduler这是Quartz 调度程序的主要接口。
    Scheduler维护了一个JobDetailsTriggers的注册表。一旦在Scheduler注册过了,当定时任务触发时间一到,调度程序就会负责执行预先定义的Job

    调度程序Scheduler实例是通过SchedulerFactory工厂来创建的。一个已经创建的scheduler ,可以通过同一个工厂实例来获取它。 调度程序创建之后,它只是出于”待机”状态,必须在任务执行前调用scheduler的start()方法启用调度程序。你还可以使用shutdown()方法关闭调度程序,使用isShutdown()方法判断该调度程序是否已经处于关闭状态。通过Scheduler的scheduleJob(…)方法的几个重载方法将任务纳入调度程序中。在上一节中我们使用的是scheduleJob(JobDetail jobDetail, Trigger trigger)方法将我们预先定义的定时任务安排进调度计划中。任务安排之后,你就可以调用start()方法启动调度程序了,当任务触发时间到了的时候,该任务将被执行。

    SchedulerFactory调度程序工厂

    SchedulerFactory有两个默认的实现类:DirectSchedulerFactoryStdSchedulerFactory

    DirectSchedulerFactory

    DirectSchedulerFactory是一个org.quartz.SchedulerFactory的单例实现。
    这里有一些使用DirectSchedulerFactory的示例代码段:
    示例1:你可以使用createVolatileScheduler方法去创建一个不需要写入数据库的调度程序实例:

    //创建一个拥有10个线程的调度程序
    DirectSchedulerFactory.getInstance().createVolatileScheduler(10);  
    //记得启用该调度程序
    DirectSchedulerFactory.getInstance().getScheduler().start();
    • 1
    • 2
    • 3
    • 4

    为方便起见,提供了几种创建方法。所有创建方法最终会最终会使用所有参数的来创建调度程序:

      public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort)
    • 1

    示例2:

    // 创建线程池
    SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads, Thread.NORM_PRIORITY); 
    threadPool.initialize(); 
    
    // 创建job存储器
    JobStore jobStore = new RAMJobStore();
    
    //使用所有参数创建调度程序
    DirectSchedulerFactory.getInstance().createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore, "localhost", 1099); 
    
    // 不要忘了调用start()方法来启动调度程序
    DirectSchedulerFactory.getInstance().getScheduler("My Quartz Scheduler", "My Instance").start();

    你也可使用JDBCJobStore,形如:

    DBConnectionManager.getInstance().addConnectionProvider("someDatasource", new JNDIConnectionProvider("someDatasourceJNDIName"));
    
    JobStoreTX jdbcJobStore = new JobStoreTX(); jdbcJobStore.setDataSource("someDatasource"); jdbcJobStore.setPostgresStyleBlobs(true); jdbcJobStore.setTablePrefix("QRTZ_"); jdbcJobStore.setInstanceId("My Instance");
    • 1
    • 2
    • 3

    StdSchedulerFactory

    StdSchedulerFactory是org.quartz.SchedulerFactory的实现类,它是基于Quartz属性文件创建Quartz Scheduler 调度程序的。我们在上一节实例中使用的就是StdSchedulerFactory,因为我们指定了属性文件quartz.properties。

    默认情况下是加载当前工作目录下的”quartz.properties”属性文件。如果加载失败,会去加载org/quartz包下的”quartz.properties”属性文件。我们使用JD-GUI反编译工具打开quartz.jar,可以在org/quartz包下找到其默认的属性文件的配置信息:

    org.quartz.scheduler.instanceName: DefaultQuartzScheduler
    org.quartz.scheduler.rmi.export: false
    org.quartz.scheduler.rmi.proxy: false
    org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
    
    org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
    org.quartz.threadPool.threadCount: 10
    org.quartz.threadPool.threadPriority: 5
    org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
    
    org.quartz.jobStore.misfireThreshold: 60000
    
    org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

     

    这里写图片描述

     

    如果你不想使用默认的文件名,你可以指定org.quartz.properties属性指向你的属性配置文件。要不然,你可以在调用getScheduler()方法之前调用initialize(xx)方法初始化工厂配置。

    属性配置文件中,还可以引用其他配置文件的信息,你可以使用$@来引用:
    quartz1.properties

    org.quartz.scheduler.instanceName=HelloScheduler
    • 1

    quartz2.properties

    org.quartz.scheduler.instanceName=$@org.quartz.scheduler.instanceName
    • 1

    参照以下StdSchedulerFactory的属性配置,实际使用中你自己可以指定一些符合需求的参数,例如指定存储器可以配置org.quartz.jobStore.class的值。

        PROPERTIES_FILE = "org.quartz.properties";
    
        PROP_SCHED_INSTANCE_NAME = "org.quartz.scheduler.instanceName";
    
        PROP_SCHED_INSTANCE_ID = "org.quartz.scheduler.instanceId";
    
        PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS = "org.quartz.scheduler.instanceIdGenerator.class";
    
        PROP_SCHED_THREAD_NAME = "org.quartz.scheduler.threadName";
    
        PROP_SCHED_SKIP_UPDATE_CHECK = "org.quartz.scheduler.skipUpdateCheck";
    
        PROP_SCHED_BATCH_TIME_WINDOW = "org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow";
    
        PROP_SCHED_MAX_BATCH_SIZE = "org.quartz.scheduler.batchTriggerAcquisitionMaxCount";
    
        PROP_SCHED_JMX_EXPORT = "org.quartz.scheduler.jmx.export";
    
        PROP_SCHED_JMX_OBJECT_NAME = "org.quartz.scheduler.jmx.objectName";
    
        PROP_SCHED_JMX_PROXY = "org.quartz.scheduler.jmx.proxy";
    
        PROP_SCHED_JMX_PROXY_CLASS = "org.quartz.scheduler.jmx.proxy.class";
    
        PROP_SCHED_RMI_EXPORT = "org.quartz.scheduler.rmi.export";
    
        PROP_SCHED_RMI_PROXY = "org.quartz.scheduler.rmi.proxy";
    
        PROP_SCHED_RMI_HOST = "org.quartz.scheduler.rmi.registryHost";
    
        PROP_SCHED_RMI_PORT = "org.quartz.scheduler.rmi.registryPort";
    
        PROP_SCHED_RMI_SERVER_PORT = "org.quartz.scheduler.rmi.serverPort";
    
        PROP_SCHED_RMI_CREATE_REGISTRY = "org.quartz.scheduler.rmi.createRegistry";
    
        PROP_SCHED_RMI_BIND_NAME = "org.quartz.scheduler.rmi.bindName";
    
        PROP_SCHED_WRAP_JOB_IN_USER_TX = "org.quartz.scheduler.wrapJobExecutionInUserTransaction";
    
        PROP_SCHED_USER_TX_URL = "org.quartz.scheduler.userTransactionURL";
    
        PROP_SCHED_IDLE_WAIT_TIME = "org.quartz.scheduler.idleWaitTime";
    
        PROP_SCHED_DB_FAILURE_RETRY_INTERVAL = "org.quartz.scheduler.dbFailureRetryInterval";
    
        PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON = "org.quartz.scheduler.makeSchedulerThreadDaemon";
    
        PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD = "org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer";
    
        PROP_SCHED_CLASS_LOAD_HELPER_CLASS = "org.quartz.scheduler.classLoadHelper.class";
    
        PROP_SCHED_JOB_FACTORY_CLASS = "org.quartz.scheduler.jobFactory.class";
    
        PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN = "org.quartz.scheduler.interruptJobsOnShutdown";
    
        PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT = "org.quartz.scheduler.interruptJobsOnShutdownWithWait";
    
    
        PROP_THREAD_POOL_CLASS = "org.quartz.threadPool.class";
    
        PROP_JOB_STORE_CLASS = "org.quartz.jobStore.class";
    
        PROP_JOB_STORE_USE_PROP = "org.quartz.jobStore.useProperties";
    
        PROP_CONNECTION_PROVIDER_CLASS = "connectionProvider.class";

    Job定时任务实例类

    一个任务是一个实现org.quartz.Job接口的类,任务类必须含有空构造器,它只有一个简单的方法:

    void execute(JobExecutionContext context)
            throws JobExecutionException;

    当关联这个任务实例的触发器表明的执行时间到了的时候,调度程序Scheduler 会调用这个方法来执行任务,我们的任务内容就可以在这个方法中执行。

    public class HelloJob implements Job {
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("现在是北京时间:" + DateUtil.getCurrDateTime() + " - helloJob任务执行");
        }
    
    }

    在该方法退出之前,会设置一个结果对象到JobExecutionContext 中。尽管这个结果对Quartz来说没什么意义,但是JobListeners或者TriggerListeners 来说,是可以监听查看job的执行情况的。后面会详细讲解监听器的内容。

    JobDataMap提供了一种”初始化成员属性数据的机制”,在实现该Job接口的时候可能会用到。

    Job实例化的过程

    可能很多人对于一个Job实例的组成以及创建过程感到迷惑,笔者曾经也是如此,所以现在请耐心理解。
    你可以创建一个Job类,在调度程序(任务计划表)中创建很多JobDetai可以存储很多初始化定义信息——每一个都可以设置自己的属性和JobDataMap——将他们全部添加到调度程序中去。

    这里举个例子说明一下,你可以创建一个任务类实现Job接口,不妨称之为”SalesReportJob”,我们用它做销售报表使用。我们可以通过JobDataMap指定销售员的名称和销售报表的依据等等。这就会创建多个JobDetails了,例如”SalesReportForJoe”,”SalesReportForMike”分别对应在JobDataMap中指定的名字”joe”和”mike”。

    重要:当触发器的执行时间到了的时候,会加载与之关联的JobDetail,并在调度程序Scheduler中通过JobFactory的配置实例化它引用的Job。JobFactory 调用newInstance()创建一个任务实例,然后调用setter 方法设置在JobDataMap定义好的名字。你可以实现JobFactory,比如使用IOC或DI机制初始化的任务实例。

    Job的声明和并发

    关于Job的声明和并发需要说明一下,以下一对注解使用在你的Job类中,可以影响Quartz的行为:
    @DisallowConcurrentExecution : 可以添加到你的任务类中,它会告诉Quartz不要执行多个任务实例。
    注意措辞,在上面的”SalesReportJob”类添加该注解,将会只有一个”SalesReportForJoe”实例在给定的时间执行,但是”SalesReportForMike”是可以执行的。这个约束是基于JobDetail的,而不是基于任务类的

    @PersistJobDataAfterExecution : 告诉Quartz在任务执行成功完毕之后(没有抛出异常),修改JobDetail的JobDataMap备份,以供下一个任务使用。

    如果你使用了@PersistJobDataAfterExecution 注解的话,强烈建议同时使用@DisallowConcurrentExecution注解,以避免当两个同样的job并发执行的时候产生的存储数据迷惑。

    Job的其他一些属性

    • 持久化 - 如果一个任务不是持久化的,则当没有触发器关联它的时候,Quartz会从scheduler中删除它。
    • 请求恢复 - 如果一个任务请求恢复,一般是该任务执行期间发生了系统崩溃或者其他关闭进程的操作,当服务再次启动的时候,会再次执行该任务。这种情况下,JobExecutionContext.isRecovering()会返回true。

    JobDetail定义任务实例的一些属性特征

    org.quartz.JobDetail接口负责传输给定的任务实例的属性到Scheduler。JobDetail是通过JobBuilder创建的。

    Quartz不会存储一个真实的Job类实例,但是允许你通过JobDetail定义一个任务实例——JobDetail是用来定义任务实例的。

    任务Job有一个名称name 和组group 来关联。在一个Scheduler中这二者的组合必须是唯一的。

    触发器任务计划执行表的执行”机制”。多个触发器可以指向同一个工作,但一个触发器只能指向一个工作。

    JobDataMap任务数据映射

    JobDataMap用来保存任务实例的状态信息。
    当一个Job被添加到调度程序(任务执行计划表)scheduler的时候,JobDataMap实例就会存储一次关于该任务的状态信息数据。也可以使用@PersistJobDataAfterExecution注解标明在一个任务执行完毕之后就存储一次。

    JobDataMap实例也可以村粗一个触发器trigger。这是非常有用的,特别是当你的任务被多个触发器引用的时候,根据不同的触发时机,你可以提供不同的输入条件。

    JobExecutionContext 也可以再执行时包含一个方便的JobDataMap ,它合并了触发器的 JobDataMap (如果有的话)和Job的 JobDataMap (如果有的话)。

    这里我们改一下上一节的程序作为示例,在定义JobDetail的时候,将一些数据放入JobDataMap 中:

    // 定义一个job,并且绑定HelloJob类
     JobDetail job = newJob(HelloJob.class)
                      .withIdentity("job1", "group1")
                      .usingJobData("jobSays", "Hello World!")
                      .usingJobData("myFloatValue", 3.141f)
                      .build();

    然后在任务执行的时候,可以获取JobDataMap 中的数据:

    package org.byron4j.quartz;
    
    import org.byron4j.utils.DateUtil;
    import org.quartz.Job;
    import org.quartz.JobDataMap;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.quartz.JobKey;
    
    /**
     * 实现org.quartz.Job接口,声明该类是一个可执行任务类
     * 
     * @author Administrator
     *
     */
    public class HelloJob implements Job {
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("现在是北京时间:" + DateUtil.getCurrDateTime() + " - helloJob任务执行");
    
            JobKey key = context.getJobDetail().getKey();
    
            JobDataMap dataMap = context.getJobDetail().getJobDataMap();
    
            String jobSays = dataMap.getString("jobSays");
            float myFloatValue = dataMap.getFloat("myFloatValue");
    
            System.err.println("Instance " + key + " of HelloJob says: " + jobSays + ", and val is: " + myFloatValue);
    
        }
    
    }
    

    控制台输出:

    scheduleName = MyScheduler
    现在是北京时间:2016-11-06 16:24:57 - helloJob任务执行
    Instance group1.job1 of HelloJob says: Hello World!, and val is: 3.141
    现在是北京时间:2016-11-06 16:24:59 - helloJob任务执行
    Instance group1.job1 of HelloJob says: Hello World!, and val is: 3.141
    现在是北京时间:2016-11-06 16:25:01 - helloJob任务执行
    Instance group1.job1 of HelloJob says: Hello World!, and val is: 3.141

    我们在触发器也添加数据:

    JobDetail job = newJob(HelloJob.class)
                      .withIdentity("job1", "group1")
                      .usingJobData("jobSays", "Hello World!")
                      .usingJobData("myFloatValue", 3.141f)
                      .build();
    
    // 声明一个触发器,现在就执行(schedule.start()方法开始调用的时候执行);并且每间隔2秒就执行一次
    Trigger trigger = newTrigger()
                      .withIdentity("trigger1", "group1")
                      .usingJobData("trigger_key", "每2秒执行一次")
                      .startNow()
                            .withSchedule(simpleSchedule()
                              .withIntervalInSeconds(2)
                              .repeatForever())            
                      .build();

    HelloJob的execute方法改成如下:

    public void execute(JobExecutionContext context) throws JobExecutionException {
            JobKey key = context.getJobDetail().getKey();
    
            //使用归并的JobDataMap
            JobDataMap dataMap = context.getMergedJobDataMap(); 
    
            String jobSays = dataMap.getString("jobSays");
            float myFloatValue = dataMap.getFloat("myFloatValue");
            String triggerSays = dataMap.getString("trigger_key");
    
            System.err.println("Instance " + key + " of HelloJob says: " + jobSays + ", and val is: " + myFloatValue
                    + ";trigger says:" + triggerSays);
        }

    控制台输出如下,得到了job、trigger的JobDataMap 的数据:

    scheduleName = MyScheduler
    Instance group1.job1 of HelloJob says: Hello World!, and val is: 3.141;trigger says:每2秒执行一次
    Instance group1.job1 of HelloJob says: Hello World!, and val is: 3.141;trigger says:每2秒执行一次

    Trigger触发器

    Trigger触发器,可以理解为安排了一个任务,这个任务是在每年9月10日早上9点向你敬爱的老师发送一天祝福短信,触发器就是指每年9月10日早上9点触发执行这个任务。

    触发器使用TriggerBuilder来实例化。
    触发器有一个TriggerKey关联,这在一个Scheduler中必须是唯一的。
    触发器任务计划执行表的执行”机制”。多个触发器可以指向同一个工作,但一个触发器只能指向一个工作。
    触发器可以传送数据给job——通过将数据放进触发器的JobDataMap。

    触发器常用属性

    触发器也有很多属性,这些属性都是在使用TriggerBuilder 定义触发器时设置的。

    • TriggerKey - 唯一标识触发器,这在一个Scheduler中必须是唯一的
    • “startTime” - 开始时间,通常使用startAt(java.util.Date)
    • “endTime” - 结束时间,设置了结束时间则在这之后,不再触发

    触发器的优先级

    有时候,你会安排很多任务,但是Quartz并没有更多的资源去处理它。这种情况下,你必须需要很好地控制哪个任务先执行了。这时候你可以使用设置priority 属性(使用方法withPriority(int))来控制触发器的优先级。

    注意:优先级只有触发器出发时间一样的时候才有意义。
    注意:当一个任务请求恢复执行时,它的优先级和原始优先级是一样的。

    JobBuilder用于创建JobDetail;TriggerBuilder 用于创建触发器Trigger

    JobBuilder用于创建JobDetail。总是把保持在有效状态,合理的使用默认设置在你调用build() 方法的时候。如果你没有调用withIdentity(..)指定job的名字,它会自动给你生成一个。

    TriggerBuilder 用于创建触发器Trigger。如果你没有调用withSchedule(..) 方法,会使用默认的schedule 。如果没有使用withIdentity(..) 会自动生成一个触发器名称给你。

    Quartz通过一种领域特定语言(DSL)提供了一种自己的builder的风格API来创建任务调度相关的实体。DSL可以通过对类的静态方法的使用来调用:TriggerBuilder, JobBuilder, DateBuilder, JobKey, TriggerKey 以及其它的关于Schedule创建的实现。

    客户端可以使用类似示例使用DSL:

    /*静态引入builder*/
    import static org.quartz.JobBuilder.newJob;
    import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
    import static org.quartz.TriggerBuilder.newTrigger;
    
    
    JobDetail job = newJob(MyJob.class)
                 .withIdentity("myJob")
                 .build();
     Trigger trigger = newTrigger() 
                 .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
                 .withSchedule(simpleSchedule()
                     .withIntervalInHours(1)
                     .repeatForever())
                 .startAt(futureDate(10, MINUTES))
                 .build();
    scheduler.scheduleJob(job, trigger);

    总结:Scheduler—job—trigger

    我们以一个现实生活中的例子为例:
    Scheduler就是定时任务执行计划表,目前共有两个job安排进了执行计划:元旦放假不上班,春节放假团圆在家,这些都是ZF预先定义好的执行计划。”元旦放假不上班”、”春节放假团圆在家”是两个job,第一个job的触发时间是每年1月1日(触发器1),第二个job是每年的农历初一(触发器2)。


    发表评论

    添加代码片

    还能输入1000个字符

    • qq_35605246

      qq_35605246: 你好,最近遇到quartz集群的一个问题,非常苦恼,望给点建议或思路,谢谢。 描述:该任务每天5点执行,三台服务器,A服务器从05:00:00.092~05:11:47.263执行结束(执行结果是成功的),B服务器从05:05:50.082~05:11:47.270执行结束(结果失败,因为A服务器正在执行中,例如锁表等),C服务器从05:11:40.092~05:11:47.320执行结束(结果失败,和B服务器一样),从结果看quartz会有这样大概5分钟左右的机制吗?如果5分钟左右没有执行完,集群时,另一台quartz会接手继续执行?(1年前#3楼)查看回复(1)举报回复

      • zixiao217

        东陆之滇回复 qq_35605246:您好,可以使用@DisallowConcurrentExecution注解可以添加到你的任务类中,它会告诉Quartz不要执行多个任务实例(1年前)举报回复

    • u012919352

      唯一本尊: 好文章,准确,明白,但是有个地方可能没说清楚,group其实可以不用的(1年前#2楼)举报回复

    展开全文
  • 为您提供PowerJob 任务调度框架下载,PowerJob是全新一代分布式调度与计算框架,支持CRON、API、固定频率、固定延迟等调度策略,提供工作流来编排任务解决依赖关系,使用简单,功能强大,文档齐全,能让您轻松完成...
  • 为您提供PowerJob 任务调度框架下载,PowerJob是全新一代分布式调度与计算框架,支持CRON、API、固定频率、固定延迟等调度策略,提供工作流来编排任务解决依赖关系,使用简单,功能强大,文档齐全,能让您轻松完成...
  • 为您提供PowerJob 任务调度框架下载,PowerJob是全新一代分布式调度与计算框架,支持CRON、API、固定频率、固定延迟等调度策略,提供工作流来编排任务解决依赖关系,使用简单,功能强大,文档齐全,能让您轻松完成...
  • 为您提供PowerJob 任务调度框架下载,PowerJob是全新一代分布式调度与计算框架,支持CRON、API、固定频率、固定延迟等调度策略,提供工作流来编排任务解决依赖关系,使用简单,功能强大,文档齐全,能让您轻松完成...
  • 任务调度框架对比

    2020-06-26 10:14:50
    前言 任务调度框架的演变:从单机到分布式
  • 为您提供PowerJob java任务调度框架下载,PowerJob是全新一代分布式调度与计算框架,支持CRON、API、固定频率、固定延迟等调度策略,提供工作流来编排任务解决依赖关系,使用简单,功能强大,文档齐全,能让您轻松...
  • 微服务任务调度框架

    2020-01-10 14:23:10
    微服务任务调度框架 https://github.com/siaorg/sia-task
  • 分布式任务调度框架

    2019-02-19 23:21:01
    我们在实际的开发工作中,或多或少的都会用到任务调度这个功能...常见的分布式任务调度框架有:cronsun、Elastic-job、saturn、lts、TBSchedule、xxl-job等。  定时任务的分布式调度  分布式调度框架大集合  ...
  • 任务调度框架之Quartz

    2020-08-05 17:26:38
    任务调度框架Quartz Quartz是一个特性丰富的,开源的任务调度框架。他几乎可以嵌入所有的Java应用程序,从很小的到大型的商业系统,Quartz可以创建成千简单的活着复杂的任务,这些任务可以用来执行任何程序可以做的...
  • Quartz任务调度框架

    万次阅读 2016-08-19 17:07:21
    Quartz 任务调度框架
  • 分布式任务调度框架几乎是每个大型应用必备的工具,本文介绍了任务调度框架使用的需求背景和痛点,对业界普遍使用的开源分布式任务调度框架的使用进行了探究实践,并分析了这几种框架的优劣势和对自身业务的思考。...
  • Java任务调度框架之分布式调度框架XXL-Job介绍及快速入门 调度器使用场景: Java开发中经常会使用到定时任务:比如每月1号凌晨生成上个月的账单、比如每天凌晨1点对上一天的数据进行对账操作,在比如每天凌晨5点给...
  • 分布式作业调度框架,是一个开发迅速、学习简单、轻量级、易扩展、高可用分布式任务调度框架。 二、分布式任务调度框架 2.1 任务调度框架的简介 任务调度是指基于给定的时间点,给定的时间间隔或者给定执行次数...
  • 接下来我们首先分析分布式任务调度框架相对单机的优势以及结合几种任务调度框架分析是如何逐步实现分布式的高可用、效率的,最后综合比较下业界比较流行的框架,在项目开发中方便选择。 2、Why 分布式任务调度框架 ...
  • java任务调度框架Quartz

    2020-12-03 15:10:34
    为什么需要任务调度框架: 账单日或者还款日上午 10 点,给每个信用卡客户发送账单通知,还款通知。如何判断客户的账单日、还款日,完成通知的发送? 对于后台一些数据迁移、任务跑批等需要定时执行。 … 类似基于...
  • 之前我在文章中介绍过.net中的任务调度框架Hangfire,HangFire虽然本身输入比较简单好用的,但是,如果我们的程序本身提供的服务不是任务调度,而任务调度只是里面并不重要的小功能的时候,用HangFire显得有点过重了...
  • 分布式任务调度框架 ElasticJob 中文文档 PDF 带目录

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,892
精华内容 2,756
关键字:

任务调度框架