Quartz集群增强版_01.集群及缺火处理(ClusterMisfireHandler)

转载请著名出处 https://www.cnblogs.com/funnyzpc/p/18542452

主要目的

  • 应用(app)与节点(node)状态同步

    不管是 node 还是 app,都可以通过对应 state 来控制节点及整个应用的启停,这是很重要的功能,同时对于集群/缺火的锁操作也是基于 app 来做的,同时附加在 app 上的这个锁是控制所有应用及集群之间的并发操作,同样也是很重要的~

  • 任务状态与执行状态更新

    因为任务扫描主要操作的是执行时间项(execute)信息,同时变更的也是执行项的状态(state),故此需要更新任务(job)状态

  • 熄火任务恢复执行

    任务扫描调度的过程可能存在 GCDB断连 的情况,需要及时修正 next_fire_time 以保证在异常恢复后能正常被扫到并被执行

  • 清理历史记录

    清理的执行频度很低,如果可以的话建议是后管接入 click sdk 手动操作,这里的自动清理是兜底方案,基于数据库锁的任务并发在表数据越少时性能理论上就越好~ ,自动清理有两大任务:

    • 1.清理执行无效应用及非执行节点
    • 2.清理任务及执行配置
  • 创建应用及执行节点

这是必要的操作,预创建节点及应用方便后续管理,同时执行调度也依赖于节点及应用的状态

前置处理

前置处理指的是 Quartz 启动时必做的维护,主要包含三部分主要内容:

  • 01.写入应用(app) 及 节点(node) ,这是很重要的
  • 02.恢复/更新应用状态
  • 将执行中或异常的 job 拿出来并检查其关联的执行项,通过执行项(execute)的状态更新任务(job)状态,如果

    多执行项存在多个状态,状态的优先级为(从高到低):ERROR->EXECUTING->PAUSED->COMPLETE

    代码表象为 :
 List<QrtzExecute> executes = getDelegate().getExecuteByJobId(conn,job.getId());
boolean hasExecuting = false;
boolean hasPaused = false;
boolean hasError = false;
boolean hasComplete = false;
for( QrtzExecute execute:executes ){
final String state = execute.getState();
if("EXECUTING".equals(state)){
hasExecuting=true;
}else if("PAUSED".equals(state)){
hasPaused=true;
}else if("ERROR".equals(state)){
hasError=true;
}else if("COMPLETE".equals(state)){
hasComplete=true;
}else{
continue; // 这里一般是INIT
}
}
// 如果所有状态都有则按以下优先级来
String beforeState = job.getState();
if(hasError){
job.setState("ERROR");
}else if(hasExecuting){
job.setState("EXECUTING");
}else if(hasPaused){
job.setState("PAUSED");
}else if(hasComplete){
job.setState("COMPLETE");
}else{
continue; // 这里对应上面的INIT状态,不做处理
}
// 不做无谓的更新...
if(!job.getState().equals(beforeState)){
job.setUpdateTime(now);
getDelegate().updateRecoverJob(conn,job);
}
  • 03.恢复/更新执行状态

获取当前应用下的所有执行中或异常的任务(job),并逐步恢复任务下所有执行中(EXECUTING)或异常(ERROR)的任务,主要是重新计算 next_fire_time

后置处理

  • 01.后置处理的内容是包含所有前置处理,同时对集群并发做了加锁 (这个很重要,后一段会讲到)
  • 02.同步节点状态与应用状态不一致的问题
  • 03.更新 check 标志,这个 check 标志主要方便于后续清理之使用,同时 app 上的 check (time_next) 是作为锁定周期的判断依据

?关于并发锁的处理

这个问题可以详细说明一下,一般一个loop(循环)是 15s(TIME_CHECK_INTERVAL) ,在集群环境中同时存在多个节点的并发问题,所以对集群及缺火的处理就存在重复执行

一开始我的思考是按照乐观锁的思路来做,代码大概是这样的:

    int ct = getDelegate().updateQrtzAppByApp(conn,app);
// 5.获取app锁的才可执行 clear 清理以及 recover 恢复,以减少读写
if( ct>0 ){
// 获取到锁后的处理
}

但是这样存在重复执行的情况,具体情况先看图:

上图中node1node2 的开始时间相差5s,所以造成了他们获取锁的时间存在5s的时间差异,因为有这5s的存在,多个节点几乎都可以执行这个update语句以获取锁,这样往下的逻辑必然存在重复执行!

任务调度扫描(QuartzSchedulerThread)是统一等到 next_fire_time 的那一刻来竞争锁,而集群/缺火处理(ClusterMisfireHandler)在一个 while 的大循环内 这个循环每次是15s,所以每个节点的所执行的周期是15s(TIME_CHECK_INTERVAL),而锁的竞争却是在执行 update 的那一刻

如果借用 任务扫描(QuartzSchedulerThread )的处理思路就是 再加一个 while 或者 sleep 等待到下一个 check_time(time_next),代码将如下:

    long t=0;
// 这里的 check_time 就是应用的check时间,loop_time则是当前循环开始时间
if( (t=check_time-loop_time)> 0 ){
Thread.sleep(t);
}
int ct = getDelegate().updateQrtzAppByApp(conn,app);
// 5.获取app锁的才可执行 clear 清理以及 recover 恢复,以减少读写
if( ct>0 ){
// 获取到锁后的处理
}

以上这样就可以可以基本保证多个node在同一时间竞争同一把锁了... ,这样做还有一个好处,就是基本保证了各个节点的 ClusterMisfireHandler循环时间基本一致,同时通过sleep可以随机打散循环时间(添加偏移量)将

ClusterMisfireHandler 的循环处理打散在其他节点执行 。

但是,但是哦,如果使用 sleep + update 的方式 也可能导致同一时间加锁(update)竞争的开销,所以,我借鉴了 shedlock 开源项目的启发,就是思考能不能在竞争锁之前判断锁定时间,获取到锁之后加一个锁定时间

锁定时间内的不再去竞争锁,锁定时间外的则可以,大致如图:

看图,如果我们假定 node1 是先于 node2 执行, 当 node1 在 14:15 成功获取锁后 他的下一次执行时间预期就是 14:30 ,同时如果加一个10s锁定时间(图中蓝线),就是在 15:25 及之前是不可以去竞争锁,这样当

node2 在 14:20 去尝试获取锁之前发现最近一个锁定时间点是 14:25 (及之后) ,此时 node2 会自动放弃竞争锁(执行update),同时进入下一时间点 14:35 并再次判断锁定时间点儿,当然这并不是没有代价的,各位自行领悟吧

经过改造后的代码如下:

   // TIME_CHECK_INTERVAL 是循环周期,固定为15秒
long tw = TIME_CHECK_INTERVAL/10*3; // 70% 减少并发
if( (app.getTimeNext()-_start)>tw ){
continue;
} // 5.获取app锁的才可执行 clear 清理以及 recover 恢复,以减少读写
if( ct>0 ){
// 获取到锁后的处理
}

Quartz集群增强版_01.集群及缺火处理(ClusterMisfireHandler)的更多相关文章

  1. 【Quartz】配置最简单的集群

    在许多情况,我们希望我们的定时任务是可靠的,不会因系统故障.机器宕机而导致某一笔定时任务不能按时运行.这种情况下,我们就需要为Quartz做个集群. 最简单的情况,有两台机器或两个应用,同时维护一批定 ...

  2. 高可用OpenStack(Queen版)集群-13.分布式存储Ceph

    参考文档: Install-guide:https://docs.openstack.org/install-guide/ OpenStack High Availability Guide:http ...

  3. docker搭建kafka集群(高级版)

    1. 环境docker, docker-compose 2.zookeeper集群 /data/zookeeper/zoo1/config/zoo.cfg # The number of millis ...

  4. Kubernetes集群搭建之Etcd集群配置篇

    介绍 etcd 是一个分布式一致性k-v存储系统,可用于服务注册发现与共享配置,具有以下优点. 简单 : 相比于晦涩难懂的paxos算法,etcd基于相对简单且易实现的raft算法实现一致性,并通过g ...

  5. Kubernetes使用集群联邦实现多集群管理

    Kubernetes在1.3版本之后,增加了“集群联邦”Federation的功能.这个功能使企业能够快速有效的.低成本的跨区跨域.甚至在不同的云平台上运行集群.这个功能可以按照地理位置创建一个复制机 ...

  6. 庐山真面目之十二微服务架构基于Docker搭建Consul集群、Ocelot网关集群和IdentityServer版本实现

    庐山真面目之十二微服务架构基于Docker搭建Consul集群.Ocelot网关集群和IdentityServer版本实现 一.简介      在第七篇文章<庐山真面目之七微服务架构Consul ...

  7. 用 edgeadm 一键安装边缘 K8s 集群和原生 K8s 集群

    背景 目前,很多边缘计算容器开源项目在使用上均存在一个默认的前提:用户需要提前准备一个标准的或者特定工具搭建的 Kubernetes 集群,然后再通过特定工具或者其他方式在集群中部署相应组件来体验边缘 ...

  8. 分布式缓存技术redis学习系列(四)——redis高级应用(集群搭建、集群分区原理、集群操作)

    本文是redis学习系列的第四篇,前面我们学习了redis的数据结构和一些高级特性,点击下面链接可回看 <详细讲解redis数据结构(内存模型)以及常用命令> <redis高级应用( ...

  9. redis 集群环境搭建-redis集群管理

    集群架构 (1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽. (2)节点的fail是通过集群中超过半数的节点检测失效时才生效. (3)客户端与redi ...

  10. 分布式缓存技术redis学习(四)——redis高级应用(集群搭建、集群分区原理、集群操作)

    本文是redis学习系列的第四篇,前面我们学习了redis的数据结构和一些高级特性,点击下面链接可回看 <详细讲解redis数据结构(内存模型)以及常用命令> <redis高级应用( ...

随机推荐

  1. 彻底解决ROS1安装问题,一键解决远离rosdep init 和rosdep update出错

    彻底解决ROS1安装问题 相信很多小伙伴在刚入门ROS的时候就遇到ROS安装这个拦路虎. 普遍出错的环节在rosdep init 和rosdep update,由于要访问国外的站点就导致国内访问很容易 ...

  2. Maven 设置 JDK 版本

    Maven 设置 JDK 版本是通过 Apache Maven Compiler Plugin 插件实现的.它用于编译项目的源代码. 方法一 有时候你可能需要将某个项目编译到与当前使用的 JDK 版本 ...

  3. LLM论文研读: MindSearch

    1. 背景 近日中科大与上海人工智能实验室联合推出的MindSearch思索,引起了不小的关注,github上的星标,短短几周时间,已经飙到了4.2K.看来确实有些内容,因此本qiang~研读了论文及 ...

  4. nftables

    RHEL 8 用 ntftable 替换 iptables, 新安装的 CentOS 8.3 是这样的: [root@net182-host113 ~]# nft list tablestable i ...

  5. 开源文档管理系统 MinDoc 安装和使用教程

    说到文档管理,很多团队的文档管理都是一团糟,每个员工在自己本地写了各种 Word 文档.Excel 表格.甚至还有手写的便签,到处都是,找起来就像大海捞针.有些聪明的团队开始用飞书来管理团队文档,但是 ...

  6. 6.24.2 数据库&漏洞口令&应急取证

    windows日志分析神器 logonTracer-外内网日志 github下载:#JPCERTCC/LogonTracer:通过可视化和分析 Windows 事件日志来调查恶意 Windows 登录 ...

  7. 用CSS border画一个铅笔

    先上效果图 该例子来自 CSS世界 的书中项目 总结技巧如下: 巧用 border 和 伪元素 来 绘制层叠效果. 使用 transform-origin 来改变元素的轴心 使用 filter:dro ...

  8. 即构音视频 Express Flutter SDK 全面支持空安全

    Flutter 是一套移动 UI 框架,可以快速在 iOS.Android 上构建高质量的原生用户界面.作为其官方语言 Dart 是类型安全的,当开发者获取变量的时候,编译器可以保证变量的类型,但类型 ...

  9. 算法与数据结构——AVL树(平衡二叉搜索树)

    AVL树 在"二叉搜索树"章节提到,在多次插入和删除操作后,二叉搜索树可能退化为链表.在这种情况下,所有操作的时间复杂度将从O(logn)劣化为O(n). 如下图,经过两次删除节点 ...

  10. Python 潮流周刊#72:Python 3.13.0 最终版已发布!(摘要)

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...