1、Quartz 介绍
1. Quartz 是什么
Quartz是一个定时任务调度框架; 有三大核心部分组成,分别是Job,Trigger、Scheduler。Quartz官网:Quartz Enterprise Job Scheduler 。
核心:
-
Job:任务,可以理解成你要做什么事,具体要执行的业务逻辑,比如:发送短信、同步数据、发送邮件等。
-
JobDetail:基于Job,进一步包装,其中关联一个Job,并为Job指定更详细的属性,比如标识等,用于描述Job任务,(任务的名称、任务的分组) - ID。
-
Trigger:触发器,任务执行的时机,定义任务执行的时间规则,可以理解成你什么时候去做,用来定义Job触发条件、触发时间、触发间隔、终止时间等。
-
SimpleTrigger:以一定的时间间隔(单位是毫秒)执行的任务,简单定义任务开始时间、执行频次、结束时间等 。
-
CronTrigger:适合更复制的任务,它支持类似于于Linus Cron的语法(并且更强大)。使用CRON表达式定义。
-
-
Scheduler:调度器,通过Trigger定义的时机来执行Job任务,可以理解成你什么时候要去做什么事,它会启动Trigger去执行Job。
2、CRON表达式
1. CORN 表达式组成
Quartz Cron表达式由7个子表达式组成,用于描述时间的各个细节,这些表达式用空格分隔
[秒] [分] [小时] [天、日期] [月] [周] [年](可选)
seconds Minutes Hours Day-of-Month Month Day-of-Week YEAR
时间字段 | 是否必填 | 取值范围 | 允许的特殊字符 |
---|---|---|---|
秒 | 是 | 0-59 | , * - / |
分 | 是 | 0-59 | , * - / |
小时 | 是 | 0-23 | , * - / |
天/日期(每月的 几号) | 是 | 1-31 | , * - / ? L W |
月 | 是 | 1-12(JAN-DEC) | , * - / |
周(星期几) | 是 | 1-7(SUN-SAT)(1周天 7周六) | , * - / ? L # |
年 | 否 | 空、1970-2099 | , * - / |
2. CORN 表达式字符
特 殊字 符 | 解释 |
---|---|
, | 表示或的关系。如用在分钟:1,5 - 表示在第一分钟和第5分钟执行 |
* | 每秒、每分、每小时、每天、每月等。在秒上写*,表示每秒钟都要执 行 |
- | 表示范围。如用在秒上:1-20 - 表示在1-20秒都要执行 |
/ | 表示每隔多长时间执行。用在秒上:*/5或0/5 - 表示每隔5秒执行一次 |
? | 只能用到天和周上 1.如果[天]字段上用了 或确切的某一天,那么 [ 周 ] 只能用 ? 2. 如果 [ 周 ] 字段上用了 或确切的某一个周几,那么[天]只能用? |
L | 表示每个月的最后一天或者每个月的最后星期几,[周]写6L - 表示每个 月最后一个星期5 |
W | 只能用在[天、日期]的字段,表示离这个日期最近的一个工作日 比如: 1. 25W:表示离这个月25号最近的一个工作日 如果25号是一个工作日,那么就是这天执行 如果25号是周六,那么应该是24号周五执行 如果25号是周天,那么应该是下周一26号执行 2.日期不能夸月 1W:离1号最近的工作日,如果1号是周六,那么离1号最近的工作日 应该是当月3号,而不是上个月的最后一天; 3.只能指定单一日期,不能用范围 |
# | 用在[周]上,表示每个月第几个周几,例如在[周上]写SUN#2 - 表示每个 月第二个周天 母亲节的例子:母亲节当天8点执行一个短信发送任务 0 0 8 ? 5 SUN#2 |
3. CORN 表达式示例
举例 | 表达式 |
---|---|
周一到周五8:50提醒打卡 | 0 50 8 ? * MON-FRI |
每天凌晨1点到1点59分,每隔5分钟执行一次 | 0 0/5 1 * * ? |
每隔5秒执行一次 | */5 * * * * ? |
每天5-15点整点触发 | 0 0 5-15 * * ? |
表示每个星期三中午12点 | 0 0 12 ? * WED |
每月最后一天23点执行一次 | 0 0 23 L * ? |
每月的最后一个星期五上午10:15触发 | 0 15 10 ? * 6L |
每月的第三个星期五上午10:15触发 | 2,15 10 ? * FRI#3 |
3、Quartz 使用
-
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
-
书写 quartz.properties
#可以设置为任意,用在 JDBC JobStore中来唯一标识实例,但是所有集群节点中必须相同。 org.quartz.scheduler.instanceName = order # 属性为 AUTO即可,基于主机名和时间戳来产生实例 ID org.quartz.scheduler.instanceId = AUTO ## 线程 org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 30 org.quartz.threadPool.threadPriority = 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true ## 存储 org.quartz.jobStore.misfireThreshold = 60000 #JobStoreTX,将任务持久化到数据库中。因为集群中节点依赖于数据库来传播 Scheduler实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储,不能在集群中使用 RAMJobStore。 org.quartz.jobStore.class = org.springframework.scheduling.quartz.LocalDataSourceJobStore org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.maxMisfiresToHandleAtATime=10 #值 true,表示 Scheduler实例要参与到一个集群当中。这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。 org.quartz.jobStore.isClustered = true #定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)。 org.quartz.jobStore.clusterCheckinInterval = 5000
-
定义 job 任务
public class OrderJob extends QuartzJobBean { @Autowired OrderService orderService; @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println(DateUtil.now()); // 取消超时订单 orderService.cancelOrder(); } }
-
书写配置类
@Configuration public class QuartzConfig { @Autowired DataSource dataSource; @Autowired DataSourceTransactionManager transactionManager; @Bean public JobDetailFactoryBean orderJobDetail(){ JobDetailFactoryBean factoryBean = new JobDetailFactoryBean(); factoryBean.setName("cancelOrder"); factoryBean.setGroup("cancelOrder"); factoryBean.setJobClass(OrderJob.class); return factoryBean; } @Bean public CronTriggerFactoryBean orderTrigger(){ CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean(); factoryBean.setName("cancelOrder"); factoryBean.setGroup("cancelOrder"); factoryBean.setJobDetail(orderJobDetail().getObject()); factoryBean.setCronExpression("0/30 * * * * ?"); return factoryBean; } @Bean public JobFactory jobFactory(){ return new SpringBeanJobFactory(); } @Bean public SchedulerFactoryBean scheduler(){ SchedulerFactoryBean factoryBean = new SchedulerFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTransactionManager(transactionManager); factoryBean.setConfigLocation(new PathMatchingResourcePatternResolver() .getResource("classpath:quartz.properties")); factoryBean.setTriggers(orderTrigger().getObject()); factoryBean.setJobFactory(jobFactory()); return factoryBean; } }
4、 任务操作(暂停、恢复、创建)
创建 job 实体
@Data
public class JobDTO {
private String jobName;
private String jobGroup;
private String jobClass;
private String triggerName;
private String triggerGroup;
private String cron;
}
创建controller
@RestController
@RequestMapping(value = "/job",produces = "text/plain;charset=utf-8")
public class jobController {
@Autowired
Scheduler scheduler;
/**
* 暂停任务
*
* @param name Trigger 名称
* @param group Trigger 分组
* @return
*/
@RequestMapping("/pause")
public String pause(@RequestParam String name,@RequestParam String group) throws Exception {
TriggerKey triggerKey = TriggerKey.triggerKey(name, group);
if(!scheduler.checkExists(triggerKey)){
return "任务不存在!";
}
Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
if(triggerState.equals(Trigger.TriggerState.NORMAL)){
scheduler.pauseTrigger(triggerKey);
return "任务已暂停!";
}
return "任务不是运行状态!";
}
/**
* 恢复任务
*
* @param name
* @param group
* @return
* @throws Exception
*/
@RequestMapping("/resume")
public String resume(@RequestParam String name,@RequestParam String group) throws Exception {
TriggerKey triggerKey = TriggerKey.triggerKey(name, group);
if(!scheduler.checkExists(triggerKey)){
return "任务不存在!";
}
Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
if(triggerState.equals(Trigger.TriggerState.PAUSED)){
scheduler.resumeTrigger(triggerKey);
return "任务已重启!";
}
return "任务不是暂停状态!";
}
/**
* 创建任务
*
* @param jobDTO
*/
@PostMapping("/create")
public String create(@RequestBody JobDTO jobDTO) throws SchedulerException, ClassNotFoundException {
JobKey jobKey = JobKey.jobKey(jobDTO.getJobName(), jobDTO.getJobGroup());
TriggerKey triggerKey = TriggerKey.triggerKey(jobDTO.getTriggerName(), jobDTO.getTriggerGroup());
if(scheduler.checkExists(jobKey) || scheduler.checkExists(triggerKey)){
return "不能重复创建任务!";
}
Class jobClass = Class.forName(jobDTO.getJobClass());
JobDetailImpl jobDetail = new JobDetailImpl();
jobDetail.setName(jobDTO.getJobName());
jobDetail.setGroup(jobDTO.getJobGroup());
jobDetail.setJobClass(jobClass);
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(jobDTO.getCron())).build();
scheduler.scheduleJob(jobDetail,cronTrigger);
return "任务创建成功!";
}
}