书接上文,介绍下Quartz Java体系结构

news2024/9/20 9:36:56

体系结构总结

image.png

JobDetail

我们创建一个实现 Job 接口的类,使用 JobBuilder 包装成 JobDetail,它可以携带 KV 的数据。

Trigger
定义任务的触发规律,Trigger,使用 TriggerBuilder 来构建。
JobDetail 跟 Trigger 是 1:N 的关系。
思考:为什么要解耦?
Trigger 接口在 Quartz 有 4 个继承的子接口:
子接口描述特点
SimpleTrigger简单触发器固定时刻或时间间隔,毫秒
CalendarIntervalTrigger基于日历的触发器比简单触发器更多时间单位,支持非固定时间的触发,例如一年可能 365/366,一个月可能 28/29/30/31
DailyTimeIntervalTrigger基于日期的触发器每天的某个时间段
CronTrigger基于 Cron 表达式的触发器

MutableTrigger 和 CoreTrigger 最终也是用到以上四个类的实现类

image.png

SimpleTrigger
SimpleTrigger 可以定义固定时刻或者固定时间间隔的调度规则(精确到毫秒)。
例如:每天 9 点钟运行;每隔 30 分钟运行一次。
CalendarIntervalTrigger
CalendarIntervalTrigger 可以定义更多时间单位的调度需求,精确到秒。
好处是不需要去计算时间间隔,比如 1 个小时等于多少毫秒。
例如每年、每个月、每周、每天、每小时、每分钟、每秒。
每年的月数和每个月的天数不是固定的,这种情况也适用。
DailyTimeIntervalTrigger
每天的某个时间段内,以一定的时间间隔执行任务。
例如:每天早上 9 点到晚上 9 点,每隔半个小时执行一次,并且只在周一到周六执行。
CronTrigger

CronTirgger 可以定义基于 Cron 表达式的调度规则,是最常用的触发器类型。

Cron 表达式

位置时间域特殊值
10-59, - * /
2分钟0-59, - * /
3小时0-23, - * /
4日期1-31, - * ? / L W C
5月份1-12, - * /
6星期1-7, - * ? / L W C
7年份(可选)1-31, - * /

星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,在分钟字段时,表示“每分钟”;

问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;

减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从 10 到 12 点,即 10,11,12;

逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;

斜杠(/):x/y 表达一个等步长序列,x 为起始值,y 为增量步长值。如在分钟字段中使用 0/15,则表示为 0,15,30 和 45 秒,而 5/15 在分钟字段中表示 5,20,35,50,你也可以使用*/y,它等同于 0/y;

L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L 在日期字段中,表示 这个月份的最后一天,如一月的 31 号,非闰年二月的 28 号;如果 L 用在星期中,则表示星期六,等同于 7。但是,如果 L 出现在星期字段里,而且在前面有一个数值 X,则表示“这个月的最后 X 天”,例如,6L 表示该月的最后星期五;

W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如 15W 表示离该月 15号最近的工作日,如果该月 15 号是星期六,则匹配 14 号星期五;如果 15 日是星期日,则匹配 16 号星期一;如果 15号是星期二,那结果就是 15 号星期二。但必须注意关联的匹配日期不能够跨月,如你指定 1W,如果 1 号是星期六,结果匹配的是 3 号星期一,而非上个月最后的那天。W 字符串只能指定单一日期,而不能指定日期范围;

LW 组合:在日期字段可以组合使用 LW,它的意思是当月的最后一个工作日;

井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如 6#3 表示当月的第三个星期五(6 表示星期五,#3 表示当前的第三个),而 4#5 表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;

C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如 5C 在日期字段中就相当于日历 5 日以后的第一天。1C 在星期字段中相当于星期日后的第一天。

Cron 表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。

上面我们定义的都是在什么时间执行,但是我们有一些在什么时间不执行的需求,比如:理财周末和法定假日购买不计息;证券公司周末和法定假日休市。

是不是要把日期写在数据库中,然后读取基于当前时间判断呢?

基于Calendar的排除规则

如果要在触发器的基础上,排除一些时间区间不执行任务,就要用到Quartz的Calendar类(注意不是JDK的Calendar)。可以按年、月、周、日、特定日期、Cron表达式排除。

image.png

调用Trigger的modifiedByCalendar()添加到触发器中,并且调用调度器的addCalendar()方法注册排除规则。

Calendar名称用法
BaseCalendar为高级的 Calendar 实现了基本的功能,实现了 org.quartz.Calendar 接口
AnnualCalendar排除年中一天或多天
CronCalendar日历的这种实现排除了由给定的CronExpression表达的时间集合。 例如,您可以使用此日历使用表达式“* * 0-7,18-23?* *”每天排除所有营业时间(上午8点至下午5点)。 如果CronTrigger具有给定的cron表达式并且与具有相同表达式的CronCalendar相关联,则日历将排除触发器包含的所有时间,并且它们将彼此抵消。
DailyCalendar您可以使用此日历来排除营业时间(上午8点 - 5点)每天。 每个DailyCalendar仅允许指定单个时间范围,并且该时间范围可能不会跨越每日边界(即,您不能指定从上午8点至凌晨5点的时间范围)。 如果属性invertTimeRange为false(默认),则时间范围定义触发器不允许触发的时间范围。 如果invertTimeRange为true,则时间范围被反转 - 也就是排除在定义的时间范围之外的所有时间。
HolidayCalendar特别的用于从 Trigger 中排除节假日
MonthlyCalendar排除月份中的指定数天,例如,可用于排除每月的最后一天
WeeklyCalendar排除星期中的任意周几,例如,可用于排除周末,默认周六和周日
Scheduler

调度器,是Quartz的指挥官,由StdSchedulerFactory产生。它是单例的。

并且是Quartz中最重要的API,默认是实现类是StdScheduler,里面包含了一个QuartzScheduler。QuartzScheduler里面又包含了一个QuartzSchedulerThread。

image.png

Scheduler中的方法主要分为三大类:

1)操作调度器本身,例如调度器的启动start()、调度器的关闭shutdown()。

2)操作Trigger,例如pauseTriggers()、resumeTrigger()。

3)操作Job,例如scheduleJob()、unscheduleJob()、rescheduleJob()

这些方法非常重要,可以实现任务的动态调度。

Scheduler中的方法主要分为三大类:

1)操作调度器本身,例如调度器的启动start()、调度器的关闭shutdown()。

2)操作Trigger,例如pauseTriggers()、resumeTrigger()。

3)操作Job,例如scheduleJob()、unscheduleJob()、rescheduleJob()

这些方法非常重要,可以实现任务的动态调度。

Listener

我们有这么一种需求,在每个任务运行结束之后发送通知给运维管理员。那是不是要在每个任务的最后添加一行代码呢?这种方式对原来的代码造成了入侵,不利于维护。如果代码不是写在任务代码的最后一行,怎么知道任务执行完了呢?或者说,怎么监测到任务的生命周期呢?

观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖它的对象都会得到通知并自动更新。

Quartz中提供了三种Listener,监听Scheduler的,监听Trigger的,监听Job的。

只需要创建类实现相应的接口,并在Scheduler上注册Listener,便可实现对核心对象的监听。

standalone工程:com.listener

MyJobListenerTest

MySchedulerListenerTest

MyTriggerListenerTest

JobListener

四个方法:

方法作用或****执行实际
getName()返回JobListener 的名称
jobToBeExecuted()Scheduler 在 JobDetail 将要被执行时调用这个方法
jobExecutionVetoed()Scheduler 在 JobDetail 即将被执行,但又被 TriggerListener 否决了时调用这个方法
jobWasExecuted()Scheduler 在 JobDetail 被执行之后调用这个方法

工具类:ListenerManager,用于添加、获取、移除监听器

工具类:Matcher,主要是基于groupName和keyName进行匹配。

image.png

TriggerListener
方法作用或执行实际
getName()返回监听器的名称
triggerFired()Trigger 被触发,Job 上的 execute() 方法将要被执行时,Scheduler 就调用这个方法
vetoJobExecution()在 Trigger 触发后,Job 将要被执行时由 Scheduler 调用这个方法。TriggerListener 给了一个选择去否决 Job 的执行。假如这个方法返回 true,这个 Job 将不会为此次 Trigger 触发而得到执行
triggerMisfired()Trigger 错过触发时调用
triggerComplete()Trigger 被触发并且完成了 Job 的执行时,Scheduler 调用这个方法
SchedulerListener

方法比较多,省略。

JobStore

问题:最多可以运行多少个任务(磁盘、内存、线程数)

Jobstore用来存储任务和触发器相关的信息,例如所有任务的名称、数量、状态等等。Quartz中有两种存储任务的方式,一种在在内存,一种是在数据库。

RAMJobStore

Quartz默认的JobStore是RAMJobstore,也就是把任务和触发器信息运行的信息存储在内存中,用到了HashMap、TreeSet、HashSet等等数据结构。

如果程序崩溃或重启,所有存储在内存中的数据都会丢失。所以我们需要把这些数据持久化到磁盘。

JDBCJobStore

JDBCJobStore可以通过JDBC接口,将任务运行数据保存在数据库中。

image.png

JDBC的实现方式有两种,JobStoreSupport类的两个子类:

JobStoreTX:在独立的程序中使用,自己管理事务,不参与外部事务。

JobStoreCMT:(Container Managed Transactions (CMT),如果需要容器管理事务时,使用它。

使用JDBCJobSotre时,需要配置数据库信息:

org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
\# 使用quartz.properties,不使用默认配置
org.quartz.jobStore.useProperties:true
\#数据库中quartz表的表名前缀
org.quartz.jobStore.tablePrefix:QRTZ_
org.quartz.jobStore.dataSource:myDS
\#配置数据源
org.quartz.dataSource.myDS.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL:jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf8
org.quartz.dataSource.myDS.user:root
org.quartz.dataSource.myDS.password:123456
org.quartz.dataSource.myDS.validationQuery=select 0 from dual

问题来了?需要建什么表?表里面有什么字段?字段类型和长度是什么?

在官网的Downloads链接中,提供了11张表的建表语句:

quartz-2.2.3-distribution\quartz-2.2.3\docs\dbTables

2.3的版本在这个路径下:src\org\quartz\impl\jdbcjobstore

表名与作用:

表名作用
QRTZ_BLOB_TRIGGERSTrigger作为Blob类型存储
QRTZ_CALENDARS存储Quartz的Calendar信息
QRTZ_CRON_TRIGGERS存储CronTrigger,包括Cron表达式和时区信息
QRTZ_FIRED_TRIGGERS存储与已触发的Trigger相关的状态信息,以及相关Job的执行信息
QRTZ_JOB_DETAILS存储每一个已配置的Job的详细信息
QRTZ_LOCKS存储程序的悲观锁的信息
QRTZ_PAUSED_TRIGGER_GRPS存储已暂停的Trigger组的信息
QRTZ_SCHEDULER_STATE存储少量的有关Scheduler的状态信息,和别的Scheduler实例
QRTZ_SIMPLE_TRIGGERS存储SimpleTrigger的信息,包括重复次数、间隔、以及已触的次数
QRTZ_SIMPROP_TRIGGERS存储CalendarIntervalTrigger和DailyTimeIntervalTrigger两种类型的触发器
QRTZ_TRIGGERS存储已配置的Trigger的信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2129899.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智慧物流系统小程序的设计

管理员账户功能包括:系统首页,个人中心,车辆管理,商品管理,物流信息管理,论坛管理,公告信息管理 微信端账号功能包括:系统首页,商品,论坛,我的 …

磁盘无法访问:深度解析与高效数据恢复策略

在数字化时代,磁盘作为数据存储的核心载体,其稳定性和可访问性直接关系到用户数据的安全与完整性。然而,当遇到“磁盘无法访问”的突发状况时,用户往往会陷入焦虑与无助之中。本文将深入探讨磁盘无法访问的原因,并详细…

最全面IO流介绍

1.字符集介绍 标准ASCII字符集:使用1个字节存储一个字符,首尾是0,总可以表示128个字符。是美国信息交换标准代码,包含英文、符号等等。 GBK汉字编码字符集,包含2万多个汉字等字符,GBK中一个中文字符编码成…

(Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)

接着上一篇文章来搞,批量查询点赞状态。这个接口提供给其他的微服务调用所以这里会用到FeignClient 直接上接口 1. 接口信息 这里是查询多个业务的点赞状态,因此请求参数自然是业务id的集合。由于是查询当前用戶的点赞状态,因此无需传递用戶信息。当前用户指的是登录用户 …

小琳AI课堂:LLaMA 3.1 开源大模型的全新里程碑

引言 大家好,这里是小琳AI课堂!今天我们要聊的是Meta最新发布的开源大模型LLaMA 3.1。这个版本在AI界掀起了不小的波澜,不仅在参数规模上有显著提升,还在多项性能上实现了突破。让我们一起来看看LLaMA 3.1带来的新变化和意义吧&a…

UEFI基础概念2——Handle和Protocol

一、Handle Handle Database是由Handle和Protocol组成。Handle(句柄)是一个或多个协议的集合,Protocol(协议)是由GUID命名的数据结构。 在平台初始化过程中,系统固件、UEFI兼容驱动程序和UEFI应用程序创建…

香港科技大学工学2025/2026年度硕士研究生(MSc)项目招生宣讲会

🔔香港科技大学工学院2025/2026年度硕士研究生(MSc)项目招生宣讲会 🕙时间:2024年9月25日(星期三)19:00 🏠地点:华南理工大学五山校区33号楼403室 🎆2024Ti…

重修设计模式-结构型-桥接模式

重修设计模式-结构型-桥接模式 将抽象和实现解耦,让它们可以独立变化 桥接模式(Bridge Pattern)的主要目的是解耦,创建一个抽象层来隔离接口与实现,让它们之间的依赖关系可以通过对象的组合来替代继承,从而…

运维学习————运维日志分析系统es——Elasticsearch

目录 一、简介 1、全文检索 1、数据分类 2、非结构化数据搜索方法 2、什么是 Elasticsearch 3、用途 4、基本概念 二、下载配置,搭建集群 集群规划 解压 配置环境变量 es配置 由于es启动不能直接使用root用户 启动 三、常用集群命令 一、简介 …

可提示 3D 分割研究里程碑!SAM2Point:SAM2加持泛化任意3D场景、任意提示!

郑重声明:本解读已获得论文作者的原创解读授权 文章链接:https://arxiv.org/pdf/2408.16768 在线demo: https://huggingface.co/spaces/ZiyuG/SAM2Point code链接:https://github.com/ZiyuGuo99/SAM2Point 亮点直击 无投影 3D 分割&#xff1…

DAY14信息打点-JS 架构框架识别泄漏提取API 接口枚举FUZZ 爬虫插件项目

本课意义: 1.如何从表现中的JS提取价值信息 2.如何从地址中FUZZ提取未知的JS文件 3.如何从JS开放框架WebPack进行测试 一、JS 前端架构-识别&分析 在JS中寻找更多的URL地址,在JS代码逻辑(加密算法、APIKey配置、验证逻辑)中进…

C语言——静态链表和动态链表

一、静态链表 创建链表中的一个节点,用struct来创建,其中包含两个部分:数据域和指针域。 创建三个结点,node1、node2、node3,分别赋值,初始化为NULL。接着就是将这些节点进行连接,组成链表。连…

干货分享|分享一款完全免费的PDF工具箱 PDF24

PDF 24介绍: PDF 24是一个全面的PDF工具包,旨在简化和优化PDF文件的处理和管理。它提供了广泛的功能和工具,包括创建、编辑、转换、合并、拆分、加密、解密、压缩、优化、签名、水印等。 主要特点:完全免费,轻量便捷,…

现货黄金避险属性是怎么来的?

众所周知,黄金是一种可以规避风险的品种,当地缘局势冲突、经济下行,甚至是出现军事摩擦等等情况,黄金都会受到避险情绪支撑而上涨。典型的例子,我们可以看俄乌冲突,还有中东的地缘局势等,这些事…

TS 常用类型

我们经常说TypeScript是JavaScript的一个超级 TypeScript 常用类型 TypeScript 是 JS 的超集,TS 提供了 JS 的所有功能,并且额外的增加了:类型系统 所有的 JS 代码都是 TS 代码 JS 有类型(比如,number/string 等&…

OPENAIGC开发者大赛企业组AI黑马奖 | 包你面-AI面试助手

在第二届拯救者杯OPENAIGC开发者大赛中,涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到,我们特意开设了优秀作品报道专栏,旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者,希望能带给您…

Linux基础知识之用户和组

Linux基础知识之用户和组 01 1.什么是用户 用户账户为不同人员和运行的程序之间提供安全边界 用户使用 username 进行标识,操作系统为其分配唯一标识号(UID),用户可能含有密码 系统中的每个程序都以一个特定用户运行&#xff…

上/下三角矩阵的压缩存储

1.给自己出题:自己动手创造,画一个5行5列的下三角矩阵 2.画图:按“行优先”压缩存储上述矩阵,画出一维数组的样子 3.简答:写出元素 i,j 与 数组下标之间的对应关系 4.画图:按“列优先”压缩存储上述矩阵…

【卷起来】VUE3.0教程-06-组件详解

各位看官,点波关注和赞吧 组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构: 这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的…

ssrf漏洞利用+CTF实例

引发ssrf漏洞的几个函数 file_get_contents() 把整个文件读入一个字符串中,获取本地或者远程文件内容fsockopen() 获得套接字信息curl_exec() 执行一个curl会话,由curl_init()初始化一个新的会话,返回一个curl句柄fopen() 打开文件或者URLre…