109 项目整合 spring-quartz 启动自动执行定时任务

前言

项目中使用了 quartz 来支持定时任务的相关基础支撑, 但是 最近添加了一个 资源消耗比较高的定时任务, 发布到测试环境之后, 发现服务突然 起不起来了[资源比较有限] 

然后 查看了一下日志, 这个定时任务怎么在执行?, 不是 配置的是 凌晨两点么, 然后 仔细一看 几乎配置的 七八个定时任务都跑了一次, 只是 可能其他的任务开销比较小, 平时没有怎么注意 

呵呵 然后 这里就是关注这个问题, 项目启动之后 所有的定时任务 自动跑了一次 

然后 在调试的过程中会发现 qrtz_triggers 会进行 多次的更新, 这些更新或许会是本文的一些核心收货之一 

备注 : 一下测试任务为1小时执行一次, 以下截图中的 fireTime 不为整点的均视为当前时间[多次调试]

 

 

定时任务的 Executor 启动执行

从这里可以看到 trigger 的 previousFireTime 是项目启动的时间, nextFireTime 是基于 previousFireTime 以及 cron 计算的下一次需要执行的时间 

这里的 previousFireTime 指的就是当前这一次定时任务的触发, 那么 这个时间 是怎么来的呢? 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

顺便查询一下 该任务对应的数据库的记录, 是和 业务代码中获取的配置是保持一致的, 那么是哪里的代码 修改的 qrtz_triggers 呢?, 这里或许会找到一些线索 

20201226105545226.png

 

 

更新 qrtz_trigger 的 fire_time 的地方

找了一下 postgres 的日志, 呵呵 发现更新 qrtz_trigger 的 sql 大致如下, 将 nextFireTime 更新为 null, previousFireTime 更新为了 当时时间 

2020-12-21 03:40:36.663 TIME,"postgres","dcams",2724,"172.18.0.1:39966",5fe017db.aa4,7,"UPDATE",2020-12-21 03:34:51 TIME,7/1626,1159546,LOG,00000,"execute <unnamed>: UPDATE QRTZ_TRIGGERS SET JOB_NAME = $1, JOB_GROUP = $2, DESCRIPTION = $3, NEXT_FIRE_TIME = $4, PREV_FIRE_TIME = $5, TRIGGER_STATE = $6, TRIGGER_TYPE = $7, START_TIME = $8, END_TIME = $9, CALENDAR_NAME = $10, MISFIRE_INSTR = $11, PRIORITY = $12 WHERE SCHED_NAME = ''quartzScheduler'' AND TRIGGER_NAME = $13 AND TRIGGER_GROUP = $14","parameters: $1 = ''HOME_OVERVIEW'', $2 = ''DEFAULT'', $3 = NULL, $4 = ''1608522036590'', $5 = ''-1'', $6 = ''WAITING'', $7 = ''CRON'', $8 = ''1600052000000'', $9 = ''0'', $10 = NULL, $11 = ''0'', $12 = ''5'', $13 = ''HOME_OVERVIEW'', $14 = ''DEFAULT''",,,,,,,,"PostgreSQL JDBC Driver"
2020-12-21 03:40:36.667 TIME,"postgres","dcams",2724,"172.18.0.1:39966",5fe017db.aa4,8,"UPDATE",2020-12-21 03:34:51 TIME,7/1626,1159546,LOG,00000,"execute <unnamed>: UPDATE QRTZ_CRON_TRIGGERS SET CRON_EXPRESSION = $1, TIME_ZONE_ID = $2 WHERE SCHED_NAME = ''quartzScheduler'' AND TRIGGER_NAME = $3 AND TRIGGER_GROUP = $4","parameters: $1 = ''0 0 0/1 * * ?'', $2 = ''Asia/Shanghai'', $3 = ''HOME_OVERVIEW'', $4 = ''DEFAULT''",,,,,,,,"PostgreSQL JDBC Driver"

 

根据上面的 update sql, 我们找一下 其在代码中的位置 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

然后定位一下使用的地方 打上断点, 呵呵 果然就来了 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

经过调试发现, trigger 的 nextFireTime 更新为当前时间是在 trig. updateAfterMisfire, 呵呵 这个我们后面再看 

我们先看一些 能够推进事情继续往下走的东西 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

我们来看一下 更新 trigger nextFireTime 更新为当前时间之前的一些情况, 呵呵 这里的 nextFireTime 是 2020.09.14 11:00?, 从时间上来推测 大概可以推测是根据 startTime 计算的下一次触发的时间[我们稍后会有具体的代码的体现]

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

顺便查询一下 该任务对应的数据库的记录, 是和 这里业务代码中获取的配置是保持一致的, 那么是哪里的代码 修改的 qrtz_triggers 呢?, 这里或许会找到一些线索 

下一个坑先挖在这里了[上一次更新 qrtz_trigger 的 fire_time 的地方], 我们接下来先填现在的这个坑 

20201226111621661.png

 

看一下 CronTrigger 对于 misfire 的任务的处理, 有几种策略, 我们这里的情况是 默认的 SMART_POLICY, 这里转换为了 立即触发一次 

1. 忽略 就直接退出了, fireTime 相关不变 

2. doNothing 是啥都不做, 但是根据当前时间重新计算一下 下一次触发的时间 

3. 立即触发一次, 是更新下一次触发时间为当前时间 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

这里可以看到 nextFireTime 更新成了当前时间, 但是在上一个阶段我们看到的结果是 previousFireTime 是当前时间, nextFireTime 是根据当前时间计算的下一个触发时间点, 是怎么回事呢 ?

上面吧 nextFireTime 更新成当前时间, 接着会触发一次当前任务的执行, 下面的地方 会更新 qrtz_trigger, switch 一下, trigger.triggered(cal) 会根据当前这次触发的时间计算下一次的触发时间, 并将 previousFireTime 更新为这一次的触发时间 

下面的 qrtz_trigger 的更新 还是走的是上面的更新的方法, 相同的 sql 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

 

上一次更新 qrtz_trigger 的 fire_time 的地方

假设我项目上一次关闭的时候 qrtz_trigger HOME_OVERVIEW 的 nextFireTime 和 previousFireTime 分别为 2020.12.26 15:00 和 2020.12.26 16:00

那么 qrtz_trigger 是怎么被更新成 nextFireTime 是 2020.09.14 11:00 的呢? spring-quartz 是怎么做到 每一次重启都会重新执行一次 定时任务的呢 ? 

因为 如果我是在 2020.12.26 15:20 重启, 那么按照上面的配置, 我们的这个任务 应该是不属于 misfire 的情况, 应该是不会走 misfire 的处理[上面的 fire_once_now] 

但是这里又会有一个疑问, 当时是在我的立场上面, 既然 qrtz_trigger 在上面的 "更新 qrtz_trigger 的 fire_time 的地方" 之前就被更新了?, 那么我上面的断点为什么第一次进来的时候 nextFireTime 为当前时间呢?, 不应该是为 2020.09.14 11:00 么?

1. 说明还有这个sql还有其他地方使用到[我的立场上面我是看过, 打过断点的, 我是可以否定]

2. 说明还有其他的 sql 会更新 qrtz_trigger, 呵呵 继续 track 一下 sql 

还是在我上面的 "更新 qrtz_trigger 的 fire_time 的地方" 断点上, 然后找一下 postgres 日志, 呵呵 找一下 这部分将 qrtz_trigger 的 nextFireTime 更新成 2020.09.14 11:00 的地方, 呵呵 找到了, 原来是先删除了, 然后 在添加进去的阿 

22020-12-21 05:48:02.294 TIME,"postgres","dcams",2925,"172.18.0.1:40038",5fe036fc.b6d,35,"DELETE",2020-12-21 05:47:40 TIME,7/1668,1159558,LOG,00000,"execute S_23: DELETE FROM QRTZ_TRIGGERS WHERE SCHED_NAME = ''quartzScheduler'' AND TRIGGER_NAME = $1 AND TRIGGER_GROUP = $2","parameters: $1 = ''HOME_OVERVIEW'', $2 = ''DEFAULT''",,,,,,,,"PostgreSQL JDBC Driver"
2020-12-21 05:48:02.325 TIME,"postgres","dcams",2925,"172.18.0.1:40038",5fe036fc.b6d,36,"INSERT",2020-12-21 05:47:40 TIME,7/1668,1159558,LOG,00000,"execute S_25: INSERT INTO QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, JOB_NAME, JOB_GROUP, DESCRIPTION, NEXT_FIRE_TIME, PREV_FIRE_TIME, TRIGGER_STATE, TRIGGER_TYPE, START_TIME, END_TIME, CALENDAR_NAME, MISFIRE_INSTR, JOB_DATA, PRIORITY)  VALUES(''quartzScheduler'', $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)","parameters: $1 = ''HOME_OVERVIEW'', $2 = ''DEFAULT'', $3 = ''HOME_OVERVIEW'', $4 = ''DEFAULT'', $5 = NULL, $6 = ''1600052400000'', $7 = ''-1'', $8 = ''WAITING'', $9 = ''CRON'', $10 = ''1600052000000'', $11 = ''0'', $12 = NULL, $13 = ''0'', $14 = ''\x'', $15 = ''5''",,,,,,,,"PostgreSQL JDBC Driver"

 

然后我们在 INSERT_TRIGGER 使用的地方来上一个断点, 呵呵 看到了 nextFireTime 计算为 2020.09.14 11:00, 并且即将插入到数据库的场景了 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

我们看一下具体的更新 trigger 的 fireTime 的地方, 呵呵 在如下代码 trig.computeFirstFireTime(cal) 

可以看到的是 newTrigger 是给 Scheduler 这边传入的 trigger, oldTrigger 看这里的情况应该是直接从数据库又查询了一次, 得到的 trigger 的状态 

可以看到大致的情况是 项目启动的时候会将各个任务添加到 Scheduler, 然后这里会根据各个任务的 startTime 重新计算 nextFireTime, 然后 替换掉数据库中已有的 qrtz_trigger[启动项目之前已经存在的 qrtz_trigger], 然后后面的 misfire 补偿检测到了 这个任务属于 misfire 的任务, 然后根据策略进行处理, 策略为 SMART_POLICY, 实际的实现中等价于 FIRE_ONCE_NOW, 立即触发了一次 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

computeFirstFireTime 的方式, 呵呵 根据 startTime 进行的计算 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

查看一下当前的 qrtz_trigger 的状态 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

所以, 到这里 你明白了这一系列的 qrtz_trigger 的转换了么? 

 

 

解决问题

更新一下外部传入给 Scheduler. rescheduleJob(TriggerKey triggerKey, Trigger newTrigger) 传入的 newTrigger 的 misfireInstruction, 更新为 doNothing 即可 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

 

完 

 

 

 

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

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

相关文章

python课后习题一

题目&#xff1a; 1. 2. 解题过程&#xff1a; 1. """计算年数和天数""" minute int(input("请输入分钟数&#xff1a;")) hours minute // 60 days hours // 24 years days // 365 last_days days % 365 print(f"{minut…

【IEDM2023】背势垒电荷运动诱导GaN HEMT随时间的非稳态击穿

分享一篇2023年IEDM上GaN HEMT&#xff08;高电子迁移率晶体管&#xff09;的研究论文&#xff0c;标题为“Charge Movement in Back Barrier Induced Time-Dependent On-State Breakdown of GaN HEMT”。论文讨论了在GaN HEMT中&#xff0c;由于背栅&#xff08;Back Barrier&…

BigDecimal类的使用,用于精确计算任意精度的数字

BigDecimal类 BigDecimal 是 Java 中用于精确表示任意精度的十进制数的类。在很多情况下,使用基本数据类型(如 double 或 float)进行浮点数计算可能会导致精度丢失或舍入错误。BigDecimal 提供了一种更精确的解决方案,可以处理需要高精度计算的场景,比如财务应用或科学计算…

力扣---括号生成---回溯---dfs/二进制

暴力--二进制 采用与&#xff1a;力扣---子集---回溯&#xff08;子集型回溯&#xff09;---递归-CSDN博客 中二进制求解一样的思路&#xff0c;即遍历0~-1&#xff08;从二进制去考虑&#xff09;&#xff0c;如果这个数的第 i 位为0&#xff0c;则括号的第 i 位为‘&#xff…

Excel第27享:基于if函数嵌套的多结果唯一性匹配

1、需求描述 如下图所示&#xff0c;现在有E列、F列、G列三列数据&#xff0c;在D列中填充“最终对应编号”&#xff0c;匹配原则是&#xff1a;E列、F列、G列三列数据中&#xff0c;哪个有数据就填充哪个数据&#xff0c;如果都没有&#xff0c;就显示空值即可。 2、解决思路…

redis 常见的异常

目录 一、缓存穿透 1、概念 解决方案 &#xff08;1&#xff09;布隆过滤器 (2)、缓存空对象 二、缓存雪崩 1、概念 解决方案 &#xff08;1&#xff09;redis高可用 &#xff08;2&#xff09;限流降级 &#xff08;3&#xff09;数据预热 一、缓存穿透 1、概念 缓…

【Greenhills】GHS-MULTI IDE-Ubuntu纯命令系统部署license文件

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 记录在Ubuntu纯命令系统中部署license文件的步骤。 2、 问题场景 客户服务器为Linux纯命令行的环境&#xff0c;客户也无其他服务器可以部署&#xff0c;需在纯命令行上尝试安装。 3、软硬件环境 1&#xff09…

Windows系统部署eXtplorer文件管理器结合内网穿透构建私人云存储服务器

文章目录 1. 前言2. eXtplorer网站搭建2.1 eXtplorer下载和安装2.2 eXtplorer网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1. 前言 通过互联网传输文件&#xff0c;是互联网最重要的应用之一&#xff0c;无论是…

8.测试教程-自动化测试selenium-3

文章目录 1.unittest框架解析2.批量执行脚本2.1构建测试套件2.2用例的执行顺序2.3忽略用例执行 3.unittest断言4.HTML报告生成5.异常捕捉与错误截图6.数据驱动 大家好&#xff0c;我是晓星航。今天为大家带来的是 自动化测试selenium第三节 相关的讲解&#xff01;&#x1f600…

ISIS邻居以及路由计算

ISIS报文细节&#xff0c;ISIS报文分为三个部分&#xff1a; 1、通用报文头 Intradomain Routeing Pro Dis&#xff1a;ISIS&#xff1a;域内路由协议鉴别符&#xff1a;0x83代表ISIS协议 Length Indicator&#xff1a;27 头部长度&#xff08;包含了通用头部和专用头部&…

【原创】三十分钟实时数据可视化网站前后端教程 Scrapy + Django + React 保姆级教程向

这个本来是想做视频的&#xff0c;所以是以讲稿的形式写的。最后没做视频&#xff0c;但是觉得这篇文还是值得记录一下。真的要多记录&#xff0c;不然一些不常用的东西即使做过几个月又有点陌生了。 文章目录 爬虫 SCRAPYxpath 后端 DJANGO前端 REACT Hello大家好这里是小鱼&a…

【spring】@Conditional注解学习

Conditional介绍 Conditional注解用于按照设定的条件进行判断&#xff0c;从而决定是否将某个bean注册到Spring容器中。 Conditional注解是在Spring 4.0版本中引入的&#xff0c;它提供了一种更加灵活的方式来控制bean的创建和注册。在此之前&#xff0c;开发者通常使用Profi…

Automated and Cooperative Vehicle Merging at Highway On-Ramps

论文解读——Automated and Cooperative Vehicle Merging at Highway On-Ramps 参考资料来源&#xff1a;《Automated and Cooperative Vehicle Merging at Highway On-Ramps》一文 1.问题描述 该文章想解决如FIG.1中的匝道合流问题&#xff0c;传统的匝道合理需要等待main r…

vue@2.7.16 使用less、less-loader

遇到问题&#xff0c;npm install less-loader7.3.0 --save安装好less-loader后&#xff0c;执行npm run serve 项目运行不起来&#xff0c;排查后发现在安装less-loader后就提示需要安装less&#xff0c;正确的安装应如下&#xff1a; npm install less less-loader7.3.0 --sa…

物联网应用技术中的stm32该怎么学,该从哪入手?

物联网应用技术中的stm32该怎么学&#xff0c;该从哪入手&#xff1f; STM32是只物联网中的一部分&#xff0c;单纯的学个STM32是没法满足物联网开发需求的&#xff0c;实际产品开发过程中会考虑成本等多种因素选择合适的方案&#xff0c;比如使用单片机还是stm32或是更高端的芯…

前端vue实现甘特图

1 什么是甘特图 甘特图(Gantt chart)又称为横道图、条状图(Bar chart)。以提出者亨利L甘特先生的名字命名&#xff0c;是项目管理、生产排程、节点管理中非常常见的一个功能。 甘特图内在思想简单&#xff0c;即以图示的方式通过活动列表和时间刻度形象地表示出任何特定项目的…

考研数学|《660》题这样刷,效率最高!

首先要了解一下660的难度&#xff0c;我认为660的难度是在基础和强化之间的一本习题册。具有一些比较好的选填题目&#xff0c;有些题目可能对初学者来说较为困难&#xff0c;尤其是一些包含二级结论的一些题目。但是对于强化阶段来说&#xff0c;这本题目可能难度较低了一些。…

基于springboot+vue的游戏交易系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

C语言学习 三、运算符与表达式

3.1 运算符分类 c语言提供了13种类型的运算符&#xff0c;如下所示&#xff1a; &#xff08;1&#xff09;算术运算符&#xff08; - * / %&#xff09; &#xff08;2&#xff09;关系运算符&#xff08;> < > < !&#xff09; &#xff08;3&#xff09;逻…

【职位管理】某大型能源化工企业职位管理体系搭建咨询项目

随着企业的发展&#xff0c;企业内部员工的数量不断增加。而管理职位的有限性使得员工的晋升出现“过独木桥”的现象&#xff0c;有些企业为了缓解这种问题&#xff0c;采取增设副职与助理岗位的办法&#xff0c;却导致组织机构迅速膨胀。那么&#xff0c;华恒智信专家是如何解…