【Quartz】(一)定时框架Quartz的持久化配置

1. 使用背景

当前,越来越多的场景下面需要使用到定时任务,比如报表的生成,每月或者每年进行一次某些数据的统计等等,使用Springboot中的@Scheduled注解固然也可以实现,只不过,如果要改定时的时间,控制定时的开关,都需要直接操作项目的代码,如果是线上环境的,还得需要把项目 停下来,代价太大,那到底有没有一种框架可以实现我们对于定时任务的增删查改,可以修改定时的时间,启动以及暂定定时任务呢,答案是有的,目前定时任务的框架有很多,比如我们今天要说的Quartz,分布式定时任务xxl-job等等。

2.Quartz持久化定时任务的原理

Quartz的依赖

    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz</artifactId>
      <version>2.3.0</version>
    </dependency>

打开Quartz包下的路径org.quartz.impl.jdbcjobstore可以看到有很多的sql
在这里插入图片描述
这些Sql文件是quartz针对不同的数据库设计的,我们使用的是Mysql,所以我们将tables_mysql_innodb.sql在数据库里面跑一下,看下效果
在这里插入图片描述
看到默认帮我们生成了这11张表,通过分析这些表,我们就能大致了解Quartz实现定时任务持久化的原理。

表名含义
qrtz_blob_triggers存储用户自定义的trigger,实现Trigger等接口实现自定义逻辑
qrtz_calendars存储和触发器关联的日期
qrtz_cron_triggers存储cron类型的触发器
qrtz_fired_triggers存储正在执行的任务
qrtz_job_details存储任务的详细信息
qrtz_locks存储执行任务需要获取的锁
qrtz_paused_trigger_grps存储被暂停的触发器组
qrtz_scheduler_state存储集群环境的状态
qrtz_simple_triggers存储简单触发器
qrtz_simprop_triggers存储CalendarIntervalTrigger和DailyTimeIntervalTrigger
qrtz_triggers存储任务执行的时间,下次执行时间,任务执行的状态等信息

其实quartz的执行逻辑很简单,就是从qrtz_triggers获取等待状态将要执行的任务,通过关联查询,查询对应的触发器,以及job_details中任务执行的类进行任务的执行,简单的来说,就是这样的。

3. 持久化配置

我这里以Springboot配置为例,讲解一下如何进行配置。

定义一个Quartz的配置类QuartzConfig

@Configuration
public class QuartzConfig{
	
	@Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource){
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        // 设置数据源,这个数据源是从Bean获取来的,可以在Bean里面配置任意的数据库
        factory.setDataSource(dataSource);
		
        Properties prop = new Properties();
        // 集群环境的唯一ID,名字可以任意设置,instanceId设置成AUTO,系统会帮我们自动生成 
        prop.put("org.quartz.scheduler.instanceName", "MyScheduler");
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        // 配置Quartz运行的线程池配置
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "20");
        prop.put("org.quartz.threadPool.threadPriority", "5");
        // 非常重要!JDBC的增删查改都是依靠JobStore来完成的。
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        // 配置集群环境
        prop.put("org.quartz.jobStore.isClustered", "true");
        prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
        prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
        //若集群环境需要配置此项,可防止重复执行,单机环境不推荐配置此项,会消耗服务器性能
        //prop.put("org.quartz.jobStore.acquireTriggersWithinLock","true");

        
        prop.put("org.quartz.jobStore.misfireThreshold", "12000");
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        factory.setQuartzProperties(prop);

        factory.setSchedulerName("MyScheduler");
        // 延时启动
        factory.setStartupDelay(1);
        factory.setApplicationContextSchedulerContextKey("applicationContextKey");
        // 可选,QuartzScheduler
        // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
        factory.setOverwriteExistingJobs(true);
        // 设置自动启动,默认为true
        factory.setAutoStartup(true);

        return factory;
    }
}

4.持久化一些简单的实现

我们首先定义一个Job,我希望这个Job帮我打印字符串

public class TestTask implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("Hello");
    }
}

然后我们告诉Quartz,申请一个名称为job1,组为group1的JobDetails帮我们记录这个Job

@RestController
@RequestMapping("/job")
public class JobController {
	
	@Autowired
    private Scheduler scheduler;

	@RequestMapping("createJob")
	public String createJob() throws SchedulerException {
		JobDetail jobDetail = JobBuilder.newJob(TestTask.class).withIdentity("job1", "group1").build();
		return "created!";
	}
}

光这样还不行,我们还要申请一个名称为trigger1,组为tgroup1的Cron类型的触发器,告诉Quartz这个任务执行的周期

@RestController
@RequestMapping("/job")
public class JobController {
	
	@Autowired
    private Scheduler scheduler;

	@RequestMapping("createJob")
	public String createJob() throws SchedulerException {
		JobDetail jobDetail = JobBuilder.newJob(TestTask.class).withIdentity("job1", "group1").build();
		CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "tgroup1")
                .withSchedule(CronScheduleBuilder.cronSchedule("* * * * *  ?")).build();
		return "created!";
	}
}

然后通过Quartz的调度器,帮我们执行的任务,通过我们创建的Cron的触发器

@RestController
@RequestMapping("/job")
public class JobController {
	
	@Autowired
    private Scheduler scheduler;

	@RequestMapping("createJob")
	public String createJob() throws SchedulerException {
		JobDetail jobDetail = JobBuilder.newJob(TestTask.class).withIdentity("job1", "group1").build();
		CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "tgroup1")
                .withSchedule(CronScheduleBuilder.cronSchedule("* * * * *  ?")).build();
        scheduler.scheduleJob(jobDetail,trigger);
        scheduler.start();
		return "created!";
	}
}

启动项目
此时观察表,可以发现
qrtz_scheduler_state已经记录了我们的第一台的机器的信息
在这里插入图片描述
此外,qrtz_locks里面也帮我们生成了两把锁,关于这个锁的含义,下一篇博客中,我会和大家讲解一下含义
在这里插入图片描述
其他表里面暂时没有数据了,那么现在我们我们利用我们刚刚写的那个接口帮我们创建一个job试一下

http://ip:端口/job/createJob

在这里插入图片描述
可以看到此时任务一直在重复执行
我们再来看一下表里面发生了哪些数据的改变
首先qrtz_cron_triggers,帮我们新增了一条我们刚刚定义的cron的trigger
在这里插入图片描述
qrtz_job_details,也帮我们新建了一条任务的详情,记录了任务执行类的全限定名
在这里插入图片描述
qrtz_fired_triggers记录了此时此刻,我们正在执行的定时任务
在这里插入图片描述
qrtz_triggers记录了我们已有任务的一些信息,包括上一次执行的时间,下一次执行的时间,以及任务此时的状态等
在这里插入图片描述
看到这里,相信大家应该觉得Quartz不过如此,非常的简单,但是如果仅仅是这样使用也会产生某些问题,后面的博客中,我将详细介绍一下,集群环境下Quartz重复执行产生的原因,以及如何去解决重复执行的问题,包括我们如果在一个Springboot项目中如何优雅的使用Quartz,我们可以在页面中,增删查改定制化我们的任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JeffHan^_^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值