热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringBoot定时任务(schedule、quartz)

SpringBoot定时任务(schedule、quartz)Scheduled只适合处理简单的计划任务,不能处理分布式计划任务。优势:是spring

 

SpringBoot定时任务(schedule、quartz)

Scheduled

  只适合处理简单的计划任务,不能处理分布式计划任务。优势:是spring框架提供的计划任务,开发简单,执行效率比较高。且在计划任务数量太多的时候,可能出现阻塞,崩溃,延迟启动等问题。
  Scheduled定时任务是spring3.0版本之后自带的一个定时任务。其所属Spring的资源包为:spring-context-support。所以需要使用Scheduled定时任务机制时,需要在工程中依赖对应资源,具体如下:


org.springframeworkspring-context-support

  如果在spring应用中需要启用Scheduled定时任务,则需要在启动类上增加注解@EnableScheduling,代表启用Scheduled定时任务机制。具体如下:

@SpringBootApplication
@EnableScheduling
public class AppStarter {public static void main(String[] args) {SpringApplication.run(AppStarter.class, args);}
}

  Scheduled定时任务的核心在于注解@Scheduled,这个注解的核心属性是cron,代表定时任务的触发计划表达式。这个表达式的格式为:

@Scheduled(cron="seconds minutes hours day month week")

  或

@Scheduled(cron="seconds minutes hours day month week year")

  推荐使用第一种表达式形式,因为在很多其他技术中都有不同的定时任务机制,其中用于设置触发计划的表达式都是第一种cron表达式。第二种表达式不能说是Spring Scheduled特有的,也是只有少数技术支持的。

  cron表达式中,每个位置的约束如下:

星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”;
问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于占位符;
减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在秒数字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;

  L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;

  W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;

  LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;

  井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;

  C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。

  Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。

  计划任务Scheduled是通过一个线程池实现的。是一个多线程的调度。SpringBoot会初始化一个线程池,线程池默认大小为1,专门用于执行计划任务。每个计划任务启动的时候,都从线程池中获取一个线程执行,如果发生异常,只是执行当前任务的线程发生异常,而不是计划任务调度线程发生异常。如果当前定时任务还未执行完成,当相同的定时任务又进入到执行周期时,不会触发新的定时任务。如:

@Scheduled(cron="* * * * * ?")
public void test1(){Random r = new Random();/*int i = r.nextInt(100);if(i % 3 == 0){throw new RuntimeException("error");}*/System.out.println(Thread.currentThread().getName() + " cron=* * * * * ? --- " + new Date());try{Thread.sleep(2000);}catch(Exception e){e.printStackTrace();}
}

  如结果所示(每次的线程名称一致,由于前一个定时任务未执行完成,因此造成后一个任务的推迟,而不是1秒执行一次,而是3秒):

pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:20 CST 2019
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:23 CST 2019
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:26 CST 2019
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:29 CST 2019

quartz

  Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。

  Quartz是一个完全由java编写的开源作业调度框架。不要让作业调度这个术语吓着你。尽管Quartz框架整合了许多额外功能, 但就其简易形式看,你会发现它易用得简直让人受不了!
在开发Quartz相关应用时,只要定义了Job(任务),Trigger(触发器)和Scheduler(调度器),即可实现一个定时调度能力。其中Scheduler是Quartz中的核心,Scheduler负责管理Quartz应用运行时环境,Scheduler不是靠自己完成所有的工作,是根据Trigger的触发标准,调用Job中的任务执行逻辑,来完成完整的定时任务调度。
  Job - 定时任务内容是什么。
  Trigger - 在什么时间上执行job。
  Scheduler - 维护定时任务环境,并让触发器生效。
  在SpringBoot中应用Quartz,需要依赖下述资源:

org.springframeworkspring-context-supportorg.quartz-schedulerquartz2.2.1slf4j-apiorg.slf4jorg.springframeworkspring-tx

  启动器添加注解@EnableScheduling:

/*** @EnableScheduling 必要* 开启定时任务机制。*/
@SpringBootApplication
@EnableScheduling
public class AppStarter {public static void main(String[] args) {SpringApplication.run(AppStarter.class, args);}
}

  定义JOB任务以及JOB任务调用的模拟业务对象:

1 public class SpringBootQuartzJobDemo implements Job {2 3 // 用于模拟任务中的业务对象。 也可能是数据访问对象,或其他类型的对象。4 @Autowired5 private CommonsUtil4Quartz commonsUtil4Quartz;6 7 @Override8 public void execute(JobExecutionContext context) throws JobExecutionException {9 System.out.println("SpringBootQuartzJobDemo : " + new Date());
10 this.commonsUtil4Quartz.testMethod();
11 }
12
13 }

@Component
public class CommonsUtil4Quartz {public void testMethod(){System.out.println("CommonsUtil4Quartz testMethod run...");}
}

  创建Trigger以及JobDetail对象,并用Schedule配置定时任务:

/*** 初始化类* Quartz环境初始化。* */
@Configuration
public class QuartzConfiguration {/*** 创建Job对象。在Spring环境中,创建一个类型的对象的时候,很多情况下,都是通过FactoryBean来间接创建的。* 如果有多个Job对象,定义多次方法。* * 在JobDetailFactoryBean类型中,用于创建JobDetail对象的方法,其底层使用的逻辑是:Class.newInstance()* 也就是说,JobDetail对象不是通过Spring容器管理的。* 因为Spring容器不管理JobDetail对象,那么Job中需要自动装配的属性,就无法实现自动状态。如上JOB的第10行会报空指针异常。* * 解决方案是: 将JobDetail加入到Spring容器中,让Spring容器管理JobDetail对象。* 需要重写Factory相关代码。实现Spring容器管理JobDetail。* @return*/@Beanpublic JobDetailFactoryBean initJobDetailFactoryBean(){JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();// 提供job类型。factoryBean.setJobClass(SpringBootQuartzJobDemo.class);return factoryBean;}/*** 管理Trigger对象* CronTrigger - 就是Trigger的一个实现类型。 其中用于定义周期时间的是CronSchedulerBuilder* 实际上,CronTrigger是用于管理一个Cron表达式的类型。* @param jobDetailFactoryBean - 上一个方法初始化的JobDetailFactoryBean* @return*/@Bean(name="cronTriggerFactoryBean1")public CronTriggerFactoryBean initCronTriggerFactoryBean(){CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();JobDetailFactoryBean jobDetailFactoryBean = this.initJobDetailFactoryBean();factoryBean.setJobDetail(jobDetailFactoryBean.getObject());factoryBean.setCronExpression("0/3 * * * * ?");return factoryBean;}/*** 初始化Scheduler* @param cronTriggerFactoryBean - 上一个方法初始化的CronTriggerFactoryBean* @return*/@Beanpublic SchedulerFactoryBean initSchedulerFactoryBean(CustomJobFactory customJobFactory,CronTriggerFactoryBean[] cronTriggerFactoryBean){SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();CronTrigger[] triggers = new CronTrigger[cronTriggerFactoryBean.length];for(int i = 0; i

  重写JobFactory:

/*** 重写的工厂对象。*/
@Component
public class CustomJobFactory extends AdaptableJobFactory {/*** AutowireCapableBeanFactory : 简单理解为Spring容器,是Spring容器Context的一个Bean对象管理工程。* 可以实现自动装配逻辑,和对象创建逻辑。* 是SpringIoC容器的一个重要组成部件。*/@Autowiredprivate AutowireCapableBeanFactory autowireCapableBeanFactory;@Overrideprotected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {// 通过父类型中的方法,创建JobDetail对象。Object obj = super.createJobInstance(bundle);// 将JobDetail对象加入到Spring容器中,让Spring容器管理,并实现自动装配逻辑。this.autowireCapableBeanFactory.autowireBean(obj);return obj;}}

分布式quartz配置

  1、资源依赖配置:由于分布式的原因,Quartz中提供分布式处理的jar包以及数据库及连接相关的依赖。

org.springframeworkspring-context-supportorg.quartz-schedulerquartz2.2.1slf4j-apiorg.slf4jorg.quartz-schedulerquartz-jobs2.2.1org.springframeworkspring-txorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-data-jpamysqlmysql-connector-javacom.alibabadruid1.0.9

  2、提供数据库相关配置:

spring.datasource.url=jdbc:mysql://localhost:3306/quartz?autoReconnect=true&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=600000
spring.datasource.timeBetweenEvictionRunsMillis=600000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=falseserver.port=8080

  3、提供Quartz配置信息,为Quartz提供数据库配置信息,如数据库,表格的前缀之类的。

# 是否使用properties作为数据存储
org.quartz.jobStore.useProperties=false
# 数据库中的表格命名前缀
org.quartz.jobStore.tablePrefix = QRTZ_
# 是否是一个集群,是不是分布式的任务
org.quartz.jobStore.isClustered = true
# 集群检查周期,单位毫秒。可以自定义缩短时间。 当某一个节点宕机的时候,其他节点等待多久后开始执行任务。
org.quartz.jobStore.clusterCheckinInterval = 5000
# 单位毫秒, 集群中的节点退出后,再次检查进入的时间间隔。
org.quartz.jobStore.misfireThreshold = 60000
# 事务隔离级别
org.quartz.jobStore.txIsolationLevelReadCommitted = true
# 存储的事务管理类型
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 使用的Delegate类型
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 集群的命名,一个集群要有相同的命名。
org.quartz.scheduler.instanceName = ClusterQuartz
# 节点的命名,可以自定义。 AUTO代表自动生成。
org.quartz.scheduler.instanceId= AUTO
# rmi远程协议是否发布
org.quartz.scheduler.rmi.export = false
# rmi远程协议代理是否创建
org.quartz.scheduler.rmi.proxy = false
# 是否使用用户控制的事务环境触发执行job。
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

  4、初始化数据库

  建表语句可以自己在官方网站中查找(Quartz-lib中),使用tables-mysql.sql建表。

  5、定义JOB类

  启动器和普通quartz无差异,但是JOB自身定义有些许差异:

/*** 使用Spring提供的Quartz相关Job类型实现Job的定义。* 父类型QuartzJobBean中,提供了分布式环境中任务的配置定义。* 保证分布式环境中的任务是有效的。*/
@PersistJobDataAfterExecution // 当job执行结束,持久化job信息到数据库
@DisallowConcurrentExecution // 保证job的唯一性(单例)
public class SpringBootQuartzJobDemo extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {System.out.println("SpringBootQuartzJobDemo : " + new Date());}
}

   6、QuartzConfiguration类型定义

@Configuration
public class QuartzConfiguration {@Autowiredprivate DataSource dataSource;/*** 创建调度器, 可以省略的。* @return* @throws Exception*/@Beanpublic Scheduler scheduler() throws Exception {Scheduler scheduler = schedulerFactoryBean().getScheduler();scheduler.start();return scheduler;}/*** 创建调度器工厂bean对象。* @return* @throws IOException*/@Beanpublic SchedulerFactoryBean schedulerFactoryBean() throws IOException {SchedulerFactoryBean factory = new SchedulerFactoryBean();factory.setSchedulerName("Cluster_Scheduler");factory.setDataSource(dataSource);factory.setApplicationContextSchedulerContextKey("applicationContext");// 设置调度器中的线程池。factory.setTaskExecutor(schedulerThreadPool());// 设置触发器factory.setTriggers(trigger().getObject());// 设置quartz的配置信息factory.setQuartzProperties(quartzProperties());return factory;}/*** 读取quartz.properties配置文件的方法。* @return* @throws IOException*/@Beanpublic Properties quartzProperties() throws IOException {PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));// 在quartz.properties中的属性被读取并注入后再初始化对象propertiesFactoryBean.afterPropertiesSet();return propertiesFactoryBean.getObject();}/*** 创建Job对象的方法。* @return*/@Beanpublic JobDetailFactoryBean job() {JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();jobDetailFactoryBean.setJobClass(SpringBootQuartzJobDemo.class);// 是否持久化job内容jobDetailFactoryBean.setDurability(true);// 设置是否多次请求尝试任务。jobDetailFactoryBean.setRequestsRecovery(true);return jobDetailFactoryBean;}/*** 创建trigger factory bean对象。* @return*/@Beanpublic CronTriggerFactoryBean trigger() {CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();cronTriggerFactoryBean.setJobDetail(job().getObject());cronTriggerFactoryBean.setCronExpression("0/2 * * * * ?");return cronTriggerFactoryBean;}/*** 创建一个调度器的线程池。* @return*/@Beanpublic Executor schedulerThreadPool() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(15);executor.setMaxPoolSize(25);executor.setQueueCapacity(100);return executor;}
}

  若JOB任务有定义调用业务等内容,也需要重写JobFactory,如上述常规quartz,此处不再赘述。

 

 

原文地址:https://www.cnblogs.com/jing99/p/11546559.html

 

 


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
author-avatar
文帅zssw
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有