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.rhkb.cn/news/281045.html

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

相关文章

jscpd检测代码的重复率

官方文档&#xff1a;jscpd jscpd 是一个开源的代码重复检测工具&#xff0c;它用于查找代码中的重复部分并生成相应的报告 1、比较两个目录之间的差异 yarn jscpd --skipLocal sre/test1/** sre/test2 --reporters html生成报告文档在 ./report/html 下面&#xff0c;可以打开…

linux下用docker安装mysql及导入文件

目录 1. 非root用户设置docker权限2. user账号安装mysql2. root账号打开防火墙3. 启动mysql容器3.1 在指定工作目录下建立文件夹3.2 配置文件3.3 开启mysql容器 4. 进入容器4.1 通过容器进入mysql4.1 设置账号4.2 建立数据库4.3 导入文件 5. windows连接数据库参考文件 1. 非ro…

水泥领域智慧工厂物联网解决方案

水泥领域智慧工厂物联网解决方案 在水泥生产行业中&#xff0c;构建智慧工厂物联网解决方案已经成为推动产业升级、实现智能制造的关键路径。该方案深度融合了先进的信息技术与传统的水泥生产工艺&#xff0c;通过全面感知、可靠传输、智能处理等环节&#xff0c;实现了对整个…

MySql实战--深入浅出索引(上)

提到数据库索引&#xff0c;我想你并不陌生&#xff0c;在日常工作中会经常接触到。比如某一个SQL查询比较慢&#xff0c;分析完原因之后&#xff0c;你可能就会说“给某个字段加个索引吧”之类的解决方案。但到底什么是索引&#xff0c;索引又是如何工作的呢&#xff1f;今天就…

oracle设置主键自增步骤

设置主键自增步骤&#xff1a; 每一张表都要设置序列&#xff0c;然后设置触发器。比mysql繁琐。 一、设置序列 选中表后&#xff0c;—》 文件—》新建—》其他—》序列. 设置如下四个值即可。 crtls保存。 给序列起个名字&#xff0c;一定要全大写字母。 二、设置触发器…

如何设置IDEA远程连接服务器开发环境并结合cpolar实现ssh远程开发

文章目录 1. 检查Linux SSH服务2. 本地连接测试3. Linux 安装Cpolar4. 创建远程连接公网地址5. 公网远程连接测试6. 固定连接公网地址7. 固定地址连接测试 本文主要介绍如何在IDEA中设置远程连接服务器开发环境&#xff0c;并结合Cpolar内网穿透工具实现无公网远程连接&#xf…

Pudgy Penguins交易量一路攀升 多次创下历史新高

日前&#xff0c;一个名为胖企鹅&#xff08;Pudgy Penguins&#xff09; NFT 项目交易量持续攀升&#xff0c;一度在3月9日成为NFT市场的“销冠”。事实上&#xff0c;从2023年下半年开始&#xff0c;Pudgy Penguins的地板价就在不断上升&#xff0c;进入2024年更是多次创下历…

算法打卡day11

今日任务&#xff1a; 1&#xff09;239. 滑动窗口最大值 2&#xff09;347.前 K 个高频元素 239. 滑动窗口最大值 题目链接&#xff1a;239. 滑动窗口最大值 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移…

TouchGFX之性能测量

TouchGFX Core开放了几个信号&#xff0c;可用于测量性能。 当这些信号在内部触发时&#xff0c;用户可在应用程序中同步触发单个GPIO&#xff0c;从而实现“渲染时间”和其他有用信号的可视化。 信号在GPIO.hpp中定义 /* 用于操作GPIO的接口类&#xff0c;以便在目标硬件上进…

发布 AUR 软件包 (ArchLinux)

首发日期 2024-03-09, 以下为原文内容: 理论上来说, 我们应该平等的对待每一个 GNU/Linux 发行版本. 但是, 因为窝日常使用 ArchLinux, 所以对 ArchLinux 有一些特别的优待, 比如自己做的软件优先为 ArchLinux 打包发布. 本文以软件包 librush-bin 为例, 介绍发布 AUR 软件包的…

构建一个前端智能停车可视化系统

引言 随着城市化进程的加速&#xff0c;停车难问题日益突出。智能停车可视化系统通过实时展示停车场的车位信息&#xff0c;帮助用户快速找到空闲车位&#xff0c;提高停车效率。 目录 引言 一、系统设计 二、代码实现 1. 环境准备 2. 安装依赖 3. 创建停车场组件 4. 集…

【蓝桥杯入门记录】继电器、蜂鸣器及原理图分析

一、继电器、继电器概述 &#xff08;1&#xff09;蜂鸣器原理 蜂鸣器的发声原理由振动装置和谐振装置组成&#xff0c;而蜂鸣器又分为无源他激型与有源自激型&#xff0c;蜂鸣器的发声原理为: 1、无源他激型蜂鸣器的工作发声原理是&#xff1a;方波信号输入谐振装置转换为声…

Docker容器化技术(docker-compose示例:部署discuz论坛和wordpress博客,使用adminer管理数据库)

安装docker-compose [rootservice ~]# systemctl stop firewalld [rootservice ~]# setenforce 0 [rootservice ~]# systemctl start docker[rootservice ~]# wget https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64创建目录 [rootse…

HarmonyOS NEXT应用开发之跨文件样式复用和组件复用

介绍 本示例主要介绍了跨文件样式复用和组件复用的场景。在应用开发中&#xff0c;我们通常需要使用相同功能和样式的ArkUI组件&#xff0c;例如购物页面中会使用相同样式的Button按钮、Text显示文字&#xff0c;我们常用的方法是抽取公共样式或者封装成一个自定义组件到公共组…

JavaEE 初阶篇-深入了解操作系统中的进程与 PCB

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 关于计算机是如何进行工作的 “常识” 1.1 关于寄存器、缓存与内存是如何配合 CPU “工作” 2.0 操作系统概述 2.1 操作系统内核 2.2 进程 2.3 PCB 2.3.1 PCB 属性…

QT增加线程函数步骤流程

在使用线程的时候&#xff0c;不仅要关注线程开启的时机&#xff0c;同时还要关注线程安全退出&#xff0c;这样才能保证程序的健壮性&#xff0c;如果线程开启的较多&#xff0c;且开启关闭比较频繁&#xff0c;建议使用线程池来处理。开启线程有三种方式&#xff1a;第一种C的…

【vue baidu-map】实现百度地图展示基地,鼠标悬浮标注点展示详细信息

实现效果如下&#xff1a; 自用代码记录 <template><div class"map" style"position: relative;"><baidu-mapid"bjmap":scroll-wheel-zoom"true":auto-resize"true"ready"handler"><bm-mar…

怎么轻松制作证件照?推荐这三款制作工具!

在日常生活中&#xff0c;我们经常需要制作各种证件照&#xff0c;如身份证、护照、驾驶证等。为了帮助大家快速、便捷地制作证件照&#xff0c;我将在本文中推荐三款优秀的证件照制作工具&#xff0c;包括国内外的软件&#xff0c;满足不同用户的需求。1.水印云 水印云是一款国…

MQ组件之RabbitMQ学习

MQ组件之RabbitMQ入门 同步调用和异步调用 在微服务架构中&#xff0c;服务之间的调用有同步调用和异步调用两种方式。 我们使用OpenFeign去调用是同步调用&#xff0c;同步调用的缺点很明显&#xff0c;在下图的场景中&#xff0c;支付完成后需要调用订单服务、仓库服务、短…

SpringBoot集成WebService

1&#xff09;添加依赖 <dependency><groupId>org.apache.cxf</groupId><artifactId>cxf-spring-boot-starter-jaxws</artifactId><version>3.3.4</version><exclusions><exclusion><groupId>javax.validation<…