Quartz 定时任务

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 "任务创建成功!";
    }
​
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值