赞
踩
特点是:简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务;能实现简单的定时任务,稍微复杂点(或要求高一些)的定时任务却不好实现。
鉴于 Timer 的缺陷,Java 5 推出了基于线程池设计的 ScheduledExecutor;
特点:每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需要注意的是,只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时间 ScheduledExecutor 都是在轮询任务的状态。
虽然用 ScheduledExecutor 和 Calendar 能够实现复杂任务调度,但实现起来还是比较麻烦,对开发还是不够友善。
Spring 对任务调度的实现支持,可以指定任务的执行时间,但对任务队列和线程池的管控较弱;一般集成于项目中,小任务很方便。
JCronTab 则是一款完全按照 crontab 语法编写的 java 任务调度工具。
特点:
Quartz是 OpenSymphony 开源组织在 Job scheduling 领域又一个开源项目,完全由 Java 开发,可以用来执行定时任务,类似于 java.util.Timer。但是相较于 Timer,Quartz 增加了很多功能:
- <!--quartz依赖-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-quartz</artifactId>
- </dependency>
- spring:
- datasource:
- druid:
- url: jdbc:mysql://127.0.0.1:3306/my_test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
- username: root
- password: admin_123
- # 异步初始化策略,可加快启动速度
- async-init: true
- # 初始化时建立物理连接的个数,同最小连接池数量
- initial-size: 5
- # 最小连接池数量(按需配置)
- min-idle: 5
- # 最大连接池数量(按需配置)
- max-active: 50
- # 获取连接超时, -1表示可一直等待
- max-wait: 6000
- # 是否缓存preparedStatement,缓存prepared-statements,开启的情况下增加字段可能会报错
- pool-prepared-statements: false
- # 缓存preparedStatement cache大小
- max-open-prepared-statements: 20
- # 检测连接是否有效的sql
- validation-query: select 1
- # 申请连接时执行validationQuery检测连接是否有效
- test-on-borrow: false
- # 归还连接时执行validationQuery检测连接是否有效
- test-on-return: false
- # 如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
- test-while-idle: true
- # 两个含义:1.Destroy线程运行周期 2.testWhileIdle判断依据
- time-between-eviction-runs-millis: 60000
- # 连接保持空闲而不被驱逐的最小时间:5分钟
- min-evictable-idle-time-millis: 300000
- # 连接保持空闲而不被驱逐的最大时间: 2天,根据生产mysql配置的wait_time配置=2天
- max-evictable-idle-time-millis: 172800000
- # 是否keep-alive:
- # 即当最小空闲连接空闲了min-evictable-idle-time-millis,执行validationQuery进行keepAlive
- keep-alive: true
- #打印druid统计信息:每天打印一次统计信息日志,后续根据日志帮助优化连接池配置和SQL(按需配置, -1表示关闭)
- time-between-log-stats-millis: 86400000
- filter:
- # 统计filter,druid默认开启
- stat:
- enabled: true
- # 打印慢SQL(如需)
- log-slow-sql: true
- # 耗时多久为慢SQL(按需配置)
- slow-sql-millis: 3000
- driver-class-name: com.mysql.jdbc.Driver
- type: com.alibaba.druid.pool.DruidDataSource
-
- quartz:
- # 任务存储类型
- job-store-type: "jdbc"
- # 关闭时等待任务完成
- wait-for-jobs-to-complete-on-shutdown: false
- # 是否覆盖已有的任务
- overwrite-existing-jobs: true
- # 是否自动启动计划程序
- auto-startup: true
- # 延迟启动
- startup-delay: 0s
- jdbc:
- # 数据库架构初始化模式(never:从不进行初始化;always:每次都清空数据库进行初始化;embedded:只初始化内存数据库(默认值))
- initialize-schema: "always"
- # 相关属性配置
- properties:
- org:
- quartz:
- scheduler:
- # 调度器实例名称
- instanceName: QuartzScheduler
- # 分布式节点ID自动生成
- instanceId: AUTO
- jobStore:
- class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
- driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
- # 表前缀
- tablePrefix: QRTZ_
- # 是否开启集群
- isClustered: true
- # 数据源别名(自定义)
- dataSource: quartz
- # 分布式节点有效性检查时间间隔(毫秒)
- clusterCheckinInterval: 10000
- useProperties: false
- # 线程池配置
- threadPool:
- class: org.quartz.simpl.SimpleThreadPool
- threadCount: 10
- threadPriority: 5
- threadsInheritContextClassLoaderOfInitializingThread: true
方法一: 若在配置项中 initialize-schema: "always"
项目启动后,在数据库中可以看到自动生成了所有以“qrtz_”开头的表。后面 initialize-schema 改成“never” 就行。
方法二:在压缩包该路径下找到对应的数据库 SQL 执行脚本,拷贝出来去 Navicat 执行;
表名 | 说明 |
qrtz_blob_triggers | 以Blob 类型存储的触发器 |
qrtz calendars | 存放日历信息,quartz可配置一个日历来指定一个时间范围 |
qrtz_cron triggers | 存放cron类型的触发器 |
qrtz fired triggers | 存放已触发的触发器 |
qrtz job _details | 存放一个jobDetail信息 |
qrtz job listeners | job监听器 |
qrtz_locks | 存储程序的悲观锁的信息(假如使用了悲观锁) |
qrtz_paused trigger_graps | 存放暂停掉的触发器 |
qrtz scheduler state | 调度器状态 |
qrtz simple triggers | 简单触发器的信息 |
qrtz_trigger_listeners | 触发器监听器 |
(1)Service 接口
- public interface QuartzService {
-
- /**
- * 新增
- *
- * @param jobName
- * @param cron
- * @param jobClassName
- * @return
- */
- String addCronJob(String jobName, String cron, String jobClassName);
-
- /**
- * 停止
- *
- * @param jobName
- * @param jobGroup
- * @param triggerName
- * @param triggerGroup
- * @return
- */
- String deleteCronJob(String jobName, String jobGroup, String triggerName, String triggerGroup);
-
- /**
- * 立即执行,不定时
- *
- * @param jobName
- * @param jobClassName
- * @return
- */
- String executeImmediately(String jobName, String jobClassName);
- }
(2)Service 接口实现类
- @Service
- @Slf4j
- public class QuartzServiceImpl implements QuartzService {
-
- @Autowired
- private Scheduler scheduler;
-
- private static final String DEFAULT_JOB_GROUP = "default_job_group";
-
- private static final String DEFAULT_TRIGGER_GROUP = "default_trigger_group";
-
- private static final String TRIGGER_PRE = "Trigger_";
-
- @Override
- public String addCronJob(String jobName, String cron, String jobClassName) {
- try {
- // 当前任务不存在才进行添加
- JobKey jobKey = JobKey.jobKey(jobName, DEFAULT_JOB_GROUP);
- if (scheduler.checkExists(jobKey)) {
- log.info("[添加定时任务]已存在该作业,jobkey为:{}", jobKey);
- return "已存在该作业";
- }
-
- // 构建 Job
- JobDetail job = JobBuilder.newJob(getClass(jobClassName).getClass())
- .withIdentity(jobKey).build();
-
- // cron表达式定时构造器
- CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
-
- // 构建 Trigger
- Trigger trigger = TriggerBuilder.newTrigger()
- .withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP))
- // .startAt(DateUtil.parseDate(start))
- // .endAt(DateUtil.parseDate(end))
- .withSchedule(cronScheduleBuilder).build();
-
- // 启动调度器
- scheduler.scheduleJob(job, trigger);
- scheduler.start();
- return "SUCCESS";
- } catch (Exception e) {
- log.error("[新增定时任务]失败,报错:", e);
- return "FAIL";
- }
-
- }
-
- @Override
- public String deleteCronJob(String jobName, String jobGroup, String triggerName, String triggerGroup) {
- try {
-
- JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
-
- TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);
-
- Trigger trigger = scheduler.getTrigger(triggerKey);
-
- if (null == trigger) {
- log.info("[停止定时任务]根据triggerName:{}和triggerGroup:{}未查询到相应的trigger!");
- return "SUCCESS";
- }
- //暂停触发器
- scheduler.pauseTrigger(triggerKey);
- // 移除触发器
- scheduler.unscheduleJob(triggerKey);
- // 删除任务
- scheduler.deleteJob(jobKey);
-
- log.info("[停止定时任务]jobName:{},jobGroup:{}, triggerName:{}, triggerGroup:{},停止--------------", jobName, jobGroup, triggerName, triggerGroup);
-
- return "SUCCESS";
-
- } catch (SchedulerException e) {
- log.error("[停止定时任务]失败,报错:", e);
- return "FAIL";
- }
- }
-
-
- public static Job getClass(String className) throws Exception {
- Class<?> classTemp = Class.forName(className);
- return (Job) classTemp.newInstance();
- }
-
- @Override
- public String executeImmediately(String jobName, String jobClassName) {
- try {
- JobKey jobKey = JobKey.jobKey(jobName, DEFAULT_JOB_GROUP);
- JobDetail job = JobBuilder.newJob(getClass(jobClassName).getClass())
- .withIdentity(jobKey).build();
-
- Trigger trigger = TriggerBuilder.newTrigger()
- .withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP))
- .build();
-
- // 启动调度器
- scheduler.scheduleJob(job, trigger);
- scheduler.start();
- return "SUCCESS";
- } catch (Exception e) {
- log.error("[立即执行一次任务,不定时]失败,报错:", e);
- return "FAIL";
- }
- }
- }
(3)具体的业务类
- @Component
- @Slf4j
- public class TaskJob implements Job {
- @Override
- public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
- log.info("=========================业务逻辑====================");
- log.info("jobName:{}", jobExecutionContext.getJobDetail().getKey().getName());
- log.info("jobGroup:{}", jobExecutionContext.getJobDetail().getKey().getGroup());
- log.info("triggerName:{}", jobExecutionContext.getTrigger().getKey().getName());
- log.info("triggerGroup:{}", jobExecutionContext.getTrigger().getKey().getGroup());
- log.info("上次触发时间:{}", DateUtil.formatDateTime(jobExecutionContext.getPreviousFireTime()));
- log.info("本次触发时间:{}", DateUtil.formatDateTime(jobExecutionContext.getFireTime()));
- log.info("下次触发时间:{}", DateUtil.formatDateTime(jobExecutionContext.getNextFireTime()));
- log.info("调度时间:{}", DateUtil.formatDateTime(jobExecutionContext.getScheduledFireTime()));
- }
- }
(4)入参对象
- @Data
- public class JobInfo {
-
- private String jobName;
-
- private String cron;
-
- private String jobGroup;
-
- private String triggerName;
-
- private String triggerGroup;
-
- }
(5)暴露接口层
- @RestController
- @RequestMapping("/quartz")
- public class QuartzController {
-
- @Autowired
- private QuartzService quartzService;
-
- @PostMapping("/createJob")
- public String createJob(@RequestBody JobInfo jobInfo) {
- return quartzService.addCronJob(jobInfo.getJobName(), jobInfo.getCron(), "com.example.springbootzy.quartz.config.TaskJob");
- }
-
- @PostMapping("/deleteJob")
- public String deleteJob(@RequestBody JobInfo jobInfo) {
- return quartzService.deleteCronJob(jobInfo.getJobName(), jobInfo.getJobGroup(), jobInfo.getTriggerName(), jobInfo.getTriggerGroup());
- }
-
- @PostMapping("/executeImmediately")
- public String executeImmediately(@RequestBody JobInfo jobInfo) {
- return quartzService.executeImmediately(jobInfo.getJobName(), "com.example.springbootzy.quartz.config.TaskJob");
- }
-
- }
(6)测试:新增一个“每十秒钟执行一次”的定时任务
(7)测试:删除上述已创建的定时任务
(8)测试:只执行一次
(9)其他
- // 任务暂停
- scheduler.pauseTrigger(TriggerKey.triggerKey("Trigger的name","Trigger的group"));
-
- // 任务恢复
- scheduler.resumeTrigger(TriggerKey.triggerKey("Trigger的name","Trigger的group"));
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。