Quartz是一个任务调度框架,最近在项目中有用到,所以做个记录总结。

一、主要元素
  • Scheduler:调度器,控制任务的调度,将JobDetail和Trigger注册到Scheduler加以控制。
  • Job:任务,是一个接口且只有一个方法void execute(JobExecutionContext context),实现该接口定义任务的执行逻辑。
  • JobDetail:Job实例,一个Job可以创建多个Job实例,每一个实例有自己的属性。
  • Trigger:触发器,定义触发规则。
二、简单使用

  我是在Spring Boot项目中使用的,这个Demo也是基于Spring Boot,实际上还可以更简洁。Quartz版本为2.3.0。

  1. 增加pom依赖

    <dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
    </dependency>
  2. 编写配置文件
    # quartz.properties
    org.quartz.scheduler.instanceName=TaskScheduler
    org.quartz.threadPool.threadCount=5
    org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX # 数据保存到数据库,使用JobStoreTX作为JobStore来管理事务
    org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 数据库代理
    org.quartz.jobStore.tablePrefix=QRTZ_ # 表前缀,默认为QRTZ_。主要可用于多个服务数据存储到同一数据库,可以创建多组不同的表供不同服务使用。
    org.quartz.jobStore.dataSource=quartzDataSource # 数据源,在下面定义数据源信息的时候需要用到 org.quartz.dataSource.quartzDataSource.driver=com.mysql.jdbc.Driver
    org.quartz.dataSource.quartzDataSource.URL=jdbc:mysql://127.0.0.1:3306/test?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useSSL=false
    org.quartz.dataSource.quartzDataSource.user=root
    org.quartz.dataSource.quartzDataSource.password=123456
    org.quartz.dataSource.quartzDataSource.maxConnections=5
    org.quartz.dataSource.quartzDataSource.validationQuery=select 1
  3. 定义配置类
    @Configuration
    public class QuartzConfig { @Autowired
    private SpringJobFactory springJobFactory;
    // 配置文件,在application.yml文件中配置
    @Value("${quartz.config}")
    private String quartzConfig; @Bean(name = "schedulerFactory")
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
    SchedulerFactoryBean factory = new SchedulerFactoryBean();
    factory.setAutoStartup(true);
    // 延时5秒启动
    factory.setStartupDelay(5);
         // 设置配置信息
    factory.setQuartzProperties(quartzProperties());
    factory.setJobFactory(springJobFactory);
    return factory;
    } @Bean
    public Properties quartzProperties() throws IOException {
    PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
    propertiesFactoryBean.setLocation(new ClassPathResource(quartzConfig));
    propertiesFactoryBean.afterPropertiesSet();
    return propertiesFactoryBean.getObject();
    } /**
    * Quartz初始化监听器
    *
    * @return
    */
    @Bean
    public QuartzInitializerListener executorListener() {
    return new QuartzInitializerListener();
    } /**
    * 将Scheduler添加到Spring管理
    *
    * @return
    * @throws IOException
    */
    @Bean(name = "scheduler")
    public Scheduler scheduler() throws IOException {
    return schedulerFactoryBean().getScheduler();
    } }
  4. 数据库增加对应表,可以到Quartz发行版的“docs / dbTables”目录中找到表创建SQL脚本。
  5. 定义Job
    public class TestJob implements Job, Serializable {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestJob.class);
    private static final long serialVersionUID = 1L; @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
    LOGGER.info("-------------- 执行Quartz测试任务 --------------");
    // Do something ...
    }
    }
  6. 创建JobDetail和Trigger并加入调度器(这部分建议写成接口)
    Class cls = Class.forName("com.xiaoliu.job.TestJob");
    cls.newInstance();
    // 创建JobDetail
    JobDetail job = JobBuilder.newJob(cls).withIdentity("test1",
    "test")
    .withDescription("测试任务1").build();
    // 创建触发器
    CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ? ")
    // 立即触发一次,然后按照正常的规则执行下一个周期的任务。
    .withMisfireHandlingInstructionFireAndProceed();
    Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger" + "test1", "test").startNow().withSchedule(cronScheduleBuilder).build();
    // 注册到scheduler
    scheduler.scheduleJob(job, trigger);

  最后运行项目,查看效果。

三、关于Trigger

  使用Quartz的过程需要了解清楚Trigger,它关系到任务触发规则的定义,以及触发过程可能遇到的问题处理。在这里只涉及最常用的两种Trigger:SimpleTrigger和CronTrigger。

  先看一下TriggerBuilder这个构造类,里面有各种Trigger的一些公共属性,主要列举几个说明:

  • jobKey:trigger触发时被执行的job的key。
  • startTime:trigger生效的时间点。
  • endTime:trigger失效的时间点。trigger只在startTime和endTime之间才会被触发。
  • priority:优先级,默认为5,priority的值可以是任意整数。假设同时执行的trigger有很多,但是Quartz线程池的工作线程很少(没有足够的资源同时触发这些trigger),这个时候会按照优先级高的先触发。
  • misfire Instructions:错过触发策略,trigger定义了一个触发阀值,在阀值时间范围内会重新触发,超过阀值范围则认为是misfire。因为某种原因,trigger在应该触发的时候未触发且错过了触发的时机,就需要一定策略来处理misfire,不同的trigger有不同的策略集。所有trigger的默认触发策略都是MISFIRE_INSTRUCTION_SMART_POLICY,值为0。

  ① SimpleTrigger

  可以满足的调度需求:在具体的时间点执行一次,或者在具体的时间点执行,并且以指定的间隔重复执行若干次。简单的调度需求可以使用SimpleTrigger。

  SimpleTrigger的主要属性有:

    • repeatCount:重复间隔
    • repeatInterval:重复次数

例:

SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10) // 10s执行一次
.repeatForever() // 次数不限
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMinutes(1) // 1分钟执行一次
.withRepeatCount(10) // 次数为10次

  

Misfire策略

  SimpleTrigger的misfire策略有以下几种:

MISFIRE_INSTRUCTION_SMART_POLICY:0,默认策略。会根据实例的配置及状态,在所有MISFIRE策略中动态选择一种Misfire策略。
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY:-1,忽略所有的超时状态,按照触发器的策略执行。
MISFIRE_INSTRUCTION_FIRE_NOW:1,立即执行
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT:2,立即执行,并重复到指定的次数。
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT:3,立即执行,且超时期内错过的执行机会作废。
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT:4,以现在为基准,以repeatInterval为周期,延时到下一个激活点执行。
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT:5,在下一个激活点执行,并重复到指定的次数。

Misfire策略设置方式如下:

SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever()
.withMisfireHandlingInstructionFireNow()

  ② CronTrigger

CronTrigger能支持更复杂的调度需求,通常比SimpleTrigger更有用,CronTrigger的属性只有Cron Expressions,但Cron表达式的功能非常强大。Cron Expressions是由七个子表达式组成的字符串,用于描述日程表的各个细节。这些子表达式用空格分隔,分别是秒/分/时/日/月/周/年,年不是必须的。

表达式 是否必须 允许值 允许的特殊字符
0-59 , - * /
0-59 , - * /
0-23 , - * /
1-31 , - * ? / L W C
1-12 或 JAN-DEC , - * /
1-7 或 SUN-SAT , - * ? / L C #
空 或 1970-2099 , - * /

比如上面例子中的表达式:0/10 * * * * ? ,表示每10s执行一次。cron的具体规则网上很多,不是本文的重点。

Misfire策略

  CronTrigger的misfire策略有以下几种:

MISFIRE_INSTRUCTION_SMART_POLICY:0,默认策略。在CronTrigger中解释为MISFIRE_INSTRUCTION_FIRE_ONCE_NOW。
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY:-1,忽略所有的超时状态,按照触发器的策略执行。
MISFIRE_INSTRUCTION_DO_NOTHING:2,什么都不做,然后就按照正常的计划执行。
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:1,立即触发一次,触发后恢复正常的频率。

Misfire策略设置方式如下:

CronScheduleBuilder.cronSchedule("0/10 * * * * ? ")
.withMisfireHandlingInstructionFireAndProceed();
四、问题记录

  在项目中使用时遇到了一个问题,也是我后面进一步了解Quartz的原因,在此记录。

  Job本地运行正常,但部署到Linux服务器后,观察业务发现Job未执行,也找不到相关错误日志。

  最开始的思考问题的原因可能是:Linux和Windows系统或其他环境问题,调度策略或者其他配置问题。通过接口手动触发Job是正常的,修改触发策略等等配置后依然无法解决。第二天发现测试系统部分任务成功执行了,查看日志发现有ClassNotFoundException,原因是执行了一个不属于当前服务的Job。问题好像有点苗头了,查看qrtz_triggers表的记录,失败Job对应的trigger记录trigger_state的值为ERROR,那么原因找到了:多个服务中的Quartz使用同一组表,维护同一组数据。当服务触发了一个不属于本服务的Job后(ClassNotFoundException,业务处理失败),会修改触发记录,其他服务就不会重复触发该任务,也不会产生错误日志。

trigger_state的值有:

  • WAITING:等待
  • PAUSED:暂停
  • ACQUIRED:正常执行
  • BLOCKED:阻塞
  • ERROR:错误

解决方案:

  1. 任务调度整合在一个服务里,其他服务开放业务处理接口。(服务有多节点时不可行)
  2. 每个服务创建一组数据库表,通过在配置文件配置org.quartz.jobStore.tablePrefix属性指定到对应的表。

Quartz使用记录总结的更多相关文章

  1. Quartz 使用记录

    Quartz 使用记录 官网 https://www.quartz-scheduler.org/ 参考文档 Quartz 2.3.0 什么是 Quartz? 官方描述: Quartz is a ric ...

  2. Quartz 学习记录1

    原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...

  3. quartz 实例记录

    之前介绍过了quartz的一些相关理论知识,感觉上挺简单的,实际动手操作也确实如此,去quartz的官网上把Jar包下载下来以后,会看到它的目录里有例子程序,看完第一个例子觉得还可以,但是看后面两个例 ...

  4. quartz相关记录

    1.http://www.quartz-scheduler.org/api/2.3.1-SNAPSHOT/ api地址 2.https://www.jianshu.com/p/3c3e166a7da1 ...

  5. quartz问题记录-missed their scheduled fire-time

    这里有3个原因:1.所有的woker thread(工作线程; 辅助线程)都在运行其他的job2.scheduler(调度器)down了(关于这个down.我不太明确是shutdown了..还是挂掉了 ...

  6. Quartz学习记录

    参考资料: 官方网站 Quartz使用总结

  7. Quartz任务调度 服务日志+log4net打印日志+制作windows服务

    引言 现在许多的项目都需要定时的服务进行支撑,而我们经常用到的定时服务就是Quartz任务调度了.不过我们在使用定时Job进行获取的时候,有时候我们就需要记录一下自定义的日志,甚至我们还会对执行定时J ...

  8. Quartz SpringBoot 简单整合一下

    一次简单的代码整合记录. 数据库准备 如果是MySQL可能出现一些小问题.比如联合主键长度超限制,已经记录解决办法了. CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NA ...

  9. Quartz.net使用记录

    1.引入dll文件: nuget控制台:安装quartz:Install-Package Quartz 安装log4net:Install-Package log4net,这里使用log4net记录一 ...

随机推荐

  1. (PMP)第1章-----引论

    1.指南概述和目的: 2.基本要素: 项目:创造独特的产品,服务或成果而进行的临时性工作.特点:1.临时性   临时性并不一定意味着项目时间短.2.独特性   3.渐进明细性 项目管理:就是将知识,技 ...

  2. Fiddler-设置取消自动更新

    fiddler 启动时老弹出要更新,但不想更新,可以这样设置 Tools-Optons->General 把第一个√去掉

  3. java集成memcached、redis防止缓存穿透

    下载相关jar,安装Memcached,安装教程:http://www.runoob.com/memcached/memcached-install.html spring配置memcached &l ...

  4. windows putty xming virt-manager

    记一次windows环境使用linux下使用virt-manager软件的问题 环境:windows server 2008.ubuntu-server 软件:putty.virt-manager.x ...

  5. BZOJ4720-换教室

    题目很长,是一道概率dp题,一般需要逆推,但这题结局不确定所以要顺推. 用f[i][j][k],i表示第i段时间,j表示用了j次申请,k就表示这轮是否用申请. 那么要求min(f[n][0~m][0] ...

  6. JS 控制输入框输入表情emoji 显示在页面上

    问题描述: 最近做一个评论回复的功能遇到了用户输入框输入表情,存入数据库的时候转变成了问号??? 起初为了避免这个问题,做了一个过滤表情的控制 var inputText = $('#pinglun' ...

  7. c++中的const与指针

    const修饰符 使用const修饰变量时,该变量的值不可修改,因此需要初始化. 例如 const int s = 0: 此时s为值不可变的变量. 那么基于此,当const修饰指针时的情况有三种: ( ...

  8. CTR常见规则摘录

    1.给用户推荐热门的10个商品  . 2.对数据进行预处理,删除未购买过品牌的用户记录(不删除最近一周才出现的新用户),删除未被购买过的品牌记录,删除疯狂点击但是从不购买的刷钻用户记录等,利用一些简单 ...

  9. 知乎技术分享:从单机到2000万QPS并发的Redis高性能缓存实践之路

    本文来自知乎官方技术团队的“知乎技术专栏”,感谢原作者陈鹏的无私分享. 1.引言 知乎存储平台团队基于开源Redis 组件打造的知乎 Redis 平台,经过不断的研发迭代,目前已经形成了一整套完整自动 ...

  10. 吴恩达机器学习笔记21-正则化线性回归(Regularized Linear Regression)

    对于线性回归的求解,我们之前推导了两种学习算法:一种基于梯度下降,一种基于正规方程. 正则化线性回归的代价函数为: 如果我们要使用梯度下降法令这个代价函数最小化,因为我们未对theta0进行正则化,所 ...