定时任务管理之java篇quartz使用

     在开发过程中,我们经常会遇到一些需要异步定期执行的批处理任务。比如夜里低峰时段的备份、统计,或者是每周、每月对数据库表进行整理,这时就需要通过使用定时任务管理器来辅助我们完成这些任务的定时触发。常见的定时任务管理器多分为三类,分别是:

     ①操作系统(OS)级别的定时任务管理器,例如linux的crontab、windows自带的计划任务。OS级不用专门开启监听器,占用系统资源较少,而且操作简便,是定时任务首选的实现方式,但是但是当任务数量非常大,而且任务与任务之间有因果关系、先后顺序、竞争条件的话,OS级别的定时任务管理器就很难满足需求了;

     ②编程语言自带的定时任务管理器,例如Java的timer和TimeTask。但是这些API提供的接口功能简单,往往不能满足用户定时任务设置需要,所以在项目开发过程中很少使用;

     ③第三方专门开发的定时任务管理组件,例如Java的quartz,python的celery等。这些组件往往既可以单独部署,也可以与当前的项目集成在一起统一部署管理,关键是他们有着强大的功能,能够满足我们对定时任务管理的各种需求,所以这些第三方组件往往在项目中应用广泛。本次我就重点讲一下quartz的配置实用。

一、了解quartz的体系结构

     Quartz对任务调度的领域问题进行了高度的抽象,提出了调度器、作业任务和触发器这3个核心的概念,并在org.quartz通过接口和类对重要的这些核心概念进行描述:

①Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;

②JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,但是它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。

     通过该类的构造函数可以更具体地了解它的功用:JobDetail(java.lang.String name, java.lang.String group, java.lang.Class jobClass),该构造函数要求指定Job的实现类,以及任务在Scheduler中的组名和Job名称;

③Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;

④Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。

     Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例;

⑤ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。

二、示例代码

1、作业任务

     通过实现 org.quartz.job 接口,可以使 Java 类变成可执行的。下面的例子就提供了 Quartz 作业的一个示例。这个类用一条非常简单的输出语句覆盖了 execute(JobExecutionContext context) 方法。这个方法可以包含我们想要执行的任何代码。

import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class SimpleQuartzJob implements Job {  	  
    public SimpleQuartzJob() {}  
  
    public void execute(JobExecutionContext context) throws JobExecutionException {  
        System.out.println("In SimpleQuartzJob - executing its JOB at "   
                + new Date() + " by " + context.getTrigger().getName());  
    }  
}

     请注意,execute 方法接受一个 JobExecutionContext 对象作为参数。这个对象提供了作业实例的运行时上下文。特别地,它提供了对调度器和触发器的访问,这两者协作来启动作业以及作业的 JobDetail 对象的执行。Quartz 通过把作业的状态放在 JobDetail 对象中并让 JobDetail 构造函数启动一个作业的实例,分离了作业的执行和作业周围的状态。JobDetail 对象储存作业的侦听器、群组、数据映射、描述以及作业的其他属性。

2、简单触发器

     触发器可以实现对任务执行的调度。Quartz 提供了几种不同的触发器,复杂程度各不相同。下面的例子中的 SimpleTrigger 展示了触发器的基础:

 package com.cyou.quartz;

import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;

public class SimpleTriggerTest {
	public static void main(String[] args) throws SchedulerException {
		//初始化一个Schedule工厂
		SchedulerFactory schedulerFactory = new StdSchedulerFactory();
		//通过schedule工厂类获得一个Scheduler类
		Scheduler scheduler = schedulerFactory.getScheduler();
		//通过设置job name, job group, and executable job class初始化一个JobDetail
		JobDetail jobDetail = new JobDetail("jobDetail-s1","jobDetailGroup-s1", SimpleQuartzJob.class);
		//设置触发器名称和触发器所属的组名初始化一个触发器
		SimpleTrigger simpleTrigger = new SimpleTrigger("simpleTrigger","triggerGroup1");		
		//获取当前时间,初始化触发器的开始日期
		long ctime = System.currentTimeMillis();
		simpleTrigger.setStartTime(new Date(ctime));
		//设置触发器触发运行的时间间隔(10 seconds here)
		simpleTrigger.setRepeatInterval(10000);
		//设置触发器触发运行的次数,这里设置运行100,完成后推出
		simpleTrigger.setRepeatCount(100);
		/**
		 * set the ending time of this job.
		 * We set it for 60 seconds from its startup time here
		 * Even if we set its repeat count to 10,
		 * this will stop its process after 6 repeats as it gets it endtime by then.
		 * **/ 
		// simpleTrigger.setEndTime(new Date(ctime + 60000L));
		//设置触发器的优先级,模式为5
		// simpleTrigger.setPriority(10);
		//交给调度器调度运行JobDetail和Trigger
		scheduler.scheduleJob(jobDetail, simpleTrigger);
		//启动调度器
		scheduler.start();
	}
}
     开始时实例化一个 SchedulerFactory,获得此调度器。就像前面讨论过的,创建 JobDetail 对象时,它的构造函数要接受一个 Job 作为参数。顾名思义, SimpleTrigger 实例相当原始。在创建对象之后,设置几个基本属性以立即调度任务,然后每 10 秒重复一次,直到作业被执行100次。

猜你喜欢

转载自student-lp.iteye.com/blog/2093395