Spring Scheduler定时任务 + Quartz

原文地址: https://blog.csdn.net/revitalizing/article/details/61420556html

 

版权声明:本文为博主原创文章,未经博主容许不得转载。 https://blog.csdn.net/lzx_2011/article/details/61420556

定时任务几种实现方式

  1. Java自带的java.util.Timer类,这个类容许你调度一个java.util.TimerTask任务,没怎么用过就不说了。
  2. Spring3.0之后自带的task,能够将它当作一个轻量级的Quartz,并且使用起来比Quartz简单许多。
  3. java的线程池类ScheduledExecutorService也能够实现一些简单的定时任务,周期性任务。
  4. Quartz是一个功能比较强大的的调度器,可让你的程序在指定时间执行,也能够按照某一个频度执行,能够方便的分布式部署、便捷的监控和管理任务,适合任务不少的状况。

Spring Scheduler注解方式实现

代码仍是挺少的java

@Component public class SchedulerPractice{ // @Scheduled(fixedDelay=60000) @Scheduled(cron = "0 0/1 * * * ?")
 public void execute() {
 logger.info("every one minute------");
 } }
  • 1
  • 2
  • 3
  • 4
  • 5

配置文件git

<beans xmlns="http://www.springframework.org/schema/beans" 
 xmlns:task="http://www.springframework.org/schema/task" 
 http://www.springframework.org/schema/task 
 http://www.springframework.org/schema/task/spring-task-3.0.xsd
 > <context:annotation-config /> <!—spring扫描注解的配置 --> <context:component-scan base-package="com.liu” /> <task:annotation-driven />
  • 1
  • 2
  • 3
  • 4
  • 5

注解@Scheduled 中有三个方法,用来对执行规则的配置:github

cron:指定cron表达式,文章最后有些配置示例。web

fixedDelay:即表示从上一个任务完成开始到下一个任务开始的间隔,单位是毫秒。redis

fixedRate:即从上一个任务开始到下一个任务开始的间隔,单位是毫秒。spring

执行时间可配置化

将cron表达式配置在java的properties文件或者环境变量中,是配置更灵活数据库

xml配置方式express

<task:scheduler id="myScheduler"/> <task:scheduled-tasks scheduler="myScheduler" pool-size="2" /> <task:scheduled ref=“testScheduler” method="execute()” cron="${cron_expression}"/> </task:scheduled-tasks> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

pool-size=”2” 有多个任务能够配置以线程池执行缓存

注解使用方式

@Scheduled(cron = "${cron_expression}")
  • 1

分布式多实例运行

scheduler与web配置在一块儿,在高可用的状况下,若是有多个web容器实例,scheduler会在多个实例上同时运行。

解决办法:

  1. 使用写死服务器Host的方式执行task,存在单点风险,负载均衡手动完成。(或者一台代码中配置任务,其余不配置任务)

  2. 在task的基类加入一些逻辑,当开始运行时,将状态(运行机器的IP、时间等)写入数据库、缓存(redis)或者zk,运行结束时重置状态。其它实例看到有这样的数据,就直接返回。带来的问题是: 
    必定要保证结束运行后将状态重置,不然下一个运行周期,全部的task都会返回的。 
    由于读写状态并不是原子操做,偶尔也会发生task同时运行的事。

  3. 使用zk分布式锁,好比在任务执行方法上自定义注解,在注解中配置锁在zk的路径,在该注解上自定义个拦截器,在拦截器中获取zk锁。

Java线程池ScheduledExecutorService

示例代码以下

public void cronThread(){ ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1); scheduledThreadPool.scheduleWithFixedDelay(new ThreadPractice(), 0, 3, TimeUnit.SECONDS); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

new ThreadPractice()是一个实现了Runnable的类。 
ScheduledExecutorService 类有两个方法

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这两个方法和@scheduled 注解中的fixedDelay和fixedRate相似。

Quartz

Quartz框架是一个全功能、开源的任务调度服务,能够集成几乎任何的java应用程序—从小的单片机系统到大型的电子商务系统。能够方便的分布式部署、便捷的监控和管理任务,Quartz能够执行上千上万的任务调度。

核心概念

Quartz核心的概念:scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节

  • Job任务:其实Job是接口,其中只有一个execute方法, 只要实现此接口,实现execute方法便可。

  • JobDetail:任务细节,Quartz执行Job时,须要新建个Job实例,可是不能直接操做Job类,因此经过JobDetail来获取Job的名称、描述信息。

  • Trigger触发器:执行任务的规则;好比天天,每小时等。触发器有SimpleTrigger和CronTrigger,这个触发器实现了Trigger接口。对于复杂的时间表达式来讲,好比每月15日上午几点几分,使用CronTrigger,对于简单的时间来讲,好比天天执行几回,使用SimpleTrigger。

  • scheduler任务调度:是最核心的概念,须要把JobDetail和Trigger注册到scheduler中,才能够执行。

quartz单机示例

使用的quartz的jar的版本是:2.2.1 ,低版本的核心类可能有些不一样。

job类

public class MyJob implements Job { @Override //把要执行的操做,写在execute方法中 public void execute(JobExecutionContext arg0) throws JobExecutionException { System.out.println(“test Quartz"+new Date()); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

测试代码

@Test public void startJobTest() { SchedulerFactory schedulerfactory = new StdSchedulerFactory(); Scheduler scheduler = null; try { // 经过schedulerFactory获取一个调度器 scheduler = schedulerfactory.getScheduler(); // 建立jobDetail实例,绑定Job实现类 // 指明job的名称,所在组的名称,以及绑定job类 JobDetail job = JobBuilder.newJob(ImageTableMonitorJob.class).withIdentity("job1", "jgroup1").build(); // 定义调度触发规则 // 使用simpleTrigger规则 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup") .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withRepeatCount(8)).startNow().build(); // 使用cornTrigger规则 天天10点42分 // Trigger trigger= TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup") // .withSchedule(CronScheduleBuilder.cronSchedule("0 42 10 * * ? *")) // .startNow().build(); // 把做业和触发器注册到任务调度中 scheduler.scheduleJob(job, trigger); // 启动调度 scheduler.start(); } catch (Exception e) { // e.printStackTrace(); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

spring提供了对quartz的整合,能够经过 org.springframework.scheduling.quartz.SchedulerFactoryBean 注入scheduler调度器,而且对调度器作些配置,好比使用线程池,并配置线程数量等。配置示例以下。

<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" > <property name="taskExecutor" ref="taskExecutor" /> <property name="autoStartup" value="false"/> </bean> <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value="2" /> <property name="maxPoolSize" value="512" /> </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Quartz分布式原理

Quartz的集群部署方案在架构上是分布式的,没有负责集中管理的节点,而是利用数据库锁的方式来实现集群环境下进行并发控制。分布式部署时须要保证各个节点的系统时间一致。没弄过,就不细说了。

Quartz数据库核心表QRTZ_LOCKS中有5条记录CALENDAR_ACCESS,JOB_ACCESS,MISFIRE_ACCESS,STATE_ACCESS,TRIGGER_ACCESS 表明5把锁,分别用于实现多个Quartz Node对Job、Trigger、Calendar访问的同步控制。

cron表达式

字段   容许值   容许的特殊字符
秒    0-59 , - * / 分 0-59 , - * / 小时 0-23 , - * / 日期 1-31 , - * ? / L W C 月份 1-12 或者 JAN-DEC , - * / 星期 1-7 或者 SUN-SAT , - * ? / L C # 年(可选) 留空, 1970-2099 , - * / - 区间 * 通配符 ? 你不想设置那个字段 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

下面列出一些实例

CRON表达式    含义 
"0 0 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分每分钟一次触发 "0 0/5 14 * * ?" 天天从下午2点开始到2:55分结束每5分钟一次触发 "0 0/5 14,18 * * ?" 天天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发 "0 0-5 14 * * ?" 天天14:00至14:05每分钟一次触发 "0 10,44 14 ? 3 WED" 三月的每周三的14:10和14:44触发 "0 15 10 ? * MON-FRI" 每一个周1、周2、周3、周4、周五的10:15触发 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

参考资料

Java任务调度框架Quartz教程实例 
Spring Scheduler的使用与坑 
Quartz应用与集群原理分析