【Spring】定时任务
spring的定时任务:
1)简单的有Java自带的Timer、 ScheduledExecutorService, Spring自带的Task。
2)相较复杂的分布式定时任务中间件有XXL-JOB、ElasticJob等。
选Quartz理由:
1)任务Tigger能够被持久化,这样即使在发布后,任务依然能够执行,不需要重新设定。
2)能够轻松暂停恢复触发器(即下次不会被调度)。
3)支持Calander,Cron表达式等复杂的触发器,可以灵活的编写复杂触发器。
一:使用Timer创建简单的定时任务
有两点问题需要注意:
1.scheduleAtFixedRate和schedule的区别:scheduleAtFixedRate会尽量减少漏掉调度的情况,如果前一次执行时间过长,导致一个或几个任务漏掉了,那么会补回来,而schedule过去的不会补,直接加上间隔时间执行下一次任务。
public class TimerDemo {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("TimerTask1 run" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));}},1000,5000);//延时1s,之后每隔5s运行一次}
}
有两点问题需要注意:
1.scheduleAtFixedRate和schedule的区别:scheduleAtFixedRate会尽量减少漏掉调度的情况,如果前一次执行时间过长,导致一个或几个任务漏掉了,那么会补回来,而schedule过去的不会补,直接加上间隔时间执行下一次任务。
2.同一个Timer下添加多个TimerTask,如果其中一个没有捕获抛出的异常,则全部任务都会终止运行。但是多个Timer是互不影响
二:使用ScheduledThreadPoolExecutor创建定时任务
public class SchedulerDemo {public static void main(String[] args) {ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(5);executorService.scheduleWithFixedDelay(new Runnable() {@Overridepublic void run() {String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));System.out.println("ScheduledThreadPoolExecutor1 run:"+now);}},1,2,TimeUnit.SECONDS);}
}
scheduleWithFixedDelay跟schedule类似,而scheduleAtFixedRate与scheduleAtFixedRate一样会尽量减少漏掉调度的情况
三:在springboot环境下使用@Scheduled创建定时任务
1、启动类添加@EnableScheduling
2、定时任务方法上添加@Scheduled
@Component
public class springScheduledDemo {@Scheduled(cron = "1/5 * * * * ?")public void testScheduled(){System.out.println("springScheduled run:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));}
}
这里的cron表达式我们可以参考网上的配置:
但是spring的@Scheduled只支持6位,年份是不支持的,带年份的7位格式会报错:Cron expression must consist of 6 fields (found 7 in “1/5 * * * * ? 2018”):cron表达式生成器:http://cron.ciding.cc/
@Scheduled 默认是单线程执行的,所以在需要的时候,我们可以设置一个线程池去执行定时任务。
通过实现SchedulingConfigurer接口来将定时线程池放入
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;@Configuration
public class TaskConfig implements SchedulingConfigurer {@Beanpublic TaskScheduler taskScheduler() {ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();executor.setPoolSize(10);executor.setThreadNamePrefix("task-thread");//设置饱和策略//CallerRunsPolicy:线程池的饱和策略之一,当线程池使用饱和后,直接使用调用者所在的线程来执行任务;如果执行程序已关闭,则会丢弃该任务executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}//配置@Scheduled 定时器所使用的线程池//配置任务注册器:ScheduledTaskRegistrar 的任务调度器@Overridepublic void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {//可配置两种类型:TaskScheduler、ScheduledExecutorService//scheduledTaskRegistrar.setScheduler(taskScheduler());//只可配置一种类型:taskSchedulerscheduledTaskRegistrar.setTaskScheduler(taskScheduler());}}
使用Quartz定时任务框架:
1、使用SpringBoot2.x 版本集成 Quartz
2、Quartz 的任务动态实现:
3、调度任务可以通过页面进行新增、删除、启动、暂定等操作
4、任务数据使用数据库保存
5、任务之间实现简单的依赖
6、Quartz 实现分布式调度,使用其本身提供的基于数据库的实现
SpringBoot2 集成 Quartz
1、SpringBoot不同的版本对于Quartz 的集成有一定的差别,本文使用 2.1.2.RELEASE 版本。其实通过分析SpringBoot对于Quartz的自动化配置源码,也有助于我们理解Quartz的使用
2、SpringBoot-2.1.2.RELEASE 版本已经集成了对于Quartz的自动化配置,其源码路径为org.springframework.boot.autoconfigure.quartz
Pom依赖
# Web工程<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency># quartz<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency># 数据库JDBC<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency># 使用MySql<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>
编码功能实现
由于Springboot2的自动化配置,不需要做任何配置,直接写JobDetail、Trigger、Job 即可实现
# Job 实现
@DisallowConcurrentExecution
public class DemoJob extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {System.out.println("~~ DemoJob 启动运行汇总~~");}
}# JobDetail、Trigger Bean配置
@Configuration
public class QuartzJobConfig {@Beanpublic JobDetailFactoryBean jobDetailFactoryBean() {JobDetailFactoryBean jobDetail = new JobDetailFactoryBean();jobDetail.setName("DemoJob");jobDetail.setGroup("DemoJob_Group");jobDetail.setJobClass(DemoJob.class);jobDetail.setDurability(true);return jobDetail;}@Beanpublic CronTriggerFactoryBean cronTriggerFactoryBean() {CronTriggerFactoryBean trigger = new CronTriggerFactoryBean();trigger.setJobDetail(jobDetailFactoryBean().getObject());trigger.setCronExpression("*/10 * * * * ?");trigger.setName("DemoJob");trigger.setMisfireInstruction(0);return trigger;}
}
这样就实现了 SpringBoot2.x 版本集成 Quartz 功能,在进行下一步之前,我们先对自动化配置的源码简单分析一下。
SpringBoot关于Quartz的自动配置的类一共有6个,分别为:
- JobStoreType:是一个枚举类,定义了jobsStore的类型,基于内存和数据库
- QuartzAutoConfiguration:自动配置类,配置了Quartz的主要组件,如SchedulerFactoryBean
- QuartzDataSource:是一个注解类。如果需要注入Quartz配置的数据库操作类,需要使用此注解标注。参考QuartzAutoConfiguration中的用法
- QuartzDataSourceInitializer:该类主要用于数据源初始化后的一些操作,根据不同平台类型的数据库进行选择不同的数据库脚本
- QuartzProperties:该类对应了在application.yml配置文件以spring.quartz开头的相关配置
- SchedulerFactoryBeanCustomizer:在自动配置的基础上自定义配置需要实现的此接口。
QuartzAutoConfiguration
- 初始化注入任务以及配置:构造函数实现
- 注入了属性配置文件类:QuartzProperties
- 注入了自定义扩展配置:SchedulerFactoryBeanCustomizer
- 注入了Quartz的任务组件:JobDetail、Trigger、Calendar。所以我们只- 需要进行 JobDetail、Trigger Bean配置,会自动注入进QuartzAutoConfiguration类中,这边是通过ObjectProvider的使用实现的。
public QuartzAutoConfiguration(QuartzProperties properties,ObjectProvider<SchedulerFactoryBeanCustomizer> customizers,ObjectProvider<JobDetail[]> jobDetails,ObjectProvider<Map<String, Calendar>> calendars,ObjectProvider<Trigger[]> triggers, ApplicationContext applicationContext) {this.properties = properties;this.customizers = customizers;this.jobDetails = jobDetails.getIfAvailable();this.calendars = calendars.getIfAvailable();this.triggers = triggers.getIfAvailable();this.applicationContext = applicationContext;}
2、配置 SchedulerFactoryBean 的详细信息。这个类是一个 FactoryBean。
- 配置 JobFactory,内部设置了applicationContext与spring容器结合
- 配置各种属性,是通过QuartzProperties类实现
- 配置注入进来的 JobDetail、Trigger、Calendar
- 配置自定配置,是通过SchedulerFactoryBeanCustomizer实现。这边包括自定义,也包括基于数据库实现的JobStore配置。
# 注释掉内存存储
# org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
#持久化
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
@Bean@ConditionalOnMissingBeanpublic SchedulerFactoryBean quartzScheduler() {# 配置 `JobFactory`SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();jobFactory.setApplicationContext(this.applicationContext);schedulerFactoryBean.setJobFactory(jobFactory);# 开始配置各种属性if (this.properties.getSchedulerName() != null) {schedulerFactoryBean.setSchedulerName(this.properties.getSchedulerName());}schedulerFactoryBean.setAutoStartup(this.properties.isAutoStartup());schedulerFactoryBean.setStartupDelay((int) this.properties.getStartupDelay().getSeconds());schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(this.properties.isWaitForJobsToCompleteOnShutdown());schedulerFactoryBean.setOverwriteExistingJobs(this.properties.isOverwriteExistingJobs());if (!this.properties.getProperties().isEmpty()) {schedulerFactoryBean.setQuartzProperties(asProperties(this.properties.getProperties()));}# 配置 jobDetails、triggers等if (this.jobDetails != null && this.jobDetails.length > 0) {schedulerFactoryBean.setJobDetails(this.jobDetails);}if (this.calendars != null && !this.calendars.isEmpty()) {schedulerFactoryBean.setCalendars(this.calendars);}if (this.triggers != null && this.triggers.length > 0) {schedulerFactoryBean.setTriggers(this.triggers);}# 自定义配置customize(schedulerFactoryBean);return schedulerFactoryBean;}
3、基于数据库实现的 JobStore 配置,内部类JdbcStoreTypeConfiguration
- @ConditionalOnSingleCandidate(DataSource.class) 指定pring容器中有且只有一个指明的DataSourceBean时生效
- 通过dataSourceCustomizer方法实现schedulerFactoryBean的数据库相关配置,该方法返回一个 SchedulerFactoryBeanCustomizer。
- 配置QuartzDataSourceInitializer 数据库初始化 Bean
- 通过后置工厂处理器 DataSourceInitializerSchedulerDependencyPostProcessor 实现对于QuartzDataSourceInitializer这个Bean的依赖关系(dependsOn)
@Bean@Order(0)public SchedulerFactoryBeanCustomizer dataSourceCustomizer(QuartzProperties properties, DataSource dataSource,@QuartzDataSource ObjectProvider<DataSource> quartzDataSource,ObjectProvider<PlatformTransactionManager> transactionManager) {return (schedulerFactoryBean) -> {# 判断是否为 JobStoreif (properties.getJobStoreType() == JobStoreType.JDBC) {# 获取 DataSourceDataSource dataSourceToUse = getDataSource(dataSource, quartzDataSource);# 配置 DataSource 和 TransactionManager管理schedulerFactoryBean.setDataSource(dataSourceToUse);PlatformTransactionManager txManager = transactionManager.getIfUnique();if (txManager != null) {schedulerFactoryBean.setTransactionManager(txManager);}}};}
支持功能配置 QuartzProperties
1、@ConfigurationProperties(“spring.quartz”) 以spring.quartz开头的配置
2、SpringBoot 已经做了相应的默认值处理,即使不做任何配置,也是没有问题的。
3、比较简单,直接贴码。属性的具体含义,任务调度
spring:quartz:scheduler-name: springboot-quartz-jdbc-dynamicauto-startup: falsestartup-delay: 5soverwrite-existing-jobs: falsewait-for-jobs-to-complete-on-shutdown: truejob-store-type: memory# jdbc:# initialize-schema: embedded# schema: classpath:org/quartz/impl/jdbcjobstore/tables_@@platform@@.sql# comment-prefix: --properties: {org.quartz.scheduler.instanceName: springboot-quartz-jdbc-dynamic,org.quartz.scheduler.instanceId: AUTO,org.quartz.threadPool.class: org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor,org.quartz.threadPool.threadCount: 25,org.quartz.threadPool.threadPriority: 5,org.quartz.jobStore.misfireThreshold: 60000,# org.quartz.jobStore.tablePrefix: QRTZ_,# org.quartz.jobStore.isClustered: true,# org.quartz.jobStore.clusterCheckinInterval: 20000,# org.quartz.jobStore.maxMisfiresToHandleAtATime: 1,# org.quartz.jobStore.txIsolationLevelSerializable: false}
Quartz 实现分布式调度
配置简单实现
上述完成的 SpringBoot与Quartz的集成,可以看到有几个先关的配置:
1、job-store-type 可以选择JDBC完成分布式JdbcJobStore切换
2、jdbc.XXX 主要是对于初始化SQL的配置。
3、对于JdbcJobStore 的一些特殊配置,如表前缀、集群指定、数据库检查等,基于RamJobStore时,这些是不允许配置的。
将类型转换成 JobStoreTX
JobStoreTX 全称是: org.quartz.impl.jdbcjobstore.JobStoreTX
# 注释掉内存存储
# org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
#持久化
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
三、 配置驱动代理
##驱动代理为 标准的jdbc
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
StdJDBCDelegate 为标准的jdbc。
四、配置数据库表前缀和数据源
#数据库表前缀
org.quartz.jobStore.tablePrefix:qrtz_
#数据源
org.quartz.jobStore.dataSource:yjlDB #JDBC驱动
org.quartz.dataSource.yjlDB.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.yjlDB.URL:jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.yjlDB.user:root
org.quartz.dataSource.yjlDB.password:您的数据库密码
五、最终 quartz.properties 配置文件内容
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: falseorg.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: trueorg.quartz.jobStore.misfireThreshold: 60000# 注释掉内存存储
# org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
#持久化
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
##驱动代理为 标准的jdbc
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#数据库表前缀
org.quartz.jobStore.tablePrefix:qrtz_
#数据
org.quartz.jobStore.dataSource:yjlDB #JDBC驱动
org.quartz.dataSource.yjlDB.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.yjlDB.URL:jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.yjlDB.user:root
org.quartz.dataSource.yjlDB.password:abc123
持久化演示
编写任务 MyJobStoreTX
package com.yjl.jdbc;import java.text.SimpleDateFormat;
import java.util.Date;import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;/*** * @author 两个蝴蝶飞* 简单的 JobStoreTx 存储时的任务**/
public class MyJobStoreTX implements Job{@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String nowDate=sdf.format(new Date());System.out.println("MyJobStoreTX 备份数据库的时间是:"+nowDate);}}
编写主程序 MyJobStoreTxDemo
//测试 JobStoreTX 存储
public class MyJobStoreTxDemo {public static void main(String[] args) throws Exception {// 获取SchedulerScheduler scheduler = StdSchedulerFactory.getDefaultScheduler();JobDetail jobDetail = JobBuilder.newJob(MyJobStoreTX.class).withIdentity("jobTX1", "groupTX1").storeDurably(true).build();// 创建 TriggerTrigger trigger = TriggerBuilder.newTrigger().withIdentity("triggerTX1", "groupTX1") // 设置标识.startNow()// 每隔3秒执行一次.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")).build();// 关联 job和 triggerscheduler.scheduleJob(jobDetail, trigger);// 启动 schedulerscheduler.start();}
}
每隔两秒,运行一次。
查看数据库的 qrtz_cron_triggers 表内容:
查看数据库的 qrtz_job_details 表内容:
查看数据库的 qrtz_triggers 表内容:
将 JobDetail, Trigger 的内容放置到了数据库里面,进行了持久性的保存。
- https://blog.csdn.net/yjltx1234csdn/article/details/105871616
- https://www.w3cschool.cn/quartz_doc/quartz_doc-i7oc2d9l.html
【Spring】定时任务相关推荐
- Spring定时任务的几种实现
Spring定时任务的几种实现 spring框架 quartz spring spring-task 定时任务 注解 近日项目开发中需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信 ...
- (转)Spring定时任务的几种实现
Spring定时任务的几种实现 博客分类: spring框架 quartzspringspring-task定时任务注解 Spring定时任务的几种实现 近日项目开发中需要执行一些定时任务,比如需要 ...
- spring定时任务
使用spring定时任务包Quartz时,必须使用Quartz1.85以下版本的. 查看发现spring3.0.5中org.springframework.scheduling.quartz.Cron ...
- Spring定时任务并行(异步)处理
最近项目中遇到一个问题 , 在SpringBoot中设置了定时任务之后 , 在某个点总是没有执行 . 经过搜索研究发现 , spring 定时器任务scheduled-tasks默认配置是单线程串行执 ...
- Spring定时任务@scheduled多线程的使用(@Async注解)
1.开篇 在Spring定时任务@Scheduled注解使用方式浅窥这篇文章里面提及过,spring的定时任务默认是单线程的,他在某些场景下会造成堵塞,那么如果我们想让每一个任务都起一条线程去执行呢? ...
- spring定时任务执行两次的原因与解决方法
spring定时任务执行两次的原因与解决方法 参考文章: (1)spring定时任务执行两次的原因与解决方法 (2)https://www.cnblogs.com/yolanda-lee/p/7339 ...
- Spring定时任务高级使用篇
Spring定时任务高级使用篇 前面一篇博文 <Spring之定时任务基本使用篇> 介绍了Spring环境下,定时任务的简单使用姿势,也留了一些问题,这一篇则希望能针对这些问题给个答案 I ...
- spring定时任务需要在项目启动时执行一次
spring定时任务需要在项目启动时执行一次,然后再按照指定规则执行 在定时任务方法上加注解@PostConstruct,不是spring提供的注解,是JAVA原生注解,在初始化servlet之前执行 ...
- 浅谈Spring定时任务
浅谈Spring定时任务 三种定时任务基于原理 多定时任务并发配置 动态定时任务 定时任务Demo 三种定时任务基于原理 SpringBoot配置定时任务主要有Spring Schedule.JDK自 ...
- 解决spring定时任务执行两次和tomcat部署缓慢的问题
解决spring定时任务执行两次和tomcat部署缓慢的问题 参考文章: (1)解决spring定时任务执行两次和tomcat部署缓慢的问题 (2)https://www.cnblogs.com/Si ...
最新文章
- jira 审批流程_博兴县行政审批服务局推暖心服务工程 企业开办实现“全程网办”_博兴新闻...
- [FJOI2007]轮状病毒
- Python编码规范:IF中的多行条件
- bzoj1588 [HNOI2002]营业额统计
- PHP 基本语法,变量
- 同事说他的应用起不来了,因为我的代码里面多了一个空格!
- android 获取动态时间间隔,android 获取时间间隔
- Mysq数据库备份(win)
- Java-ReentrantLock-NonfairSync/FairSync
- pycharm直接显示所有show value的值(直接打开所有的值)
- 简单的jQuery获取URL的?后带的参数
- TIME_WAIT状态过多的排查
- android其架构图,Android系统架构图,带你直观了解Android基本架构
- 工具篇:金蝶K3工具下载
- STM32F103学习笔记(4)—— 串口通信——发送、接收数据详解
- setw()使用方法
- java timer异常_JAVA Timer的缺陷以及解决办法
- 【potplayer安装及设置LAV Splitter】
- 我对职业规划和未来发展的一些思考
- Zerg虫族的传说[官方资料]