定时任务JOB框架之Quartz (一) Quartz入门与快速Demo搭建

一、为什么要使用Quartz

比如在电商系统中:买家下单时系统生成订单,用户有30分钟左右的时间去支付订单,如果30分钟后还没有支付,那么订单就不见了。

实现方式:系统中有个定时任务,每隔10秒钟(当然也可以设置为30秒、1分钟等)去检测大于等于30分钟之前创建的还未支付的订单,检查到该订单后就将订单删除或者将订单的状态改为失效,使用户无法看到该订单。

像这样的定时任务使用场景还有很多,比如:每天的固定时间点自动生成报表、每个月份的月底自动生成报表;卖家订单发货成功后,买家7天还没有点击确认收货,系统自动确认收货等等。

Quartz是一个完全由Java编写的开源任务调度框架,可以方便灵活的自动去执行我们编写的任务代码,实现指定的业务逻辑。Quartz使用起来非常简单,功能却非常强大,支持任务和触发器的多对多的关系,还能把多个任务与不同的触发器关联,触发器支持非常灵活的时间设置,如:在一天中的某个时间执行,一周的某几天执行,在每月的某一天执行,重复执行直到一个特定的时间/日期等等。Quartz还支持集群、自动故障转移,多机部署服务时,单机故障不会影响到系统的可服务性。

二、创建Spring Boot工程

三、创建HelloJob类

package com.ljhua.quartz1;

import org.springframework.stereotype.Service;
import java.util.Date;

@Service
public class HelloJob {
    private int count = 0;
    public void handle() {
        int curJob = count++;
        System.out.println("处理逻辑-" + Thread.currentThread().getId() + "-start:" + new Date() + " ### " + curJob);
        try {
            Thread.sleep(30 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("处理逻辑-" + Thread.currentThread().getId() + "---end:" + new Date() + " ### " + curJob);
    }
}

四、创建HelloConfiguration类

package com.ljhua.quartz1;

import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

@Configuration
public class HelloConfiguration {
    @Bean(name = "jobDetail")
    public MethodInvokingJobDetailFactoryBean detailFactoryBean(HelloJob helloJob) {
        MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();

        // 是否并发执行
        jobDetail.setConcurrent(false);

        // 设置任务的名字
        jobDetail.setName("helloJob");

        // 设置任务的分组,在多任务的时候使用
        jobDetail.setGroup("JobGroup");

        // 需要执行的对象
        jobDetail.setTargetObject(helloJob);

        /*
         * TODO  非常重要
         * 执行HelloJob类中的handle方法
         */
        jobDetail.setTargetMethod("handle");
        return jobDetail;
    }

    @Bean(name = "jobTrigger")
    public CronTriggerFactoryBean cronJobTrigger(JobDetail jobDetail) {
        CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();

        tigger.setJobDetail(jobDetail);

        //cron表达式,每10秒执行一次
        tigger.setCronExpression("0/10 * * * * ?");

        tigger.setName("jobTrigger");
        return tigger;
    }

    @Bean(name = "scheduler")
    public SchedulerFactoryBean schedulerFactory(Trigger jobTrigger) {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();

        // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job
        factoryBean.setOverwriteExistingJobs(true);

        // 延时启动,应用启动1秒后
        factoryBean.setStartupDelay(1);

        // 注册触发器
        factoryBean.setTriggers(jobTrigger);
        return factoryBean;
    }
}

执行工程,运行结果如下:

可以看到代码中tigger.setCronExpression("0/10 * * * * ?")把调度的时间间隔设置为10秒,但实际上handle()函数上一次执行和下一次执行的时间间隔是0秒。这是因为handle()函数里执行Thread.sleep(30 * 1000),系统睡眠了30秒(假设你的业务逻辑执行一次需要30秒),导致handle()函数执行一次的耗时(30秒)大于调度间隔(10秒),所以真实的执行结果就成了handle()函数2次执行的间隔为0秒。

jobDetail.setConcurrent(false)的参数false改为true后jobDetail.setConcurrent(true),再来看下程序的运行结果是怎样?

可以看到start ### 0,start ### 1,start ### 2,start ### 3,start ### 4,start ### 5,start ### 6都是间隔10秒,虽然函数执行一次的耗时还是30秒,调度间隔设置还是10秒,但是这次执行结果:handle()函数上一次执行和下一次执行的时间间隔是10秒。区别就在于改了一行代码jobDetail.setConcurrent(true),为什么二次执行的结果不同?因为jobDetail.setConcurrent(true)是同步模式,即无论handle()函数是否处理完业务逻辑,都会按调度时间的固定间隔去调用handle(),而jobDetail.setConcurrent(false)是非同步模式,即上一次调度的handle()函数处理完了业务逻辑才会发起下一次调度。

那么问题来了:有些业务场景对定时任务的执行时间有很强的执行时间要求,同时又不能接受多个线程同时处理同一笔业务可能会出现的故障,怎么做才合理呢?关于这个问题以后再述。

发布了5 篇原创文章 · 获赞 0 · 访问量 159

猜你喜欢

转载自blog.csdn.net/anron/article/details/104436352