首页 > 其他分享 >Quartz集群增强版_01.集群及缺火处理(ClusterMisfireHandler)

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

时间:2024-11-12 21:59:32浏览次数:1  
标签:Quartz app 增强版 job 集群 time 执行 节点

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 开源项目的启发,就是思考能不能在竞争锁之前判断锁定时间,获取到锁之后加一个锁定时间

标签:Quartz,app,增强版,job,集群,time,执行,节点
From: https://www.cnblogs.com/funnyzpc/p/18542452

相关文章

  • redis集群搭建 - cluster模式
    概述搭建一套rediscluster集群。架构192.168.0.21:6379主192.168.0.23:6380从192.168.0.22:6379主192.168.0.21:6380从192.168.0.23:6379主192.168.0.22:6380从修改这三台服务器的host文件(选做)vim/etc/hosts192.168.0.21node1192.168.0.22node2192.168.......
  • mongos 分片集群
    1、先配置配置服务器,113-114两个集群,端口27018,配置文件/etc/mongod27018.conf,服务mongod27018.servicemongo--host127.0.0.1--port27018useadmindb.auth('root','password')rs.stauts()2、配置分片服务器113-1186个分片,每个分片又是一个单独的集群模式,端口27017......
  • ElasticSearch 7.14 向已启用XPACK认证的集群增加新的节点
    一、环境现状描述:     目前的ElasticSearch集群仅有一个单一节点,且这个集群中已建立有索引,索引已包含业务文档数据(超过200G),该集群已经启用XPACK认证,现希望扩展这个集群,增加复制节点,且复制节点启动后,自动从主节点同步数据到新节点。     目前的ElasticSearch集群节点......
  • Quartz集群增强版_00.How to use?(如何使用)
    Quartz集群增强版_00.Howtouse?(如何使用)转载请著名出处https://www.cnblogs.com/funnyzpc/p/18540378开源地址https://github.com/funnyzpc/quartz表的基本结构    总的来说任务的配置及开发基本遵从上图的表的基本关系,除app以及node之外均需要手动手动配置......
  • 企业生产环境-麒麟V10(ARM架构)操作系统部署Zookeeper单节点&高可用集群版
    前言:ZooKeeper是一个分布式协调服务,它为分布式应用提供一致性服务,是ApacheHadoop的子项目。它被设计为易于编程,同时具有高性能和高可靠性。ZooKeeper提供了一个简单的接口和一些基本的文件系统操作,使得开发者能够快速地构建分布式应用。以下是ZooKeeper的一些关键特性和概念:......
  • Docker Redis Sentinel 高可用集群搭建指南
    DockerRedisSentinel高可用集群搭建指南前提条件:已安装Docker和docker-compose。配置了镜像源加速,确保能够正常拉取镜像。一、手动搭建DockerRedisSentinel1.拉取Redis镜像dockerpullredis2.Redis集群IP和端口设置(单机测试)主节点:ip:6379从节点:i......
  • 以太网链路聚合与交换机堆叠、集群
    一、网络可靠性需求网络的可靠性可以从单板、设备、链路多个层面实现。1.单板可靠性2.设备可靠性3.链路可靠性二、链路聚合技术原理与配置1.以太网链路聚合Eth-Trunk基本原理链路聚合,通过将多个物理接口捆绑成为一个逻辑接口,可以在不进行硬件升级的条件下,达到增加......
  • redis cluster集群部署(docker部署)
    1.集群说明  三台测试机器172.16.4.78、172.16.4.79、172.16.4.80,做三主三从,请提前安装好docker以及docker-compose主从槽位容器名称库连接方式172.16.4.78:26379172.16.4.78:263800-5460redis_26379、redis_26380cluster集群只有0库代理三个主库做轮询172.16.4......
  • WireGuard 多节点集群的Shell 半自动化配置脚本
    要将上述WireGuard配置脚本扩展为支持 多节点集群(即多个服务器和客户端节点的配置),我们需要做出一些修改:支持多个 服务器 和 客户端 配置。自动为每个节点生成独特的配置文件,并确保它们能够互相通信。支持集群模式,配置多个 服务器端 和 客户端 节点之间的连接。以......
  • 【k8s安全】etcd未授权到控制k8s集群
    免责声明本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者及本公众号团队不为此承担任何责任。在安装完K8s后,默认会安装etcd组件,etcd是一个高可用的key-value数据库,它为k8s集群提供底层数据存储,保存了......