springboot集成schedule
背景
在项目开发过程中,我们经常需要执行具有周期性的任务。通过定时任务可以很好的帮助我们实现。
我们拿常用的几种定时任务框架做一个比较:
从以上表格可以看出,Spring Schedule框架功能完善,简单易用。对于中小型项目需求,Spring Schedule是完全可以胜任的。
1、springboot集成schedule
1.1 添加maven依赖包
由于Spring Schedule包含在spring-boot-starter基础模块中了,所有不需要增加额外的依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1.2 启动类,添加启动注解
在springboot入口或者配置类中增加@EnableScheduling注解即可启用定时任务。
1
2
3
4
5
6
7
|
@EnableScheduling @SpringBootApplication public class ScheduleApplication { public static void main(String[] args) { SpringApplication.run(ScheduleApplication. class , args); } } |
1.3.添加定时任务
我们将对Spring Schedule三种任务调度器分别举例说明。
1.3.1 Cron表达式
类似于Linux下的Cron表达式时间定义规则。Cron表达式由6或7个空格分隔的时间字段组成,如下图:
常用表达式:
举个栗子:
添加一个work()方法,每10秒执行一次。
注意:当方法的执行时间超过任务调度频率时,调度器会在下个周期执行。
如:假设work()方法在第0秒开始执行,方法执行了12秒,那么下一次执行work()方法的时间是第20秒。
1
2
3
4
5
6
7
|
@Component public class MyTask { @Scheduled (cron = "0/10 * * * * *" ) public void work() { // task execution logic } } |
1.3.2 固定间隔任务
下一次的任务执行时间,是从方法最后一次任务执行结束时间开始计算。并以此规则开始周期性的执行任务。
举个栗子:
添加一个work()方法,每隔10秒执行一次。
例如:假设work()方法在第0秒开始执行,方法执行了12秒,那么下一次执行work()方法的时间是第22秒。
1
2
3
4
|
@Scheduled (fixedDelay = 1000 * 10 ) public void work() { // task execution logic } |
1.3.3 固定频率任务
按照指定频率执行任务,并以此规则开始周期性的执行调度。
举个栗子:
添加一个work()方法,每10秒执行一次。
注意:当方法的执行时间超过任务调度频率时,调度器会在当前方法执行完成后立即执行下次任务。
例如:假设work()方法在第0秒开始执行,方法执行了12秒,那么下一次执行work()方法的时间是第12秒。
1
2
3
4
|
@Scheduled (fixedRate = 1000 * 10 ) public void work() { // task execution logic } |
2、配置TaskScheduler线程池
在实际项目中,我们一个系统可能会定义多个定时任务。那么多个定时任务之间是可以相互独立且可以并行执行的。
通过查看org.springframework.scheduling.config.ScheduledTaskRegistrar源代码,发现spring默认会创建一个单线程池。这样对于我们的多任务调度可能会是致命的,当多个任务并发(或需要在同一时间)执行时,任务调度器就会出现时间漂移,任务执行时间将不确定。
1
2
3
4
5
6
7
|
protected void scheduleTasks() { if ( this .taskScheduler == null ) { this .localExecutor = Executors.newSingleThreadScheduledExecutor(); this .taskScheduler = new ConcurrentTaskScheduler( this .localExecutor); } //省略... } |
2.1 自定义线程池
新增一个配置类,实现SchedulingConfigurer接口。重写configureTasks方法,通过taskRegistrar设置自定义线程池。
1
2
3
4
5
6
7
8
9
10
11
12
|
@Configuration public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(taskExecutor()); } @Bean (destroyMethod= "shutdown" ) public Executor taskExecutor() { return Executors.newScheduledThreadPool( 20 ); } } |
3、实际应用中的问题
3.1 Web应用中的启动和关闭问题
我们知道通过spring加载或初始化的Bean,在服务停止的时候,spring会自动卸载(销毁)。但是由于线程是JVM级别的,如果用户在Web应用中启动了一个线程,那么这个线程的生命周期并不会和Web应用保持一致。也就是说,即使Web应用停止了,这个线程依然没有结束(死亡)。
解决方法:
1)当前对象是通过spring初始化
spring在卸载(销毁)实例时,会调用实例的destroy方法。通过实现DisposableBean接口覆盖destroy方法实现。在destroy方法中主动关闭线程。
1
2
3
4
5
6
7
8
9
10
|
@Component public class MyTask implements DisposableBean{ @Override public void destroy() throws Exception { //关闭线程或线程池 ThreadPoolTaskScheduler scheduler = (ThreadPoolTaskScheduler)applicationContext.getBean( "scheduler" ); scheduler.shutdown(); } //省略... } |
2)当前对象不是通过spring初始化(管理)
那么我们可以增加一个Servlet上下文监听器,在Servlet服务停止的时候主动关闭线程。
1
2
3
4
5
6
7
|
public class MyTaskListenter implements ServletContextListener{ @Override public void contextDestroyed(ServletContextEvent arg0) { //关闭线程或线程池 } //省略... } |
3.2 分布式部署问题
在实际项目中,我们的系统通常会做集群、分布式或灾备部署。那么定时任务就可能出现并发问题,即同一个任务在多个服务器上同时在运行。
解决方法(分布式锁):
1)通过数据库表锁
2)通过缓存中间件
3)通过Zookeeper实现
参考链接:https://www.cnblogs.com/skychenjiajun/p/9057379.html?utm_source=tuicool&utm_medium=referral
springboot集成schedule的更多相关文章
- springboot集成schedule(深度理解)
背景 在项目开发过程中,我们经常需要执行具有周期性的任务.通过定时任务可以很好的帮助我们实现. 我们拿常用的几种定时任务框架做一个比较: 从以上表格可以看出,Spring Schedule框架功能完善 ...
- SpringBoot集成PageHelper时出现“在系统中发现了多个分页插件,请检查系统配置!”
近日在项目中使用SpringBoot集成PageHelper后,跑单元测试时出现了"在系统中发现了多个分页插件,请检查系统配置!"这个问题. 如下图所示: org.mybatis. ...
- springboot集成调用Azkaban
springboot集成调用Azkaban 一. 说明 1.Azkaban是由Linkedin公司推出的一个批量工作流任务调度器,主要用于在一个工作流内以一个特定的顺序运行一组工作和流程,它的配置是通 ...
- 【springBoot】springBoot集成redis的key,value序列化的相关问题
使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...
- SpringBoot集成security
本文就SpringBoot集成Security的使用步骤做出解释说明.
- springboot集成Actuator
Actuator监控端点,主要用来监控与管理. 原生端点主要分为三大类:应用配置类.度量指标类.操作控制类. 应用配置类:获取应用程序中加载的配置.环境变量.自动化配置报告等与SpringBoot应用 ...
- SpringBoot集成Shiro并用MongoDB做Session存储
之前项目鉴权一直使用的Shiro,那是在Spring MVC里面使用的比较多,而且都是用XML来配置,用Shiro来做权限控制相对比较简单而且成熟,而且我一直都把Shiro的session放在mong ...
- SpringBoot集成redis的key,value序列化的相关问题
使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...
- springboot集成mybatis(二)
上篇文章<springboot集成mybatis(一)>介绍了SpringBoot集成MyBatis注解版.本文还是使用上篇中的案例,咱们换个姿势来一遍^_^ 二.MyBatis配置版(X ...
随机推荐
- 我的学习目标(目前已初步学习完Java语言基础)
操作系统.尤其是内存/线程/进程方面 计算机网络协议,重点关注 TCP/UDP/HTTP. 数据结构与算法. 数据库 设计模式,熟练掌握常用的几种设计模式. Java语言基础.熟悉java语言基础,了 ...
- iOS .tbd
http://stackoverflow.com/questions/31450690/why-xcode-7-shows-tbd-instead-of-dylib http://www.jiansh ...
- osgi Quick Start
今天有空看了下osgi,根据官网的说明来快速搭建一个服务,把步骤记下来. (官网地址:https://enroute.osgi.org/tutorial/020-tutorial_qs.html) 一 ...
- 2019 Power BI最Top50面试题,助你面试脱颖而出系列<下>
Q:什么是附加题? A:这就是常说的送分可选题,可做可不做:也可以说是加分项,是难点提升题. Power BI 面试题 — 附加题 33)什么是 Power View? 答案: Power View是 ...
- 【转】干货 | 【虚拟货币钱包】从 BIP32、BIP39、BIP44 到 Ethereum HD Wallet
虚拟货币钱包 钱包顾名思义是存放$$$.但在虚拟货币世界有点不一样,我的帐户资讯(像是我有多少钱)是储存在区块链上,实际存在钱包中的是我的帐户对应的 key.有了这把 key 我就可以在虚拟货币世界证 ...
- HyperLogLog算法
项目在统计UV/PV时用到了Druid的Hyper hyperunique算法,书上介绍这种算法求出的UV/PV存在一定误差,因此需要了解下误差来自哪里. 实现去重功能,最简单的就是使用set记录集合 ...
- Golang微服务:Micro介绍
官方文档地址 https://micro.mu/docs/index.html Tookit API HTTP接入网关.反向代理或将HTTP转为RPC请求调用后端服务 Web 一个web应用程序,默认 ...
- SQL中Union和Union All
工作中,看到大佬写的一段SQL,查询了五个表中的数据,最后求某个收入的总和,其中使用了Union All,因此在这里记录一下我从中学到的东西 先上语法 Union: [ Select语句1 ] U ...
- COOKIE和SESSION之间的区别以及用法
一.Session简单介绍 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下).因此,在需要保存用户数据时,服务 ...
- Python之PIL库的运用、GIF处理
一.PIL库简介 PIL(Python Image Library)库是Python语言的第三方库,它支持图像存储.显示和处理,它能够处理几乎所有图片格式,可以完成对图像的缩放.剪裁.折叠以及像图片添 ...