1.1、Quartz基础结构 Quartz对任务调度的领域问题进行了高度抽象,提出了调度器、任务和触发器3个核心概念。
- Job:该接口只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义需要执行的任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。
- JobDetail:JobDetail用于描述Job的名称、描述、关联监听器等信息,其构造函数为JobDetail(String name,String group,Class jobClass)。
- Trigger:该类是描述Job执行的时间触发规则,其子类为SimpleTrigger和CronTrigger。SimpleTrigger适用于仅需触发一次或以固定间隔周期性执行;CronTrigger用于定义时间表达式。
- Calendar:一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点,比如排除法定节假日执行某项任务。
- Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,2者在Scheduler中拥有各自的组及名称。组和名称是Scheduler查找定位容器中某一对象的依据。组合名称的组合必须唯一,但Trigger和JobDetail的组和名称的组合可以相同,因为2者类型不同。Scheduler提供了多个接口方法,允许外部通过组和名称访问和控制容器中的Trigger和JobDetail。Scheduler可以将Trigger绑定到JobDetail中,这样当Trigger被出发时,Job就会被执行。Job和Trigger的比例是1:n。可以通过SchedulerFactory来创建一个Scheduler实例,并通过Scheduler#getContext()获得一个SchedulerContext,Trigger和JobDetail可以访问SchedulerContext中的上下文信息。
- ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
(1)Job有一个StatefullJob子接口,代表有状态的任务,该接口是一个没有方法的标签接口,其目的是让Quartz知道任务的类型,以便采用不同的执行方案。
- 无状态任务在执行时拥有自己的JobDataMap复制,对JobDataMap的更改不会影响下次的执行。
- 有状态的任务共享同一个JobDataMap实例,每次任务执行对JobDataMap所做的更改会保存下来。
1.2、使用SimpleTrigger (1)构造函数如下:
(2)Quartz任务示例: 其中SimpleJob是Job接口子类,另外还可以使用TriggerUtils来创建特定时间的触发器。
1.3、使用CronTrigger (1)Cron表达式时间字段
位置 | 时间域名 | 允许值 | 允许的特殊字符 |
1 | 秒 | 0-59 | ,-*/ |
2 | 分钟 | 0-59 | ,-*/ |
3 | 小时 | 0-23 | ,-*/ |
4 | 日期 | 1-31 | ,-*?/L C W |
5 | 月份 | 1-12 | ,-*/ |
6 | 星期 | 1-7 | ,-*?/L C # |
7 | 年(可选) | 空值1970-2099 | ,-*/ |
示例如下:
(2)CronTrigger实例
1.4、使用Calendar 示例如下:
1.5、任务调度信息存储 (1)Quartz默认设置任务调度信息是保存在内存中的,如果想修改这一机制,可以在类路径下创建一个同名的quartz.properties文件,来覆盖原来的配置文件。 quartz.properties默认配置如下:
Quartz属性配置文件主要包含3方面信息:
- 集群信息;
- 调度器线程池
- 任务调度现场数据的保存
(2)如果想持久化任务调度信息,可以修改配置如下:
2、在Spring中使用Quartz Spring为创建Scheduler提供了BeanFactory类。
2.1、创建JobDetail (1)JobDetailBean 为了不在配置文件中使用构造函数的形式配置JobDetail,Spring提供了一个JobDetailBean,以便使用属性配置的方式配置JobDetail。使用JobDetailBean时,Bean的名字就是任务名,如果没有指定所属组就使用默认组。JobDetailBean还扩展了以下属性:
- jobClass:类型为Class,实现了Job接口的任务类。
- beanName:Bean的id名,对应任务名称。
- jobDataAsMap:为JobDataMap提供值,因为用户无法在Spring配置文件中为JobDataMap提供值。
- applicationContextJobDataKey:改属性作为一个指向ApplicationContext的key,以便用户在Job中可以访问ApplicationContext。
- jobListenerNames:指定在Scheduler中注册的监听器的名称,为Stirng[],以便监听任务。
运用示例如下:
(2)MethodInvokingJobDetailFactoryBean 通常情况下,任务都是定义在一个业务类方法中,所以我们要在Job类中组合一个业务类的引用。为避免创建只包含这个一行调用代码的Job实现类,Spring为我们提供了MethodInvokingJobDetailFactoryBean,借由该Bean,我们可以将一个普通Bean的某个方法封装成满足要求的Job。 配置示例如下:
①concurrent是用来设置任务的类型的:
- true:无状态任务;
- false:有状态任务(默认)。
2.2、创建Trigger (1)SimpleTriggerBean 新增以下属性:
- jobDetail:对应JobDetail。
- beanName:Bean的id名,对应Trigger名称。
- jobDataAsMap:为JobDataMap提供值。
- startDelay:延迟多少时间触发,单位毫秒,默认0。
- triggerListenerNames:监听触发器的事件。
运用示例如下:
(2)CronTriggerBean 配置示例如下:
2.3、创建Scheduler (1)为了保证Scheduler能感知Spring容器的生命周期,Spring提供了SchedulerFactoryBean,该Bean使Scheduler关联了Spring生命周期,同时也通过属性配置的方式替代了Quartz的自身配置文件。 配置示例如下:
①SchedulerFactoryBean允许用户以Map的方式设置Scheduler关联的SchedulerContext的值。 ②除此以外,SchedulerFactoryBean还有以下属性:
- calendars:Map类型,通过该属性向Scheduler中注册Calendar。
- jobDetails:JobDetail[],通过该属性向Scheduler中注册JobDetail。
- autoStartup:代表是否在SchedulerFactoryBean初始化后立即启动Scheduler。
- startupDelay:延迟一段时间启动。
- dataSource:如果需要数据库持久化任务调度数据,可以通过该属性指定一个数据源。
- transactionManager:为防止表锁定,Spring建议用户使用同一个事务管理器。
- quartzProperties:类型为Properties,该属性可以覆盖quartz.properties中的属性,配置示例如下:
3、在Spring中使用JDK Timer JDK Timer相较于Quartz的局限性:
- 无法满足日历相关的任务要求;
- Timer中所有TimerTask都在同一背景线程中执行,所以Timer只适合执行时间短的任务,长时间的任务会严重影响Timer的调度工作。
3.1、Timer和TimerTask (1)TimerTask TimerTask相当于Quartz中的Job,两者的主要区别是Quartz每次执行任务都会创建一个新的Job实例,而Timer则使用相同的TimerTask。 TimerTask是个抽象类,3个方法如下:
(2)Timer 一般情况下Timer使用守护线程来执行任务,所以可以通过Timer#cancel()方法手工结束任务。 主要方法如下:
3.2、Spring对Timer的支持 (1)JDK Timer中在执行Timer方法时才能指定调度规则,因此不太适合进行Bean配置,所以Spring提供了ScheduledTimerTask,可以通过属性指定任务和调度规则。 配置如下:
ScheduledTimerTask还可以将实现了Runnable接口的类封装成一个任务,用户可以通过runnable属性进行设置。 (2)其余:
4、Spring对JDK 5.0 Executor的支持 4.1、了解JDK 5.0的Executor (1)Executor的主要目的是要将“任务提交”和“任务执行”分开,它只有一个方法: void execute(Runnable command)。 (2)Executor有2个子接口,ExecutorService和ScheduledExecutorService。
- ExecutorService:添加了结束任务的方法,在提交任务时还可以获取一个Future实例,以便通过这个实例跟踪异步任务的运行情况。
- ScheduledExecutorService:可以对任务进行调度,如指定执行的延迟时间以及运行周期。
4.2、Spring对Executor所提供的抽象 (1)Spring的org.springframework.core.task.TaskExecutor接口等同于Executor接口,TaskExecutor拥有一个子接口SchedulingTaskExecutor,新增了任务调度的定制功能。 (2)Spring发行包中预定义了一些TaskExecutor实现,它们可以满足大部分应用需求:
5、任务调度与应用集群 5.1、任务调度对应用集群的影响 (1)任务可分为2种:
- 全局任务:指具有全局可见性,执行结果会影响到应用系统全局的任务,全局任务最好部署在单独的服务节点上,否则可能会因重复执行引发系统逻辑错误。
- 本地任务:指执行结果只影响本地,可以且必须部署在每个服务节点。
- 通过数据库使节点互相感知,实现failover。
- 通过Terracotta进行集群管理,Terracotta是一个JVM级的开源集群框架。
5.2、任务调度云 123+336=?
5.3、Web应用任务调度开闭问题 (1)线程是JVM级别的,所以web容器的生命周期和线程的生命周期有区别,即关闭容器不一定能理解结束任务。 (2)Spring为Timer和Quartz提供的TimerFactoryBean和SchedulerFactoryBean能够和Spring容器的生命周期关联,在Spring容器启动时启动调度器,关闭时关闭调度器,其实就是在init-method和destory-method中设置。