Elastic-job 启动阻塞“假死”的问题

2023-11-13

问题记录

最近项目引入Elastic Job实现定时任务的分布式调度。引入的版本2.1.5,加入相关的job配置后启动项目,主线程假死,不进行后续逻辑处理和日志输出。

输出的日志如下:


[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 15:53:27.049] [] [StdSchedulerFactory] [Using default implementation for ThreadExecutor]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 15:53:27.130] [] [SchedulerSignalerImpl] [Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 15:53:27.131] [] [QuartzScheduler] [Quartz Scheduler v.2.2.1 created.]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 15:53:27.135] [] [JobShutdownHookPlugin] [Registering Quartz shutdown hook.]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 15:53:27.136] [] [RAMJobStore] [RAMJobStore initialized.]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 15:53:27.139] [] [QuartzScheduler] [Scheduler meta-data: Quartz Scheduler (v2.2.1) 'dailyScanMercReratingJob' with instanceId 'NON_CLUSTERED'

  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.

  NOT STARTED.

  Currently in standby mode.

  Number of jobs executed: 0

  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 1 threads.

  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 15:53:27.139] [] [StdSchedulerFactory] [Quartz scheduler 'dailyScanMercReratingJob' initialized from an externally provided properties instance.]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 15:53:27.139] [] [StdSchedulerFactory] [Quartz scheduler version: 2.2.1]

解决方案

直接上解决方案:将项目的curator的框架版本全部调整为2.10.0 ,包括

  • curator-client;

  • curator-recipes;

  • curator-framework

项目中因为curator-framework引用了2.7.0导致出现了此问题。

问题追踪

在项目的Spring框架的以下位置打断点追踪项目启动过程:

  • ContextLoaderListener.contextInitialized() 方法

  • AbstractApplicationContext.refresh() 方法;

发现代码在AbstractApplicationContext.refresh() 方法里,执行: finishBeanFactoryInitialization(beanFactory) 时陷入等待一直无法跳出继续执行。

根据Spring框架的启动机制,finishBeanFactoryInitialization 是完成单例bean的初始化的方法,这个方法会去真正操作elastic-job 对于job的操作代码。

从日志中发现代码最后一行输出为:


[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 15:53:27.139] [] [StdSchedulerFactory] [Quartz scheduler version: 2.2.1]

StdSchedulerFactory 类中找到关于Quartz scheduler version 相关的日志打断点继续跟踪:


//…………………… 省略之前的代码

jrsf.initialize(scheduler);



            qs.initialize();



            getLog().info(

                    "Quartz scheduler '" + scheduler.getSchedulerName()

                            + "' initialized from " + propSrc);

    //这里断点

            getLog().info("Quartz scheduler version: " + qs.getVersion());



            // prevents the repository from being garbage collected

            qs.addNoGCObject(schedRep);

            // prevents the db manager from being garbage collected

            if (dbMgr != null) {

                qs.addNoGCObject(dbMgr);

            }



            schedRep.bind(scheduler);

            return scheduler;

在上面的位置断点后发现,elastic job 继续执行,持续跟踪最终跟踪到SchedulerFacade类的registerStartUpInfo方法:


  /**

    * 注册作业启动信息.

    *

    * @param enabled 作业是否启用

    */

    public void registerStartUpInfo(final boolean enabled) {

        listenerManager.startAllListeners();

        leaderService.electLeader();

        serverService.persistOnline(enabled);

        instanceService.persistOnline();

        shardingService.setReshardingFlag();

        monitorService.listen();

        if (!reconcileService.isRunning()) {

            reconcileService.startAsync();

        }

    }

代码在leaderService.electLeader(); 陷入等待。

根据以上最终可用得出结论:

  • <font color='red'>elastic-job 在job的选主过程中陷入了无限等待,即无法选出主节点执行任务。</font>

根据对LeaderService 的代码的研究,elastic job 选主使用的是 curator框架的 LeaderLatch 类完成的。

具体时线程wait的操作在:JobNodeStorageexecuteInLeader方法中:


/**

    * 在主节点执行操作.

    *

    * @param latchNode 分布式锁使用的作业节点名称

    * @param callback 执行操作的回调

    */

    public void executeInLeader(final String latchNode, final LeaderExecutionCallback callback) {

        try (LeaderLatch latch = new LeaderLatch(getClient(), jobNodePath.getFullPath(latchNode))) {

            latch.start();

            latch.await();

            callback.execute();

        //CHECKSTYLE:OFF

        } catch (final Exception ex) {

        //CHECKSTYLE:ON

            handleException(ex);

        }

    }

上面的方法调用 latch.await(); 来等待获取 leadership。由于无法获取主节点,导致线程一致wait。

LeaderLatch 大概的机制为:所有客户端向zk的同一个path竞争的写入数据,谁先写入成功谁就获取了leadership

LeaderLatchawait方法如下:


public void await() throws InterruptedException, EOFException

    {

        synchronized(this)

        {

            while ( (state.get() == State.STARTED) && !hasLeadership.get() )

            {

                wait();

            }

        }

        if ( state.get() != State.STARTED )

        {

            throw new EOFException();

        }

    }

如果LeaderLatch无法获取leadership那么就当前的Thread就会一直陷入wait

问题解决

定位到问题的发生点,解决问题的思路就要看为什么无法获取到leadership

登录到ZK上查询节点信息,发现正常项目启动后,elastic job会向zk的写入如下格式的节点内容:


/{job-namespace}/{job-id}/leader/election/latch

但是异常的项目是没有这个节点的,所以应该是ZK的操作发生了问题。具体哪里发生了问题这里还没有发现。

继续将项目日志调整为DEBUG级别会发下有如下的日志输出:


[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 17:51:47.687] [] [RAMJobStore] [RAMJobStore initialized.]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 17:51:47.689] [] [QuartzScheduler] [Scheduler meta-data: Quartz Scheduler (v2.2.1) 'dailyScanMercReratingJob' with instanceId 'NON_CLUSTERED'

  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.

  NOT STARTED.

  Currently in standby mode.

  Number of jobs executed: 0

  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 1 threads.

  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 17:51:47.689] [] [StdSchedulerFactory] [Quartz scheduler 'dailyScanMercReratingJob' initialized from an externally provided properties instance.]

[INFO] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 17:51:47.689] [] [StdSchedulerFactory] [Quartz scheduler version: 2.2.1]

[DEBUG] [Timer-0] [2018-10-10 17:51:49.553] [] [UpdateChecker] [Checking for available updated version of Quartz...]

[DEBUG] [RMI TCP Connection(2)-127.0.0.1] [2018-10-10 17:51:49.586] [] [LeaderService] [Elect a new leader now.]

[DEBUG] [Curator-TreeCache-0] [2018-10-10 17:51:50.724] [] [RegExceptionHandler] [Elastic job: ignored exception for: KeeperErrorCode = NoNode for /payrisk-job/dailyScanMercReratingJob/leader/election/instance]

[DEBUG] [Curator-TreeCache-0] [2018-10-10 17:51:50.738] [] [RegExceptionHandler] [Elastic job: ignored exception for: KeeperErrorCode = NoNode for /payrisk-job/dailyScanMercReratingJob/leader/election/instance]

[DEBUG] [Curator-TreeCache-0] [2018-10-10 17:51:50.759] [] [RegExceptionHandler] [Elastic job: ignored exception for: KeeperErrorCode = NoNode for /payrisk-job/dailyScanMercReratingJob/leader/election/instance]

[DEBUG] [Curator-TreeCache-0] [2018-10-10 17:51:50.769] [] [RegExceptionHandler] [Elastic job: ignored exception for: KeeperErrorCode = NoNode for /payrisk-job/dailyScanMercReratingJob/leader/election/instance]

[DEBUG] [Curator-TreeCache-0] [2018-10-10 17:51:50.791] [] [RegExceptionHandler] [Elastic job: ignored exception for: KeeperErrorCode = NoNode for /payrisk-job/dailyScanMercReratingJob/leader/election/instance]

[DEBUG] [Curator-TreeCache-0] [2018-10-10 17:51:50.803] [] [RegExceptionHandler] [Elastic job: ignored exception for: KeeperErrorCode = NoNode for /payrisk-job/dailyScanMercReratingJob/leader/election/instance]

[DEBUG] [Curator-TreeCache-0] [2018-10-10 17:51:50.813] [] [RegExceptionHandler] [Elastic job: ignored exception for: KeeperErrorCode = NoNode for /payrisk-job/dailyScanMercReratingJob/leader/election/instance]

[DEBUG] [Curator-TreeCache-0] [2018-10-10 17:51:50.818] [] [LeaderService] [Elect a new leader now.]

[DEBUG] [Timer-0] [2018-10-10 17:51:51.261] [] [UpdateChecker] [Quartz version update check failed: Server returned HTTP response code: 403 for URL: http://www.terracotta.org/kit/reflector?kitID=quartz&pageID=update.properties&id=2130706433&os-name=Mac+OS+X&jvm-name=Java+HotSpot%28TM%29+64-Bit+Server+VM&jvm-version=1.8.0_112&platform=x86_64&tc-version=2.2.1&tc-product=Quartz&source=Quartz&uptime-secs=1&patch=UNKNOWN]

这行日志的输出代码位于:elastic-jobRegExceptionHandler.handleException()方法:




    /**

    * 处理异常.

    *

    * <p>处理掉中断和连接失效异常并继续抛注册中心.</p>

    *

    * @param cause 待处理异常.

    */

    public static void handleException(final Exception cause) {

        if (null == cause) {

            return;

        }

        if (isIgnoredException(cause) || null != cause.getCause() && isIgnoredException(cause.getCause())) {

            log.debug("Elastic job: ignored exception for: {}", cause.getMessage());

        } else if (cause instanceof InterruptedException) {

            Thread.currentThread().interrupt();

        } else {

            throw new RegException(cause);

        }

    }

这里 elastic job ignore了zk的操作异常,导致选主失败但是并没有做兼容处理,主线程陷入 wait()

NoNodeException

根据上面的查询,无法选主是因为curator框架抛出了NoNodeException,通过google很容找到解决这个问题的方法:统一curator的版本。

关于为什么会抛出这个问题,需要深入研究下。留待考察和研究。

最终解决方案

将项目中curator中的jar包版本全部统一为2.10.0问题解决。

NOTE:注意jar包是否完全升级了要去打包后的项目的lib下面观察下,看所有的jar是否全部都是2.10.0并且没有其他版本的jar。

总结

调试此类问题很耗时,经验:

  • 如果对框架比较熟悉,先尝试跟踪看问题代码发送地点;

  • 去框架的github官网的issue中查看是否有同类问题;

  • 如果还是无法解决,将日志级别调整为DEBUG再仔细观察下日志;

此外写框架的时候:

  • 最后不要吞掉异常,不管什么原因,如果要ignore Exception,最好是打印一个INFO或者WARNING级别的日志。

参考文章:

elastic-job源码分析(四)Leader选举_三寸花笺的博客-CSDN博客

elastic-job选主过程 - 腾讯云开发者社区-腾讯云
https://www.jianshu.com/p/950db264df22https://cloud.tencent.com/developer/article/2032932

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Elastic-job 启动阻塞“假死”的问题 的相关文章

  • Mockito 在调用参数数量可变的方法时使用参数匹配器

    我试图在对具有可变数量参数的方法的调用中使用参数匹配器 Java 中的东西 没有成功 我的代码如下 我还将列出我尝试用来完成此工作的所有行 import static org mockito Mockito public class Met
  • JPA 中的复合键

    我想创建一个具有自动生成的主键的实体 而且还有一个由其他两个字段组成的唯一复合键 我如何在 JPA 中执行此操作 我想这样做是因为主键应该用作另一个表中的外键 并且使其复合并不好 在下面的代码片段中 我需要命令和模型是唯一的 pk当然是主键
  • Java Runtime.getRuntime().freeMemory() 问题

    我搜索并看到了一些线程 但没有一个能够解决我遇到的具体问题 我正在尝试使用以下方式监视我的内存使用情况Runtime getRuntime freeMemory Runtime getRuntime maxMemory and Runtim
  • JVisualVM/JConsole 中的 System.gc() 与 GC 按钮

    我目前正在测试处理 XML 模式的概念验证原型 并围绕一个非常消耗内存的树自动机外部库 我已经获得了源代码 构建 我想绘制 真实峰值 堆 随着模式大小的增加 不同运行的内存消耗 使用的指标符合我的目的并且不会影响问题 或者至少是它的合理近似
  • “java.net.MalformedURLException:未找到协议”读取到 html 文件

    我收到一个错误 java net MalformedURLException Protocol not found 我想读取网络上的 HTML 文件 mainfest uses permission android name android
  • 是否有任何简单(且最新)的 Java 框架可用于在 Swing 应用程序中嵌入电影?

    我正在构建一个小型 Swing 应用程序 我想在其中嵌入一部电影 重要的是 这个应用程序是一个 WebStart 应用程序 并且该库应该能够打包在我启动的 jnlp 中 即 不依赖于本机库 我知道并尝试过 JMF 但我认为与其他框架相比 其
  • Integer.parseInt("0x1F60A") 以 NumberformatException 结束

    我尝试从数据库中获取长字符串内的表情符号代码 格式如下 0x1F60A 所以我可以访问代码 但它将是String 起初 我尝试通过执行以下操作来转换变量tv setText beforeEmo getEmijoByUnicode int e
  • 如何根据运行的 jar 的结果让我的 ant 任务通过或失败?

    我正在运行 CrossCheck 无浏览器 js 单元测试 作为 ant 脚本的一部分 如果 CrossCheck 测试失败 我希望 ant 报告失败 这是 build xml 中的相关部分
  • 当客户端关闭连接时,Spring StreamingResponseBody 请求线程未清理

    我在控制器中有一个端点 它返回一个StreamingResponseBody 用于向客户端发送文件 其代码大致如下 RestController RequestMapping value api public class Controlle
  • 使用 Guice 优化注册表

    你好 今天思考了一种优化 有一些疑问 语境 我正在使用 Guice 2 进行 Java 开发 在我的网络应用程序中 我有一个转换器注册表 可以即时转换为某种类型 转换器描述如下 public class StringToBoolean im
  • Java:如何为山区时间创建 TimeZone 对象?

    必须不禁用夏令时 嗯 在这个清单 http en wikipedia org wiki List of tz database time zones在 zoneinfo 时区名称中 有很多声称是 山地时间 找到最适合您想要的那个 然后使用它
  • 了解joda时间PeriodFormatter

    我以为我明白了 但显然我不明白 你能帮我通过这些单元测试吗 Test public void second assertEquals 00 00 01 OurDateTimeFormatter format 1000 Test public
  • HashMap 值需要不可变吗?

    我知道 HashMap 中的键需要是不可变的 或者至少确保它们的哈希码 hashCode 不会改变或与另一个具有不同状态的对象发生冲突 但是 HashMap中存储的值是否需要与上面相同 为什么或者为什么不 这个想法是能够改变值 例如在其上调
  • QuerySyntaxException:无法找到类

    我正在使用 hql 生成 JunctionManagementListDto 类的实际 Java 对象 但我最终在控制台上出现以下异常 org hibernate hql internal ast QuerySyntaxException
  • 返回 Java 8 中的通用函数接口

    我想写一种函数工厂 它应该是一个函数 以不同的策略作为参数调用一次 它应该返回一个函数 该函数根据参数选择其中一种策略 该参数将由谓词实现 嗯 最好看看condition3为了更好的理解 问题是 它没有编译 我认为因为编译器无法弄清楚函数式
  • “无法实例化活动”错误

    我的一个 Android 应用程序拥有大约 100 000 个用户 每周大约 10 次 我会通过 Google 的市场工具向我报告以下异常情况 java lang RuntimeException Unable to instantiate
  • org.apache.commons.net.io.CopyStreamException:复制时捕获 IOException

    我正在尝试使用以下方法中的代码将在我的服务器中创建的一些文件复制到 FTP 但奇怪的是我随机地低于错误 我无法弄清楚发生了什么 Exception org apache commons net io CopyStreamException
  • 泛型、数组和 ClassCastException

    我想这里一定发生了一些我不知道的微妙事情 考虑以下 public class Foo
  • 洪水填充优化:尝试使用队列

    我正在尝试创建一种填充方法 该方法采用用户指定的初始坐标 检查字符 然后根据需要更改它 这样做之后 它会检查相邻的方块并重复该过程 经过一番研究 我遇到了洪水填充算法并尝试了该算法 它可以工作 但无法满足我对 250 x 250 个字符的数
  • Spring表单ModelAttribute字段验证避免400 Bad Request错误

    我有一个ArticleFormModel包含正常发送的数据html form由 Spring 使用注入 ModelAttribute注释 即 RequestMapping value edit method RequestMethod PO

随机推荐

  • 自定义控件玩套路以及canvas StaticLayout的使用

    遇到自定义控件的时候很苦恼 不知道从哪里下手 鄙人也是新手 记录下开发的思路 我们有时候需要把某一个功能封装成控件 很简单 写好布局 将其inflate出来 必要的属性 就跟普通的activity一样定义即可 context直接调用getC
  • ShuffleNet V1、V2 & EfficientNet & 迁移学习

    一 ShuffleNet V1 ShuffleNet Unit中全是GConv和DWConv 在左侧的网络结构中 对于输入特征矩阵 有串行的GConv1和GConv2 对于普通的组卷积的计算 只针对该组内的channel的信息进行计算 组卷
  • HDU - 1827 Summer Holiday(强连通分量+贪心)

    题目大意 To see a World in a Grain of Sand And a Heaven in a Wild Flower Hold Infinity in the palm of your hand And Eternity
  • 分布式限流之 - Nginx层限流

    写在前面的话 高并发的三驾马车 缓存 降级 限流 这里仅仅说限流 常用的限流算法有 计数器算法 固定窗口算法 滑动窗口算法 漏桶算法 令牌桶算法 每种算法的特点和优缺点这里不展开 比较适用的限流算法基本都会选择令牌桶 并且这里基于Sprin
  • 《Perl语言入门》读书笔记(五)输入与输出

    1 读取标准输入 使用
  • Android Data Binding

    请注明链接 https blog csdn net feather wch article details 79789597 Data Binding 1 DataBinding引入 android compileSdkVersion 23
  • 自动备份脚本linux下,Linux七牛云自动备份脚本

    1 下载七牛云自动备份脚本 目录如下 image png 进入此目录 执行命令 python setup py install 需要python2 7以上 安装完毕 目录如下 image png 2 编写配置文件 备份名称 用于标记 BAC
  • 基于Qt实现分块下载网络文件

    实际开发经常遇到下载网络文件的需求 对于一些比较大的网络文件 如果通过网络接口一次性读取数据后再写入文件 会占用较大的内存 比较好的做法是 先通过QNetworkRequest ContentLengthHeader获取文件的大小 然后设定
  • LED摩托车灯升降压恒流芯片OC4000电路原理图

    LED摩托车灯升降压恒流芯片OC4000是一种可以将不稳定的直流电压转化为稳定的直流电压的电子元件 特别适合用于需要大电流 高电压的LED灯具中 它是一种高精度 高效率的降压型LED恒流驱动控制芯片 可以将电压降至适合LED灯珠的安全范围
  • 综合交易平台API技术开发指南

    综合交易平台API技术开发指南 草稿 第一章 CTP 产品特性 2 第二章 CTP API 技术基础 4 第三章 CTP API 证券交易
  • APP自动化-- 03 adb简介

    文章目录 1 ADB简介 2 adb命令 3 参考资料 4 monkey 1 ADB简介 adb组成 adb adb exe 运行于PC端 包括Linux Windows Mac OS等系统之中 通常是x86架构上 下文中 ADB指整个模块
  • js之匿名函数详解

    1 函数的声明与函数表达式区别 1 1 函数的声明 如下方法 add 就是函数声明的代码结构 function add x y alert x y add 1 2 弹窗显示 3 关于函数声明 它最重要的一个特征就是函数声明提升 意思是执行代
  • 【项目总结】基于SSM+SpringBoot+Redis的个人博客系统项目总结

    文章目录 项目介绍 开发背景 数据库设计 主要使用到的技术点 前端 后端 自定义统一返回对象 自定义拦截器 加盐加密操作 分页功能 session持久化 自定义头像的存储和获取 项目编写过程中遇到的困难点 困难点一 小 困难点二 小 困难点
  • javascript取Date时间的前一天和后一天

    在页面里直接用js Date curDate new Date var preDate new Date curDate getTime 24 60 60 1000 前一天var nextDate new Date curDate getT
  • Python 入门

    python输出 python输出hello world print hello world 变量 print 12 34 print 12 34 2 print 12 34 2 3 a 12 34 b a 2 c b 3 print a
  • 网络安全期末整理

    什么网络安全 网络安全是指网络系统的硬件 软件及其系统中的数据受到保护 不因偶然的或者恶意的原因而遭到破坏 更改或泄露 系统连续 可靠 正常地运行 网络服务不中断 网络安全的特征 保密性 完整性 可用性 可控性 面对威胁网络安全的安全措施
  • 关于深度图像

    深度图像 深度图像 depth image 也被称为距离影像 range image 是指将从图像采集器到场景中各点的距离 深度 作为像素值的图像 它直接反映了景物可见表面的几何形状 深度图像经过坐标转换可以计算为点云数据 有规则及必要信息
  • 泊松曲面重建(基于PCL)

    Possion重建是Kazhdan等2006年提出的网格重建方法 Possion重建的输入是点云及其法向量 输出是三维网格 表面重建流程 1 构建八叉树 采用的是自适应的空间网格划分的方法 根据点云的密度调整网格的深度 根据采样点集的位置定
  • 自动控制原理知识点梳理——8.非线性控制系统分析

    1 非线性控制系统 线性控制系统 由线性元件组成 输入输出具有叠加性和齐次性性质 非线性控制系统 系统中有非线性元件 输入输出间不具有叠加性和齐次性性质 非线性系统的特征 非线性系统的特征 稳定性问题 稳定性问题 频率响应畸变 常见的非线性
  • Elastic-job 启动阻塞“假死”的问题

    问题记录 最近项目引入Elastic Job实现定时任务的分布式调度 引入的版本2 1 5 加入相关的job配置后启动项目 主线程假死 不进行后续逻辑处理和日志输出 输出的日志如下 INFO RMI TCP Connection 2 127