91 xxl-job executor 还存在 并且 job 正在执行, 但是 job 被标记为 “任务结果丢失,标记失败“

前言

最近出现了一个这样的问题 

我们生产环境中的一个 xxl-job 任务, 很大一部分执行记录被标记为 "任务结果丢失,标记失败",  几乎是 98% 吧 

然后 调试的时候 存在几个令人疑惑的地方  

1. 通过 xxl-job 点击查看任务的执行记录的日志, 日志为空, 另外根据配置的日志的路径来查找, 查找不到给定的 job 的日志文件

2. jstack 查看该程序, 可以看到该 job 是正在执行中, 为什么找不到日志的问题? 根据 jobThread 的代码来看, 只要进入了业务代码, 那么至少应该会有一些 框架打印的日志信息到日志文件才对 

3. 查询该 job 产生的业务数据, 我们可以查询到 "当前时间附近" 的一些相关业务数据[这一点是可以被 jstack 查询可以找到 job 正在执行解释, 业务代码中使用的是 now(), you业务代码执行, 就会有 "当前时间附近" 的业务数据]

4. 根据 monitorThread 的业务代码来看, 扫描的是正在运行的 已经超过了十分钟 并且找不到关联的 executor 的任务执行记录, 而我们的这个 job 的 executor 还存在, 怎么就被标记为 "任务结果丢失,标记失败" 了? 

 

然后 后面当我查看到了 当前 job 对应的 jobThread 中的 triggerQueue 中还有 76 个任务的时候, 我突然明白了 

 

本文的代码, 调试图片基于 jdk8 + xxl-job 2.3.0

 

 

测试用例

新建测试用例如下 StackingJob 

可以看到的是这个 stackingJobHandler 是以 三次任务执行为一个循环  

第一次执行 sleep 3s, 第二次执行 sleep 5s, 第三次执行 sleep 10s, 然后 循环  

/**
 * StackingJob
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2021-10-02 16:20
 */
public class StackingJob {

    int counter = 0;

    @XxlJob("stackingJobHandler")
    public void stackingJobHandler() throws Exception {
        if (counter % 3 == 0) {
            Thread.sleep(3000);
        } else if (counter % 3 == 1) {
            Thread.sleep(5000);
        } else {
            Thread.sleep(10_000);
        }

        counter++;
    }

}

 

然后为了测试, 调整一下 monitorThread 的过期时间, 更新为 7s 

结合上面的 stackingJobHandler, 以及 jobThread 顺序执行 job 的情况, 以及 monitorThread 查询丢失的任务的判断标准是以 任务 的期望执行时间来判断的[trigger_time], 我们大致可以判断的是

第一次任务执行 成功, 第二次任务执行 成功, 第三次任务执行 失败 "任务结果丢失,标记失败"

第四次任务执行 失败 "任务结果丢失,标记失败", 第五次任务执行 失败 "任务结果丢失,标记失败"

以及 后面的都应该会失败  

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

然后启动 xxl-admin, 创建一个 jobGroup localExecutor, 创建一个  调度任务 关联在 localExecutor 上面 

然后 来尝试复现这个问题, 果然 和我们期望的差不多, 启动任务 stackingJobHandler, 执行日志如下 

我们注意观察一下 这个 handle_time 基本上都是 trigger_time + 7s, 这是由 monitorThread 来处理标记的 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

 

问题的调试 

首先我们要观察的是 moniitorThread 中是怎么将我们这里还在执行的 executor 对应的任务找到的 

下面第一张图片是 monitorThread 的核心的业务逻辑, 主要是找到 丢失的任务, 并且更新处理状态 

下面第二张图片是 具体的查找 丢失的任务 的方式, 查询的是已经正常触发的, 还未被 admin 正常/异常 收尾处理的, 期望触发时间在 7s 之前的, 并且 job 对应的 executor 关联不到的 job 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

 

我们来查询一下 job_log 和 job_registry 的相关数据, 我们查看一下对应的 executor_address 和 registry_value 

可以看到这里 job_log 中的 executor_address 为 http://localhost:9998 , 但是 job_registry 中的 registry_value 为 http://192.168.43.14:9998/ 

根据 "=" 关联 匹配, 没有匹配上, 所以 xxl-admin 将该 job 视为了 executor 丢失的情况 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

job_log 中的 executor_address 来自于 job 对应的 jobGroup 中的 executor_address, 也就是我们上面手动录入的这个 localExecutor 里面的 executor 暴露的 tcp 地址信息 

然后 job_registry 的这个 registry_value 是来自于, executor 这边向 admin 注册的时候, 传递过来的地址信息, 优先取的是 xxl.job.executor.address 的配置信息, 以 IpUtil.getIpPort 来进行兜底, 也就是我们上面得到的 http://192.168.43.14:9998/ 

所以说我们配置 xxl.job.executor.address 和 jobGroup 的 address 的时候一定要注意, 如果是同一个逻辑 executor 一定要配置成一样, 即使是一个是  http://192.168.43.14:9998/ , 一个是    http://192.168.43.14:9998 这样也是不行的 

如果是 xxl.job.executor.address 没有配置, 那么配置 jobGroup 的 address 的时候要按照它的默认规则配置, 务必需要一模一样, 因为关联是通过 字符串 "="匹配 来进行关联的 

 

 

假设我们 jobGroup 的 address 和 jobRegistry 的 registryValue 配置一致 

那么这里的 job 执行不会出现这里的 "任务结果丢失,标记失败" 的情况了 

但是 依然会出现任务 拥塞 的情况, 那么我们就需要合理调整任务的用时 或者 说调度的周期了  

我们观察一下 这一组的任务执行情况, 都是 执行成功, 并且 在任务拥塞了之后, 都是任务的执行间隔都是 3, 5, 10, 因为任务已经拥塞在队列里面了, jobThread 一个任务执行完成之后, 会立即取下一个任务来执行 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

 

我们再来看一下任务 拥塞 的情况 

可以看到的是 stackingJobHandler 的 jobThread 中已经拥塞了 三个任务了, 分别是 94, 95, 96, 可以根据这个 id 去 xxl_job_log 中查询  

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

 

日志中的细节

我们回到最问题出现的开始的情况, 我们来查看一下 这一部分 job 执行记录对应的日志信息 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

抽样前面的几个来看一下 

可以看到的是 这些日志中任务开始之前, 任务结束之后, xxljob 都打印了相关的日志, 我们可以从日志中可以大致的判断出任务的执行时间, 这里的执行时间规则大概是 3, 5, 10, 3, 我们从前面的结果中可以看到的是 34, 35 应该是正常执行成功 

36, 37 应该是被标记为了 "任务结果丢失,标记失败" 

但是我们看具体的日志中 callback 的情况, 我去 居然还是 callback finished ? 按照 jobThread 这边的处理, 我们正常理解期望应该是输出一些 异常信息才对 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16 watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

我们在抽样一下 后面的几个执行记录 

可以看到任务执行的时间规则大概也还是 3, 5, 10, 3 

并且 callback 这里都是 finished, 呵呵 我们来看一下 这里的 callback 的具体的情况 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16 watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

 

我们来看一下 xxl-admin 这边的处理, 实际的 callback 的处理是放到了异步任务里面  

然后这里不管处理情况怎么样, 返回给 executor 的都是 SUCCESS, 所以 executor 这边拿到的总是 callback finished 

然后我们再来看一下 我们这里这种被标记为了 "任务结果丢失,标记失败" 的任务, 在 callback 里面实际又是怎么处理的? watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

因为 jobLog 的 handleCode 已经不为0了[表示已经被 admin 正常/异常 处理过了], 这里标记为重复的 callback 的请求, 直接丢弃了请求 

具体的错误信息, 会在上面的 callback 的外层会有日志体现 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd6aOOOQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 

所以 如果我们想要感知到这部分 拥塞 的任务的执行情况, 我们一般来说有几种方式 

1. 从 xxl-admin 的日志中查找这个 jobId 的相关的信息, 可以查询到具体的 任务调用 callback 的一个大致的时间

2. jobHandler 中基于 XxlJobContext 输出 job 的相关信息, 这样就可以通过 日志来查询 

3. 等待能够找到这个 jobLog 对应的日志文件, 也就说明这个任务 开始执行了 

 

 

 回溯一下问题 

1. 通过 xxl-job 点击查看任务的执行记录的日志, 日志为空, 另外根据配置的日志的路径来查找, 查找不到给定的 job 的日志文件

        这是因为这部分 jobLog 是期望调用了, 但是由于任务拥塞 还没有调用, 因此还没有执行, 因此找不到对应的 jobLog 的日志文件 

2. jstack 查看该程序, 可以看到该 job 是正在执行中, 为什么找不到日志的问题? 根据 jobThread 的代码来看, 只要进入了业务代码, 那么至少应该会有一些 框架打印的日志信息到日志文件才对 

        我们 jstack 查看到的任务的执行, 是所有的拥塞的任务再之前的一个 jobLog, 但是后面还有一系列拥塞的 任务 还未执行

3. 查询该 job 产生的业务数据, 我们可以查询到 "当前时间附近" 的一些相关业务数据[这一点是可以被 jstack 查询可以找到 job 正在执行解释, 业务代码中使用的是 now(), you业务代码执行, 就会有 "当前时间附近" 的业务数据]

        这是由于任务的业务代码决定的, jobHandler 中查询的是 now 附近的业务数据, 那么当然是什么时候执行, 获取的就是那个时候的时间 

4. 根据 monitorThread 的业务代码来看, 扫描的是正在运行的 已经超过了十分钟 并且找不到关联的 executor 的任务执行记录, 而我们的这个 job 的 executor 还存在, 怎么就被标记为 "任务结果丢失,标记失败" 了? 

        jobLog 对应的 jobInfo 对应的 jobGroup 的 executor_address 和 executor 启动的时候向 admin 注册的 jobRegistry 的 registry_value 不一致, 导致 xxl-job 认为这个 job 对应的 executor 已经不在了, 这两者需要完全的字符串匹配  

 

 

完 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/379723.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

异步编程(JS)

前言 想要学习Promise&#xff0c;我们首先要了解异步编程、回调函数、回调地狱三方面知识&#xff1a; 异步编程 异步编程技术使你的程序可以在执行一个可能长期运行的任务的同时继续对其他事件做出反应而不必等待任务完成。 与此同时&#xff0c;你的程序也将在任务完成后显示…

《剑指 Offer》专项突破版 - 面试题 37 : 小行星碰撞(C++ 实现)

题目链接&#xff1a;LCR 037. 行星碰撞 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 输入一个表示小行星的数组&#xff0c;数组中每个数字的绝对值表示小行星的大小&#xff0c;数字的正负号表示小行星运动的方向&#xff0c;正号表示向右飞行&#xff0c;负…

【开源】SpringBoot框架开发医院门诊预约挂号系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 功能性需求2.1.1 数据中心模块2.1.2 科室医生档案模块2.1.3 预约挂号模块2.1.4 医院时政模块 2.2 可行性分析2.2.1 可靠性2.2.2 易用性2.2.3 维护性 三、数据库设计3.1 用户表3.2 科室档案表3.3 医生档案表3.4 医生放号…

【开源项目阅读】Java爬虫抓取豆瓣图书信息

原项目链接 Java爬虫抓取豆瓣图书信息 本地运行 运行过程 另建项目&#xff0c;把四个源代码文件拷贝到自己的包下面 在代码爆红处按ALTENTER自动导入maven依赖 直接运行Main.main方法&#xff0c;启动项目 运行结果 在本地磁盘上生成三个xml文件 其中的内容即位爬取…

Elasticsearch:通过 ingest pipeline 对大型文档进行分块

在我之前的文章 “Elasticsearch&#xff1a;使用 LangChain 文档拆分器进行文档分块” 中&#xff0c;我详述了如何通过 LangChain 对大的文档进行分块。那个分块的动作是通过 LangChain 在 Python 中进行实现的。对于使用版权的开发者来说&#xff0c;我们实际上是可以通过 i…

【工作学习 day04】 9. uniapp 页面和组件的生命周期

问题描述 uniapp常用的有&#xff1a;页面和组件&#xff0c;并且页面和组件各自有各自的生命周期函数&#xff0c;那么在页面/组件请求数据时&#xff0c;是用created呢&#xff0c;还是用onLoad呢&#xff1f; 先说结论: 组件使用组件的生命周期&#xff0c;页面使用页面的…

【Docker】02 镜像管理

文章目录 一、Images镜像二、管理操作2.1 搜索镜像2.1.1 命令行搜索2.1.2 页面搜索2.1.3 搜索条件 2.2 下载镜像2.3 查看本地镜像2.3.1 docker images2.3.2 --help2.3.3 repository name2.3.4 --filter2.3.5 -q2.3.6 --format 2.4 给镜像打标签2.5 推送镜像2.6 删除镜像2.7 导出…

移动应用开发Android 创建第一个Android项目

文章目录 一、创建第一个Android项目1.1 准备好Android Studio1.2 运行程序1.3 程序结构是什么app下的结构res - 子目录&#xff08;所有图片、布局、字AndroidManifest.xml 有四大组件&#xff0c;程序添加权限声明 Project下的结构 二、开发android时&#xff0c;部分库下载异…

svg基础(六)滤镜-图像,光照效果(漫反射,镜面反射),组合

1 feImage&#xff1a;图像滤镜 feImage 滤镜从外部来源取得图像数据&#xff0c;并提供像素数据作为输出&#xff08;意味着如果外部来源是一个 SVG 图像&#xff0c;这个图像将被栅格化。&#xff09; 1.1 用法: <feImage x"" y"" width"&quo…

基于鲲鹏服务NodeJs安装

准备工作 查看当前环境 uname -a查看鲲鹏云CPU架构 cat /proc/cpuinfo# 查看CPU architecture项&#xff0c;8表示v8&#xff0c;7表示v7下载Node.js NodeJs 选择 Linux Binaries (ARM) ARMv8 wget -c https://nodejs.org/dist/v12.18.3/node-v12.18.3-linux-arm64.tar.xz…

Android用setRectToRect实现Bitmap基于Matrix矩阵scale缩放RectF动画,Kotlin(一)

Android用setRectToRect实现Bitmap基于Matrix矩阵scale缩放RectF动画&#xff0c;Kotlin&#xff08;一&#xff09; 基于Matrix&#xff0c;控制Bitmap的setRectToRect的目标RectF的宽高。从很小的宽高开始&#xff0c;不断迭代增加setRectToRect的目标RectF的宽高&#xff0c…

选调生怎么搜题答案?分享四个可以搜答案的软件 #其他#知识分享#职场发展

大学生活是一个充满挑战和机遇的阶段&#xff0c;在这个阶段&#xff0c;我们需要不断提升自己的学习能力和技巧。而寻找适合自己的学习工具也成为了我们必须面对的任务。幸运的是&#xff0c;现在有许多日常学习工具可以帮助我们更好地组织学习、提高效率。今天&#xff0c;我…

Kubernetes基础(十四)-Cluster Autoscaler

Kubernetes 给出的解决方案就是&#xff1a;自动伸缩&#xff08;auto-scaling&#xff09;&#xff0c;通过自动伸缩组件之间的配合&#xff0c;可以 7*24 小时的监控着k8s集群&#xff0c;动态变化负载&#xff0c;以适应用户需求。 1 自动伸缩组件 1.1 自动伸缩类型 1.1.…

斯巴鲁Subaru EDI需求分析

斯巴鲁Subaru是日本运输集团斯巴鲁公司&#xff08;前身为富士重工&#xff09;的汽车制造部门&#xff0c;以性能而闻名&#xff0c;曾赢得 3 次世界拉力锦标赛和 10 次澳大利亚拉力锦标赛。 斯巴鲁Subaru EDI 需求分析 企业与斯巴鲁Subaru建立EDI连接&#xff0c;首先需要确…

【Linux】进程学习(二):进程状态

目录 1.进程状态1.1 阻塞1.2 挂起 2. 进程状态2.1 运行状态-R进一步理解运行状态 2.2 睡眠状态-S2.3 休眠状态-D2.4 暂停状态-T2.5 僵尸状态-Z僵尸进程的危害 2.6 死亡状态-X2.7 孤儿进程 1.进程状态 1.1 阻塞 阻塞&#xff1a;进程因为等待某种条件就绪&#xff0c;而导致的…

备战蓝桥杯---搜索(完结篇)

再看一道不完全是搜索的题&#xff1a; 解法1&#xff1a;贪心并查集&#xff1a; 把冲突事件从大到小排&#xff0c;判断是否两个在同一集合&#xff0c;在的话就返回&#xff0c;不在的话就合并。 下面是AC代码&#xff1a; #include<bits/stdc.h> using namespace …

飞书上传图片

飞书上传图片 1. 概述1.1 访问凭证2. 上传图片获取image_key1. 概述 飞书开发文档上传图片: https://open.feishu.cn/document/server-docs/im-v1/image/create 上传图片接口,支持上传 JPEG、PNG、WEBP、GIF、TIFF、BMP、ICO格式图片。 在请求头上需要获取token(访问凭证) …

Lua: 一门轻量级、高效的脚本语言

Lua: 一门轻量级、高效的脚本语言 在当今软件开发的领域中&#xff0c;寻找一门既灵活又高效的脚本语言&#xff0c;一直是开发者们追求的目标。Lua作为一门小巧、高效、可嵌入的脚本语言&#xff0c;已经成为了众多开发者的首选之一。无论是游戏开发、嵌入式系统、Web 开发还是…

左叶子之和

给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 24示例 2: 输入: root [1] 输出: 0提示: …

Office2013下载安装教程,保姆级教程,附安装包和工具

前言 Microsoft Office是由Microsoft(微软)公司开发的一套基于 Windows 操作系统的办公软件套装。常用组件有 Word、Excel、PowerPoint、Access、Outlook等。 准备工作 1、Win7 及以上系统 2、提前准备好 Office 2013 安装包 安装步骤 1.鼠标右击【Office2013(64bit)】压缩…
最新文章