当前位置:   article > 正文

Springboot 整合 Quartz(定时任务框架)_springboot整合quartz框架

springboot整合quartz框架

一、java 定时任务调度的实现方式

1、Timer

特点是:简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务;能实现简单的定时任务,稍微复杂点(或要求高一些)的定时任务却不好实现。

2、ScheduledExecutor

鉴于 Timer 的缺陷,Java 5 推出了基于线程池设计的 ScheduledExecutor;
特点:每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需要注意的是,只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时间 ScheduledExecutor 都是在轮询任务的状态。
虽然用 ScheduledExecutor 和 Calendar 能够实现复杂任务调度,但实现起来还是比较麻烦,对开发还是不够友善。

3、Spring Scheduler

Spring 对任务调度的实现支持,可以指定任务的执行时间,但对任务队列和线程池的管控较弱;一般集成于项目中,小任务很方便。

4、开源工具包 JCronTab

JCronTab 则是一款完全按照 crontab 语法编写的 java 任务调度工具。
特点:

  • 可指定任务的执行时间;
  • 提供完全按照 Unix 的 UNIX-POSIX crontab 的格式来规定时间;
  • 支持多种任务调度的持久化方法,包括普通文件、数据库以及 XML 文件进行持久化;
  • JCronTab 内置了发邮件功能,可以将任务执行结果方便地发送给需要被通知的人;
  • 设计和部署是高性能并可扩展。

5、开源工具包 Quartz

  • 具有强大的调度功能,很容易与 Spring 集成,形成灵活可配置的调度功能;
  • 调度环境的持久化机制:可以保存并恢复调度现场,即使系统因为故障关闭,任务调度现场的数据并不会丢失;timer 没有这些特点;
  • 灵活的应用方式:可以灵活的定义触发器调度的时间表,并可以对触发器与任务进行关联映射;
  • 分布式与集群能力;

二、什么是 Quartz?

Quartz是 OpenSymphony 开源组织在 Job scheduling 领域又一个开源项目,完全由 Java 开发,可以用来执行定时任务,类似于 java.util.Timer。但是相较于 Timer,Quartz 增加了很多功能:

  • 持久性作业 - 就是保持调度定时的状态;
  • 作业管理 - 对调度作业进行有效的管理;

三、Quartz的相关概念

  • Scheduler:调度器,进行任务调度;quartz的大脑。
  • Job:业务job,亦可称业务组件;定时任务的具体执行业务需要实现此接口,调度器会调用此接口的execute方法完成我们的定时业务。
  • JobDetail:用来定义业务Job的实例,我们可以称之为quartz job,很多时候我们谈到的job指的是JobDetail。
  • Trigger:触发器,用来定义一个指定的Job何时被执行。
  • JobBuilder:Job构建器,用来定义或创建JobDetail的实例;JobDetail限定了只能是Job的实例。
  • TriggerBuilder:触发器构建器,用来定义或创建触发器的实例。

四、springboot整合Quartz

1、pom.xml中引入依赖

  1. <!--quartz依赖-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-quartz</artifactId>
  5. </dependency>

2、application.xml 中添加配置项

  1. spring:
  2. datasource:
  3. druid:
  4. url: jdbc:mysql://127.0.0.1:3306/my_test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
  5. username: root
  6. password: admin_123
  7. # 异步初始化策略,可加快启动速度
  8. async-init: true
  9. # 初始化时建立物理连接的个数,同最小连接池数量
  10. initial-size: 5
  11. # 最小连接池数量(按需配置)
  12. min-idle: 5
  13. # 最大连接池数量(按需配置)
  14. max-active: 50
  15. # 获取连接超时, -1表示可一直等待
  16. max-wait: 6000
  17. # 是否缓存preparedStatement,缓存prepared-statements,开启的情况下增加字段可能会报错
  18. pool-prepared-statements: false
  19. # 缓存preparedStatement cache大小
  20. max-open-prepared-statements: 20
  21. # 检测连接是否有效的sql
  22. validation-query: select 1
  23. # 申请连接时执行validationQuery检测连接是否有效
  24. test-on-borrow: false
  25. # 归还连接时执行validationQuery检测连接是否有效
  26. test-on-return: false
  27. # 如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
  28. test-while-idle: true
  29. # 两个含义:1.Destroy线程运行周期 2.testWhileIdle判断依据
  30. time-between-eviction-runs-millis: 60000
  31. # 连接保持空闲而不被驱逐的最小时间:5分钟
  32. min-evictable-idle-time-millis: 300000
  33. # 连接保持空闲而不被驱逐的最大时间: 2天,根据生产mysql配置的wait_time配置=2
  34. max-evictable-idle-time-millis: 172800000
  35. # 是否keep-alive:
  36. # 即当最小空闲连接空闲了min-evictable-idle-time-millis,执行validationQuery进行keepAlive
  37. keep-alive: true
  38. #打印druid统计信息:每天打印一次统计信息日志,后续根据日志帮助优化连接池配置和SQL(按需配置, -1表示关闭)
  39. time-between-log-stats-millis: 86400000
  40. filter:
  41. # 统计filter,druid默认开启
  42. stat:
  43. enabled: true
  44. # 打印慢SQL(如需)
  45. log-slow-sql: true
  46. # 耗时多久为慢SQL(按需配置)
  47. slow-sql-millis: 3000
  48. driver-class-name: com.mysql.jdbc.Driver
  49. type: com.alibaba.druid.pool.DruidDataSource
  50. quartz:
  51. # 任务存储类型
  52. job-store-type: "jdbc"
  53. # 关闭时等待任务完成
  54. wait-for-jobs-to-complete-on-shutdown: false
  55. # 是否覆盖已有的任务
  56. overwrite-existing-jobs: true
  57. # 是否自动启动计划程序
  58. auto-startup: true
  59. # 延迟启动
  60. startup-delay: 0s
  61. jdbc:
  62. # 数据库架构初始化模式(never:从不进行初始化;always:每次都清空数据库进行初始化;embedded:只初始化内存数据库(默认值))
  63. initialize-schema: "always"
  64. # 相关属性配置
  65. properties:
  66. org:
  67. quartz:
  68. scheduler:
  69. # 调度器实例名称
  70. instanceName: QuartzScheduler
  71. # 分布式节点ID自动生成
  72. instanceId: AUTO
  73. jobStore:
  74. class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
  75. driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  76. # 表前缀
  77. tablePrefix: QRTZ_
  78. # 是否开启集群
  79. isClustered: true
  80. # 数据源别名(自定义)
  81. dataSource: quartz
  82. # 分布式节点有效性检查时间间隔(毫秒)
  83. clusterCheckinInterval: 10000
  84. useProperties: false
  85. # 线程池配置
  86. threadPool:
  87. class: org.quartz.simpl.SimpleThreadPool
  88. threadCount: 10
  89. threadPriority: 5
  90. threadsInheritContextClassLoaderOfInitializingThread: true

3、创建 Quartz 框架使用的 11 张表

方法一: 若在配置项中 initialize-schema: "always" 

项目启动后,在数据库中可以看到自动生成了所有以“qrtz_”开头的表。后面 initialize-schema 改成“never” 就行。

方法二:在压缩包该路径下找到对应的数据库 SQL 执行脚本,拷贝出来去 Navicat 执行;

4、11张表说明

表名说明
qrtz_blob_triggers以Blob 类型存储的触发器
qrtz calendars存放日历信息,quartz可配置一个日历来指定一个时间范围
qrtz_cron triggers存放cron类型的触发器
qrtz fired triggers存放已触发的触发器
qrtz job _details存放一个jobDetail信息
qrtz job listenersjob监听器
qrtz_locks存储程序的悲观锁的信息(假如使用了悲观锁)
qrtz_paused trigger_graps存放暂停掉的触发器
qrtz scheduler state调度器状态
qrtz simple triggers简单触发器的信息
qrtz_trigger_listeners触发器监听器

5、简单的 demo 演示

(1)Service 接口

  1. public interface QuartzService {
  2. /**
  3. * 新增
  4. *
  5. * @param jobName
  6. * @param cron
  7. * @param jobClassName
  8. * @return
  9. */
  10. String addCronJob(String jobName, String cron, String jobClassName);
  11. /**
  12. * 停止
  13. *
  14. * @param jobName
  15. * @param jobGroup
  16. * @param triggerName
  17. * @param triggerGroup
  18. * @return
  19. */
  20. String deleteCronJob(String jobName, String jobGroup, String triggerName, String triggerGroup);
  21. /**
  22. * 立即执行,不定时
  23. *
  24. * @param jobName
  25. * @param jobClassName
  26. * @return
  27. */
  28. String executeImmediately(String jobName, String jobClassName);
  29. }

(2)Service 接口实现类

  1. @Service
  2. @Slf4j
  3. public class QuartzServiceImpl implements QuartzService {
  4. @Autowired
  5. private Scheduler scheduler;
  6. private static final String DEFAULT_JOB_GROUP = "default_job_group";
  7. private static final String DEFAULT_TRIGGER_GROUP = "default_trigger_group";
  8. private static final String TRIGGER_PRE = "Trigger_";
  9. @Override
  10. public String addCronJob(String jobName, String cron, String jobClassName) {
  11. try {
  12. // 当前任务不存在才进行添加
  13. JobKey jobKey = JobKey.jobKey(jobName, DEFAULT_JOB_GROUP);
  14. if (scheduler.checkExists(jobKey)) {
  15. log.info("[添加定时任务]已存在该作业,jobkey为:{}", jobKey);
  16. return "已存在该作业";
  17. }
  18. // 构建 Job
  19. JobDetail job = JobBuilder.newJob(getClass(jobClassName).getClass())
  20. .withIdentity(jobKey).build();
  21. // cron表达式定时构造器
  22. CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
  23. // 构建 Trigger
  24. Trigger trigger = TriggerBuilder.newTrigger()
  25. .withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP))
  26. // .startAt(DateUtil.parseDate(start))
  27. // .endAt(DateUtil.parseDate(end))
  28. .withSchedule(cronScheduleBuilder).build();
  29. // 启动调度器
  30. scheduler.scheduleJob(job, trigger);
  31. scheduler.start();
  32. return "SUCCESS";
  33. } catch (Exception e) {
  34. log.error("[新增定时任务]失败,报错:", e);
  35. return "FAIL";
  36. }
  37. }
  38. @Override
  39. public String deleteCronJob(String jobName, String jobGroup, String triggerName, String triggerGroup) {
  40. try {
  41. JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
  42. TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);
  43. Trigger trigger = scheduler.getTrigger(triggerKey);
  44. if (null == trigger) {
  45. log.info("[停止定时任务]根据triggerName:{}和triggerGroup:{}未查询到相应的trigger!");
  46. return "SUCCESS";
  47. }
  48. //暂停触发器
  49. scheduler.pauseTrigger(triggerKey);
  50. // 移除触发器
  51. scheduler.unscheduleJob(triggerKey);
  52. // 删除任务
  53. scheduler.deleteJob(jobKey);
  54. log.info("[停止定时任务]jobName:{},jobGroup:{}, triggerName:{}, triggerGroup:{},停止--------------", jobName, jobGroup, triggerName, triggerGroup);
  55. return "SUCCESS";
  56. } catch (SchedulerException e) {
  57. log.error("[停止定时任务]失败,报错:", e);
  58. return "FAIL";
  59. }
  60. }
  61. public static Job getClass(String className) throws Exception {
  62. Class<?> classTemp = Class.forName(className);
  63. return (Job) classTemp.newInstance();
  64. }
  65. @Override
  66. public String executeImmediately(String jobName, String jobClassName) {
  67. try {
  68. JobKey jobKey = JobKey.jobKey(jobName, DEFAULT_JOB_GROUP);
  69. JobDetail job = JobBuilder.newJob(getClass(jobClassName).getClass())
  70. .withIdentity(jobKey).build();
  71. Trigger trigger = TriggerBuilder.newTrigger()
  72. .withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP))
  73. .build();
  74. // 启动调度器
  75. scheduler.scheduleJob(job, trigger);
  76. scheduler.start();
  77. return "SUCCESS";
  78. } catch (Exception e) {
  79. log.error("[立即执行一次任务,不定时]失败,报错:", e);
  80. return "FAIL";
  81. }
  82. }
  83. }

(3)具体的业务类

  1. @Component
  2. @Slf4j
  3. public class TaskJob implements Job {
  4. @Override
  5. public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
  6. log.info("=========================业务逻辑====================");
  7. log.info("jobName:{}", jobExecutionContext.getJobDetail().getKey().getName());
  8. log.info("jobGroup:{}", jobExecutionContext.getJobDetail().getKey().getGroup());
  9. log.info("triggerName:{}", jobExecutionContext.getTrigger().getKey().getName());
  10. log.info("triggerGroup:{}", jobExecutionContext.getTrigger().getKey().getGroup());
  11. log.info("上次触发时间:{}", DateUtil.formatDateTime(jobExecutionContext.getPreviousFireTime()));
  12. log.info("本次触发时间:{}", DateUtil.formatDateTime(jobExecutionContext.getFireTime()));
  13. log.info("下次触发时间:{}", DateUtil.formatDateTime(jobExecutionContext.getNextFireTime()));
  14. log.info("调度时间:{}", DateUtil.formatDateTime(jobExecutionContext.getScheduledFireTime()));
  15. }
  16. }

(4)入参对象

  1. @Data
  2. public class JobInfo {
  3. private String jobName;
  4. private String cron;
  5. private String jobGroup;
  6. private String triggerName;
  7. private String triggerGroup;
  8. }

(5)暴露接口层

  1. @RestController
  2. @RequestMapping("/quartz")
  3. public class QuartzController {
  4. @Autowired
  5. private QuartzService quartzService;
  6. @PostMapping("/createJob")
  7. public String createJob(@RequestBody JobInfo jobInfo) {
  8. return quartzService.addCronJob(jobInfo.getJobName(), jobInfo.getCron(), "com.example.springbootzy.quartz.config.TaskJob");
  9. }
  10. @PostMapping("/deleteJob")
  11. public String deleteJob(@RequestBody JobInfo jobInfo) {
  12. return quartzService.deleteCronJob(jobInfo.getJobName(), jobInfo.getJobGroup(), jobInfo.getTriggerName(), jobInfo.getTriggerGroup());
  13. }
  14. @PostMapping("/executeImmediately")
  15. public String executeImmediately(@RequestBody JobInfo jobInfo) {
  16. return quartzService.executeImmediately(jobInfo.getJobName(), "com.example.springbootzy.quartz.config.TaskJob");
  17. }
  18. }

(6)测试:新增一个“每十秒钟执行一次”的定时任务

(7)测试:删除上述已创建的定时任务

(8)测试:只执行一次

(9)其他

  1. // 任务暂停
  2. scheduler.pauseTrigger(TriggerKey.triggerKey("Trigger的name","Trigger的group"));
  3. // 任务恢复
  4. scheduler.resumeTrigger(TriggerKey.triggerKey("Trigger的name","Trigger的group"));

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/488044
推荐阅读
相关标签
  

闽ICP备14008679号