小曹学spring--任务调度和异步执行器

时间:2022-01-17 19:58:55
1、Quartz快速进阶 Quartz允许开发人员灵活地定义触发器的调度时间表,并可对触发器和任务进行关联映射。此外,Quartz提供了调度运行环境的持久化机制,可以保存并恢复调度现场,即使系统因故障关闭,任务调度现场数据并不会丢失。此外,Quartz还提供了组件式的侦听器、各种插件、线程池等功能。
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执行的时间触发规则,其子类为SimpleTriggerCronTriggerSimpleTrigger适用于仅需触发一次或以固定间隔周期性执行;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所做的更改会保存下来。
正因为这个原因,无状态的Job可以并发执行,而有状态的StatefullJob不能并发执行,因此如无必要,尽量使用无状态的Job。 (2)如果Quartz使用了数据库持久化任务调度信息,无状态的JobDataMap仅会在Scheduler注册任务时保存一次,而有状态的JobDataMap每次执行任务后都会保存。 (3)Trigger自身也可以拥有一个JobDataMap,可以通过JobExecutionContext#getTrigger().getJobDataMap()获取。不管是无状态还是有状态的任务,在任务执行期间对Trigger的JobDataMap所做的更改都不会持久化。
1.2、使用SimpleTrigger (1)构造函数如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器 (2)Quartz任务示例: 其中SimpleJob是Job接口子类,另外还可以使用TriggerUtils来创建特定时间的触发器。 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
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 ,-*/
特殊字符含义如下: 小曹学spring--任务调度和异步执行器 小曹学spring--任务调度和异步执行器 小曹学spring--任务调度和异步执行器
示例如下:
小曹学spring--任务调度和异步执行器

(2)CronTrigger实例 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
1.4、使用Calendar 示例如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
1.5、任务调度信息存储 (1)Quartz默认设置任务调度信息是保存在内存中的,如果想修改这一机制,可以在类路径下创建一个同名的quartz.properties文件,来覆盖原来的配置文件。 quartz.properties默认配置如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器 Quartz属性配置文件主要包含3方面信息:
  • 集群信息;
  • 调度器线程池
  • 任务调度现场数据的保存

(2)如果想持久化任务调度信息,可以修改配置如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
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[],以便监听任务。
配置示例如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器 运用示例如下: 小曹学spring--任务调度和异步执行器 小曹学spring--任务调度和异步执行器

(2)MethodInvokingJobDetailFactoryBean 通常情况下,任务都是定义在一个业务类方法中,所以我们要在Job类中组合一个业务类的引用。为避免创建只包含这个一行调用代码的Job实现类,Spring为我们提供了MethodInvokingJobDetailFactoryBean,借由该Bean,我们可以将一个普通Bean的某个方法封装成满足要求的Job。 配置示例如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器 ①concurrent是用来设置任务的类型的:
  • true:无状态任务;
  • false:有状态任务(默认)。
②目标Bean的方法可以是static的,也可以是非static的,但是方法不能有入参 ③使用MethodInvokingJobDetailFactoryBean生成的JobDetail不能序列化,所以无法持久化。
2.2、创建Trigger (1)SimpleTriggerBean 新增以下属性:
  • jobDetail:对应JobDetail。
  • beanName:Bean的id名,对应Trigger名称。
  • jobDataAsMap:为JobDataMap提供值。
  • startDelay:延迟多少时间触发,单位毫秒,默认0。
  • triggerListenerNames:监听触发器的事件。
配置示例如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器 运用示例如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
(2)CronTriggerBean 配置示例如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
2.3、创建Scheduler (1)为了保证Scheduler能感知Spring容器的生命周期,Spring提供了SchedulerFactoryBean,该Bean使Scheduler关联了Spring生命周期,同时也通过属性配置的方式替代了Quartz的自身配置文件。 配置示例如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器 ①SchedulerFactoryBean允许用户以Map的方式设置Scheduler关联的SchedulerContext的值。 ②除此以外,SchedulerFactoryBean还有以下属性:
  • calendars:Map类型,通过该属性向Scheduler中注册Calendar。
  • jobDetails:JobDetail[],通过该属性向Scheduler中注册JobDetail。
  • autoStartup:代表是否在SchedulerFactoryBean初始化后立即启动Scheduler。
  • startupDelay:延迟一段时间启动。
  • dataSource:如果需要数据库持久化任务调度数据,可以通过该属性指定一个数据源。
  • transactionManager:为防止表锁定,Spring建议用户使用同一个事务管理器。
  • quartzProperties:类型为Properties,该属性可以覆盖quartz.properties中的属性,配置示例如下:小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
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个方法如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器 (2)Timer 一般情况下Timer使用守护线程来执行任务,所以可以通过Timer#cancel()方法手工结束任务。 主要方法如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
3.2、Spring对Timer的支持 (1)JDK Timer中在执行Timer方法时才能指定调度规则,因此不太适合进行Bean配置,所以Spring提供了ScheduledTimerTask,可以通过属性指定任务和调度规则。 配置如下: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器 ScheduledTimerTask还可以将实现了Runnable接口的类封装成一个任务,用户可以通过runnable属性进行设置。 (2)其余: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
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:可以对任务进行调度,如指定执行的延迟时间以及运行周期。
(3)ThreadPoolExecutor实现了Executor和ExecutorService这2个接口,ThreadPoolExecutor的子类ScheduledThreadPoolExecutor实现了ScheduledExecutorService接口。 (4)java.util.concurrent包中为创建这些接口实例提供了一个综合性的工厂类Executors,它拥有如下方法: 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器
4.2、Spring对Executor所提供的抽象 (1)Spring的org.springframework.core.task.TaskExecutor接口等同于Executor接口,TaskExecutor拥有一个子接口SchedulingTaskExecutor,新增了任务调度的定制功能。 (2)Spring发行包中预定义了一些TaskExecutor实现,它们可以满足大部分应用需求: 小曹学spring--任务调度和异步执行器 小曹学spring--任务调度和异步执行器
小曹学spring--任务调度和异步执行器 小曹学spring--任务调度和异步执行器
5、任务调度与应用集群 5.1、任务调度对应用集群的影响 (1)任务可分为2种:
  • 全局任务:指具有全局可见性,执行结果会影响到应用系统全局的任务,全局任务最好部署在单独的服务节点上,否则可能会因重复执行引发系统逻辑错误。
  • 本地任务:指执行结果只影响本地,可以且必须部署在每个服务节点。
(2)Quartz集群实现方式 ①原理:让多个调度节点互为热备,同一时刻只有一个调度节点是激活的,其他处于“休眠状态”,当激活的节点崩溃时,则唤醒一个节点接管调度工作。 ②实现方式:
  • 通过数据库使节点互相感知,实现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中设置。