- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
转载请著名出处 https://www.cnblogs.com/funnyzpc/p/18542452 。
应用(app)与节点(node)状态同步 。
不管是 node 还是 app,都可以通过对应 state 来控制节点及整个应用的启停,这是很重要的功能,同时对于集群/缺火的锁操作也是基于 app 来做的,同时附加在 app 上的这个锁是控制所有应用及集群之间的并发操作,同样也是很重要的~ 。
任务状态与执行状态更新 。
因为任务扫描主要操作的是执行时间项(execute)信息,同时变更的也是执行项的状态(state),故此需要更新任务(job)状态 。
熄火任务恢复执行 。
任务扫描调度的过程可能存在 GC 及 DB断连 的情况,需要及时修正 next_fire_time 以保证在异常恢复后能正常被扫到并被执行 。
清理历史记录 。
清理的执行频度很低,如果可以的话建议是后管接入 click sdk 手动操作,这里的自动清理是兜底方案,基于数据库锁的任务并发在表数据越少时性能理论上就越好~ ,自动清理有两大任务:
创建应用及执行节点 。
这是必要的操作,预创建节点及应用方便后续管理,同时执行调度也依赖于节点及应用的状态 。
前置处理指的是 Quartz 启动时必做的维护,主要包含三部分主要内容:
app
) 及 节点(node
) ,这是很重要的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);
}
获取当前应用下的所有执行中或异常的任务(job),并逐步恢复任务下所有执行中(EXECUTING)或异常(ERROR)的任务,主要是重新计算 next_fire_time 。
check
标志,这个 check
标志主要方便于后续清理之使用,同时 app
上的 check (time_next
) 是作为锁定周期的判断依据这个问题可以详细说明一下,一般一个loop(循环)是 15s(TIME_CHECK_INTERVAL) ,在集群环境中同时存在多个节点的并发问题,所以对集群及缺火的处理就存在重复执行 一开始我的思考是按照乐观锁的思路来做,代码大概是这样的:
int ct = getDelegate().updateQrtzAppByApp(conn,app);
// 5.获取app锁的才可执行 clear 清理以及 recover 恢复,以减少读写
if( ct>0 ){
// 获取到锁后的处理
}
但是这样存在重复执行的情况,具体情况先看图:
上图中node1 与 node2 的开始时间相差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)的文章就讲到这里了,如果你想了解更多关于Quartz集群增强版_01.集群及缺火处理(ClusterMisfireHandler)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试使用增强的 for 循环遍历 Iterable,但我无法确定何时处理最后一个值。 public void apply(Tuple key,
我正在使用以下代码在 Sheet2 的 A:H 范围内查找和替换 Sheet1 中存在的单词列表(ColA 用于 FIND 单词,ColB 用于 REPLACE 单词)。它执行这项工作,但非常缓慢。可
我正在使用 Hibernate (JPA2) hibernate.hbm2ddl.auto=update用于测试和 hibernate.hbm2ddl.auto=validate用于生产。 我想要做的
基本问题: 为什么我只能用 Scala 编写: println(10) 为什么我不需要写: Console println(10) 后续问题: 如何引入一个新方法“foo”,它像“println”一样
我正在尝试将 Maven 项目迁移到 Bazel,但在 Datanucleus 增强方面遇到了麻烦。 后 jar -file 已构建,Datanucleus 会查看其中的内部并执行一些字节码操作以增强
正在使用 css3 转换进行漂亮的导航。为此还编写了一些 javascript。 但不幸的是它看起来有点凌乱。你们能给我一些优化 javascript 代码的技巧吗? 笔--> http://code
我想将自定义任务绑定(bind)到默认构建器发布周期中。我想在项目编译、打包、标记和部署之后但在增加版本号并提交之前运行此代码。 我将如何融入发布周期的这一部分? 最佳答案 不幸的是,release
我使用ElasticSearch 6.6。我的应用程序通过从不同数据源提取数据来构建ES索引。搜索未指定数据源。它只是建立一个类似的查询: GET employerdata/_search { "
我正在使用此代码将“k1 = v1; k2 = v2; k3 = v3; kn = vn”字符串解析为映射。 qi::phrase_parse( begin,end,
我正在试图弄清楚作业的一部分,但我已经把头撞在墙上有一段时间了。我正在尝试将 DNA 序列转录为 RNA 序列。然而,我收到了 ArrayOutOfBoundsException。我不熟悉使用增强的
我需要对基于 python Google App Engine 的应用程序的警告进行分类。我从 GAE stackdriver 下载日志。我认为 GAE Stackdriver 错误报告位于 http
我有一个 django charField,通过 is_valid() 方法进行检查。用户应该在此字段中输入有效的逻辑表达式,因此我编写了一个解析方法,如果表达式不正确,该方法会引发异常。 如何增强
我编写了以下控制台应用程序,要求用户输入一天。 我需要一些帮助才能改进,以便他们为一周中的所有日子提供正确的答案。 如果用户输入除星期一以外的任何其他日期,则输出为“今天”、“昨天”、“明天”,并在这
我在使用带有 ES6 let 关键字的模块模式(扩充)时遇到错误。 这有效。 var Example = ( Example => { Example.name = ""; retur
我只是问是否线程安全可以使用 我明确指出“doSomething()”是线程安全的。 最佳答案 线程安全取决于您正在迭代的 Collection,而不是 enhanced for 的使用。如果 Col
我有一个非常符合 this Jquery demo 的要求,这是一个简单的购物车演示。基本上,我需要对该演示进行两项改进。 我需要文本输入以及可用的“产品”。因此,当我拖放其中一种产品时,文本字段应随
我正在三个表 messages、message_recipients 和 users 上运行查询。 messages表的表结构: id int pk message_id int message te
这个问题已经有答案了: In detail, how does the 'for each' loop work in Java? (29 个回答) 已关闭 4 年前。 由于增强的 for 循环是只读
我在 css 中制作了一个很酷的鼠标悬停,当父级鼠标悬停时它会显示动画 gif。 这是我的代码:http://codepen.io/clemeeent/pen/oggzMa 问题是我将有大约 40 天
目前,当使用 Knockout foreach 绑定(bind)时,您可以使用 $index 访问当前索引。我想让其他类似的功能可用于我的内部绑定(bind) - 例如: array(让我访问正在操作
我是一名优秀的程序员,十分优秀!