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

定时任务使用指南

目录定时任务概述jdk自带的TimerSpringBoot自带的定时任务Schedule附:springboot自带的异步任务AsyncQuartzquartz的体系结构sprin

目录

      • 定时任务概述
      • jdk自带的Timer
      • SpringBoot自带的定时任务Schedule
      • 附:springboot自带的异步任务Async
      • Quartz
        • quartz的体系结构
        • springboot整合quartz
      • xxl-job
      • cron表达式

 

定时任务概述

定时任务常见的使用场景

  • 定时开始:铁路定时放票,美团定时发放优惠券、红包,到点定时抢票,定时推送订阅消息。
  • 定时查询:定时从第三方拉取信息,同步到我们自己的库中,比如定时查询合作银行的转账进度、合作快递的物流进度;定时查询优惠券即将过期的,车票、机票、电影票即将要使用的,通知用户。
  • 定时更新状态:商品定时开售,活动到期自动修改为已结束状态,活动结束商品自动下架,超过三十分钟未支付自动取消订单,到期自动解冻账号。
  • 定时备份数据、统计账单流水、更新缓存
  • 定时检测节点心跳,确定节点状态、是否可用
     

常见的定时任务框架

  • jdk自带的Timer:位于util包下,使用简单,但功能单一,对并发任务、复杂任务支持差。
  • springboot自带的Schedule:比jdk自带的Timer好一些,使用简单,但对复杂任务支持较差。
  • Quartz:专业的定时任务框架,功能强大,但quartz是伪分布式的,对分布式支持差。
  • elastic-job:以上三个对分布式的支持都差,集群时容易出现多个节点重复执行定时任务的问题,可以自行实现分布式、将任务状态持久化,但比较麻烦。elastic-job是当当网开源的弹性分布式任务调度框架,已成为apache ShardingSphere的子项目,功能丰富强大,提供web管理界面,可以实现高可用的定时任务、任务分片执行,但需要借助zk实现分布式协调、部署麻烦,且文档较少。
  • xxl-job: 大众点评开源的弹性分布式任务调度框架,轻量、使用简单、易扩展,功能丰富强大,提供web管理界面,文档完善。基于quartz的集群方案,使用数据库持久化任务状态,不需要借助其它服务器。

使用建议:简单的单机定时任务用springboot自带的,复杂的单机任务用quartz,分布式任务用xxl-job。

 

jdk自带的Timer

1、编写定时任务

/** * 继承抽象类TimerTask,实现run()方法 */
public class MyTask extends TimerTask {
@Override
public void run() {
//要定时执行的代码
}
}

 
2、调度、执行

Timer timer = new Timer();
MyTask myTask = new MyTask();
//一个timer实例会用一个单独的后台线程来执行此timer所有的任务
timer.schedule(myTask, 5000L, 1000L);
// timer.scheduleAtFixedRate(myTask, 5000L, 1000L);
//当前线程继续往下执行
//取消指定的定时任务
// myTask.cancel();
//取消此timer中所有的定时任务
// timer.cancel();

timer提供了多个方法来执行定时任务,schedule()、scheduleAtFixedRate()的区别

  • task初次执行时间设置在加入timer之前时:schedule()、scheduleAtFixedRate()都是从当前时间开始执行,但schedule()不会执行之前漏掉的场次;scheduleAtFixedRate()会补上之前漏掉的场次,可能存在一个任务的多场并发执行,容易出现并发问题。
  • 时间间隔:schedule()是从上次任务执行完毕后算,scheduleAtFixedRate()是从上次任务执行开始时算。
     

说的是多个任务并发执行、或者单个任务的多个场次并发执行,但实际只有一个线程来执行,是交替执行的。

如果执行某个任务时抛出异常,会终止此timer所有的任务,可靠性差。

 

SpringBoot自带的定时任务Schedule

不需要额外添加依赖

1、引导类上加 @EnableScheduling
 
2、编写定时任务

@Component //放到spring容器中
public class XxxTask {
@Scheduled(fixedRate = 5000) //指定执行时间。每5000ms执行1次,间隔是距上次执行开始
public void task1(){
//要执行的代码
}
@Scheduled(fixedDelay = 5000) //每5000ms执行1次,间隔是距上次执行结束
public void task2(){
//要执行的代码
}
@Scheduled(fixedRateString = "5000") //fixedRateString、fixedDelayString 分别对应以上2种,只是把值写成字符串形式
public void task3(){
//要执行的代码
}
@Scheduled(cron = "0 0/10 * * * ?") //使用cron表达式指定
public void task4(){
//要执行的代码
}}

把定时|异步任务抽出来,统一放在单独的包、类中,方便维护。
 

3、yml中配置定时任务的线程池

spring:
task:
scheduling:
pool:
#定时任务线程池中的线程数。默认值1,线程池中只有1个线程来执行springboot的定时任务
size: 10

如果不手动修改值,有多个springboot定时任务时,会出现只执行一个定时任务的情况。

springboot的定时任务、异步任务都是使用单独的线程池来执行,如果公司有线程池的使用规范、觉得内置的线程池不满足需求,可以使用自定义的线程池。

 

附:springboot自带的异步任务Async

异步任务可分为2类

  • 不需要返回数据:比如处理日志、发送邮件、短信…
  • 需要返回数据:比如用户支付订单要校验库存、用户余额、风控…都没有问题才允许支付订单

异步任务都是启动一条新线程来执行,不阻塞当前线程,可以提高效率。
 

1、引导类上加 @EnableAsync
 

2、编写要执行的异步任务

@Component //放到spring容器中
@Async //将类中的方法都标识为异步方法。如果不需要标注全部方法,在需要标注的方法上标注@Async即可
public class XxxAsyncTask {
//不需要返回结果的
public void task1(){
//......
}
//需要返回结果的,泛型指定要返回的数据类型
public Future<String> task2(){
//......
//参数是要返回的数据
return new AsyncResult<>("success");
}
public Future<String> task3(){
//......
return new AsyncResult<>("fail");
}
}

 
3、使用异步任务

@Service
public class XxxService {
@Autowired
private XxxAsyncTask xxxAsyncTask;
public void xxx() {
//......
//都是启动新线程来执行task
xxxAsyncTask.task1();
Future<String> task2 = xxxAsyncTask.task2();
Future<String> task3 = xxxAsyncTask.task3();
//...... //当前线程继续往下执行
//当前线程需要使用异步任务的返回结果时,使用get()阻塞当前线程等待异步任务执行完毕
try {
//能指定超时时间的方法都尽量指定超时时间,避免发生异常时一直阻塞线程
String r2 = task2.get(1, TimeUnit.MINUTES);
String r3 = task3.get(1, TimeUnit.MINUTES);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
//...... //当前线程继续往下执行
}
}

 

4、yml中配置异步任务的线程池

spring:
task:
execution:
#线程名称前缀,默认task-
thread-name-prefix: async-task-thread-
#异步线程池配置
pool:
#默认值 8
core-size: 6
#默认值 2147483647
max-size: 20
#异步任务队列的容量,默认值 2147483647
queue-capacity: 300

 

Quartz

quartz的体系结构

3个核心概念

  • 任务 Job
  • 触发器 Trigger
  • 调度器 Scheduler
     

《定时任务使用指南》
 
quartz提供了2种作业存储类型

  • RAMJobStore :默认使用的作业存储类型,将任务调度状态保存在内存中,性能好但不具备持久性,发生故障时会丢失所有的任务状态信息。
  • JDBC作业存储:将任务调度保存到数据库中,但需要我们自行建一些表,表名、字段名都是固定的,quartz会自动持久化任务数据到数据库中,发生故障时也能恢复调度现场,性能略差,需要自行建表、略麻烦。

 

springboot整合quartz

1、依赖
创建项目时勾选 I/O -> Quartz Scheduler,也可以手动添加依赖

<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-quartzartifactId>
dependency>

 

2、编写任务

/** * 继承QuartzJobBean,重写executeInternal(),一个类对应一个任务 * 不需要放到spring容器中 */
public class XxxJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//要执行的代码
}
}

 

3、quartz的配置类

@Configuration
public class QuartzConfig {
/** * 创建JobDetail实例,放到spring容器中 * 一个job对应一个JobDetail实例,有多个job时copy下来改一下 */
@Bean
public JobDetail xxxJobDetail() {
return JobBuilder.newJob(XxxJob.class) //绑定要Job
.withIdentity("xxxJob", "defaultJobGroup") //指定job的名称、所属的组
.storeDurably() //如果此JobDetail实例没有关联Trigger,也不删除此JobDetail实例
.build();
}
/** * 创建trigger实例,放到spring容器中 * 一个job对应一个trigger实例,有多个job时copy下来改一下 */
@Bean
public Trigger xxxTrigger() {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/10 * * * * ?");
return TriggerBuilder.newTrigger()
.withIdentity("xxxTrigger", "defaultTriggerGroup") //指定trigger实例的name、所属的group
.forJob(xxxJobDetail()) //调用获取JobDetail的方法,关联JobDetail
.startNow()
.withSchedule(scheduleBuilder)
.build();
}
}

在yml中输入quartz即可查看quartz的配置项,一般使用springboot提供的默认配置即可,不用修改。

 

xxl-job

官方文档地址:https://www.xuxueli.com/xxl-job
 

1、下载最新稳定版的压缩包,解压,在IDEA中导入

用于生产时不要直接检出最新的源码,最新的源码中可能含有已提交但尚未发布的部分,不稳定,尽量使用已发布的版本。包含的模块如下

  • xxl-job:父项目,包含下面3个子项目
  • xxl-job-core:核心模块,提供公共依赖
  • xxl-job-admin:调度中心,提供web界面的控制台
  • xxl-job-executor-samples:执行器示例,作为一个父项目,包含多个版本的子项目xxl-job-executor-sample-xxx,推荐使用springboot版本 xxl-job-executor-sample-springboot,不需要的可以删除。
     

2、在navicat中,执行doc/db下的sql脚本初始化xxl-job的数据库

 

3、xxl-job-admin 调度中心(web控制台)

  • application.properties:修改应用使用的端口、访问路径、数据库配置、报警邮箱配置。邮箱配置可以不管,但不能注释掉。
  • logback.xml:修改日志配置

xxl-job-admin集群部署时,各xxl-job-admin节点务必连接同一个mysql数据库、节点机器的时钟务必保持一致;如果mysql做主从,xxl-job-admin各节点务必强制走主库。
 

启动应用后,登录系统,初始化系统数据

  • 在用户管理中修改密码、增删用户
  • 在任务管理中删除示例任务
  • 在执行器管理中增删、修改执行器

 

4、在xxl-job-executor-sample-springboot 执行器中编写定时任务

  • application.properties:修改以下配置项

server.port=8081
#调度中心的访问地址,有多个url时逗号分隔
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
#要注册到调度中心的哪个执行器上,执行器要是
xxl.job.executor.appname=xxx
#与调度中心通信使用的端口
xxl.job.executor.port=9999
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler

  • logback.xml:修改日志配置

 

  • 仿照 service.jobhandler.SampleXxlJob 写定时任务

/** * 简单任务示例(Bean模式) */
@XxlJob("demoJobHandler") //@XxlJob将方法标识为一个job(定时任务),指定此job的name
public ReturnT<String> demoJobHandler(String param) throws Exception { //参数是调度中心控制台创建任务实例时指定的任务参数
//.....
XxlJobLogger.log("xxx"); //XxlJobLogger.log()打印的是日志只显示在调度中心->调度日志->执行日志中,不会输出到std、file之类的logger终端
return ReturnT.FAIL; //ReturnT.SUCCESS是任务执行成功,ReturnT.FAIL是任务执行失败
}

 

  • 启动应用,在调度中心->任务管理->增删、修改任务实例。运行模式使用BEAN,JobHandler指定代码中定义的job的name。

#执行器集群部署时,调度中心支持的路由策略如下
ROUND(轮询)
FIRST(第一个):固定选择第一个节点
LAST(最后一个):固定选择最后一个节点
RANDOM(随机):随机选择在线的节点
CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某个节点,且所有任务均匀散列在不同节点上。
LEAST_FREQUENTLY_USED(最不经常使用):优先选择使用频率最低的节点
LEAST_RECENTLY_USED(最近最久未使用):优先选择最久未使用的节点
FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的节点选定为目标执行器并发起调度
BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的节点选定为目标执行器并发起调度
SHARDING_BROADCAST(分片广播):广播触发对应集群中所有节点执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务
#调度过于密集,执行器来不及处理任务实例时,调度中心提供的阻塞处理策略如下
单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO队列并以串行方式运行
丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败
覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务

 

cron表达式

一共7个字段,依次是

  • second:0-59
  • minute:0-59
  • hour:0-23
  • day of month:1-31
  • month:1-12
  • day of week:1-7
  • year:1970-2099

前6个必填,第7个选填、可以缺省。
 

使用示例

# ?表示不关心该字段的值,用于第六个(day of week)字段
# 一般前面写0,后面写*,*表示任意值
#直接写数字表示在指定时间执行。如果年月日时分秒都写死,则只在指定时间执行一次
0 0 10 * * ? # 每天10点执行1次
0 0 0 1 * ? #每月1号凌晨执行1次
# */n 表示每隔n执行1次,也可以写成0/n
0 0 */2 * * ? #每隔2小时执行1次
# -表示区间
0 0 10-14 * * ? # 每天的10、11、12、13、14点各执行1次
# ,表示或者
0 0 9,15,21 * * ? #每天9点、15点、21点各执行1次

推荐阅读
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
author-avatar
0只0为0等0你0
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有