ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DolphinScheduler中ChunJun任务保存失败?全套排查定位与修复指南

DolphinScheduler中ChunJun任务保存失败?全套排查定位与修复指南 如果你在 DolphinScheduler 里维护数据同步任务有一个场景不知道你遇没遇到过工作流已经跑得好好的某天想往里面加一个 ChunJun 节点配置也填了参数也写了结果点“保存”就是提示失败。最坑的是前端提示根本没说清楚哪里失败后端日志翻半天也不一定有堆栈整个排查过程像是在猜谜。我第一次碰这个坑的时候差点觉得是系统环境的问题后来才发现问题远不是重启一下服务就能解决的。这篇就把我这次“DolphinScheduler 工作流中 ChunJun 任务保存失败”的完整排查链路和修复方案写下来给同样被这个问题卡住的人一个可直接复现的思路。1. 问题初现DS 工作流里 ChunJun 节点保存失败的三种形态先说现象。不同版本的 DolphinScheduler 搭配不同接入方式ChunJun 任务保存失败的表现并不完全一样。我自己在实际排查和跟朋友交流的过程中发现至少可以归纳成三种典型形态很多人遇到的其实是其中某一种但排查方向完全不一样。形态一前端保存按钮直接报错接口返回非 0 状态码。最直观的一种。你在工作流定义编辑页拖入一个 CHUNJUN 任务节点填完 JSON 参数点击确定或保存前端弹出错误提示“保存失败”或“接口错误”。打开浏览器开发者工具看 Network 面板能看到某个请求返回了非预期的状态码或错误信息。这种最容易定位因为至少报错链路是通的问题大概率出在后端接口处理逻辑上。形态二保存提示成功但刷新后任务配置丢失或恢复原样。这种最坑。前端给你弹了一个“保存成功”你也看到任务节点在画布上结果你刷新页面或者重新打开工作流定义发现刚才配置的 ChunJun 节点参数全部变成默认值甚至整个节点直接消失。这种我刚开始排查时根本没想到是“保存失败”因为系统没报错。后来才发现本质上还是后端保存任务定义时出现了异常回滚但前端没有正确捕获到错误做了假成功提示。形态三保存时一切正常任务提交后立即运行失败。严格来说这种不算“保存失败”但很多人会误以为是保存环节出了问题因为运营同学看到的直观反馈就是“我保存了一个跑不起来的任务”。真正原因通常是 ChunJun 插件在 worker 节点没有被加载或者参数结构不满足 ChunJun 的启动要求。这种如果你按照“保存失败”去排查方向会跑偏得先区分清楚。把现象分清楚之后先别急着改配置因为你要先找到一个关键问题的答案——保存失败到底失败在哪一层。是前端 JS 校验拦截了还是后端 API 在处理时抛异常了还是底层 MySQL 写入时报错了。这三层对应的处置方式完全不同。2. 定位方法从前端报错到 API 响应再到服务端日志我在这一步的排查顺序是固定的先浏览器 Network 面板再 api-server 日志最后数据库落库情况。如果不按这个顺序来很容易在配置层面浪费大量时间。2.1 先看 Network 面板确认具体是哪个接口在报错打开浏览器开发者工具F12切到 Network 面板在页面上执行一次任务保存操作。重点看两个请求/dolphinscheduler/projects/{projectCode}/process-definition保存工作流定义/dolphinscheduler/task-definition/save保存单个任务定义不同版本路径可能略有差异但大同小异。点击请求后看 Response 响应体。如果响应里的code字段不是 0msg字段通常会给出一些提示。比如我在一次复现中看到的响应是这样{ code: 500, msg: Save task definition failed, TaskType: CHUNJUN is not supported, data: null }这条提示已经把核心问题暴露了一半后端在保存任务定义时发现 CHUNJUN 这个任务类型没有被支持。但“不支持”是哪种不支持是插件没装还是插件没加载还是参数校验不认这里还需要往服务端线程栈继续挖。如果响应的code是 0但刷新后发现配置丢失那问题就更多落在数据库事务回滚层面需要往底层看。2.2 翻 api-server 日志看异常堆栈的 Caused byDolphinScheduler 的 api-server 日志路径通常在解压目录下的logs/dolphinscheduler-api-{hostname}.log。如果你们是容器化部署一般对应 api-server 这个 Pod 的 stdout。用 grep 过滤刚才操作时间段的日志关键词可以直接搜ERROR、Exception或TaskType。我当时搜到的关键堆栈片段类似这样Caused by: java.lang.IllegalStateException: TaskPlugin: CHUNJUN is not registered at org.apache.dolphinscheduler.dao.entity.TaskDefinition.checkTaskType(TaskDefinition.java:...)到这里基本可以断定是任务插件注册问题。继续深挖就要谈到 DolphinScheduler 的任务插件机制了这块我会在下一章展开。现在先记住一个结论很多保存失败不是参数写错而是后端服务根本没有这个任务的处理器。也有另一种常见日志报的是SQLException或DataIntegrityViolationException比如Caused by: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Data too long for column task_params at row 1这种情况就属于任务参数的 JSON 太长或者字段类型不匹配导致 MySQL 写不进去从而触发保存事务回滚。这个方向要单独处理。2.3 用 curl 绕过前端确认是否是 UI 侧问题有些场景下前端会有自己的字段校验跟后端逻辑不一致会导致你在 UI 上怎么改都报错但接口本身是通的。为了区分这一点我的做法是直接用 curl 模拟后端保存任务定义的请求。先用账号密码调登录接口拿到 sessionIdcurl -c cookies.txt -b cookies.txt -H Content-Type: application/json \ -X POST http://{api-server-ip}:12345/dolphinscheduler/login \ -d {userName:admin,userPassword:dolphinscheduler123}然后构造一次保存任务定义的请求。这里以一个最简单的 SHELL 任务为例先用非 ChunJun 任务测试后端保存链路是否正常curl -b cookies.txt -H Content-Type: application/json \ -X POST http://{api-server-ip}:12345/dolphinscheduler/task-definition/save \ -d { name: test-shell, taskType: SHELL, taskParams: { localParams: [], resourceList: [], rawScript: echo hello } }如果普通任务能保存成功而 ChunJun 任务保存失败那问题就集中在 ChunJun 插件本身不用怀疑 UI 或公共链路。这个操作本质上是在帮助你收窄问题范围尤其是多人协作的环境里别人改过配置你根本不知道先验证公共链路是最高效的。3. 根因拆解SPI 插件未加载与 task_params 数据结构的双重坑排除完公共链路问题后我这次遇到的根因可以拆成两个层面一个是 DolphinScheduler 任务插件体系层面的“CHUNJUN 未注册”另一个是任务参数 JSON 落库层面的“结构不被识别”。两者可能同时存在也可能只触发其一但都需要理解原理不然换了台机器你还会踩同样的问题。3.1 DolphinScheduler 的任务插件机制决定了“能选不等于能用”DolphinScheduler 从 2.0 开始采用 SPI 机制管理任务插件。前端任务类型下拉框能显示 CHUNJUN不一定代表后端已经安装了对应的 TaskChannel 实现。前端这边只是有一个静态枚举列表把 DS 所有已知的任务类型都渲染了出来但如果你在 api-server 或者 worker 的插件目录里没有安装对应的dolphinscheduler-task-chunjun相关 jar 包后端的 TaskChannelRegistry 就找不到这个类型的注册通道。具体来说DS 的任务插件目录结构如下dolphinscheduler-bin/ ├── lib/ │ └── plugin/ │ └── task-plugin/ │ ├── dolphinscheduler-task-shell/ │ ├── dolphinscheduler-task-datax/ │ └── ...每次 api-server 启动时DS 会扫描task-plugin目录下的所有 jar通过 SPI 自动注册 TaskChannel。如果你没把 ChunJun 任务插件包放到这个目录或者放进去的版本和 DS 主版本不匹配就会出现日志里的TaskPlugin: CHUNJUN is not registered。这里有一个很反直觉的点很多网上的教程说把 chunjun 的 dist 包直接丢进lib/目录就可以了。这个做法在低版本里也许能跑通但在 DS 3.x 版本里非常危险因为 chunjun 自身携带了大量 Flink 相关依赖直接丢进公共lib/容易跟 DS 的 Flink 组件产生冲突轻则任务保存失败重则 api-server 启动直接崩掉。正确做法是把 ChunJun 的插件包放到独立的task-plugin目录里并且确认里面的 jar 不是 fat jar或至少不要包含与 DS 冲突的依赖。3.2 task_params 的存储结构不像你想的那么宽松另一个坑出现在任务参数的 JSON 结构上。DS 保存任务定义时会把任务的taskParams序列化成 JSON 字符串写入t_ds_task_definition表的task_params字段中。这个字段在 MySQL 中通常是TEXT类型长度上限 65535 字节如果 JSON 太长就会出现前面说的Data too long报错。但比长度问题更隐蔽的是 JSON 结构本身。ChunJun 任务节点在 DS 前端提交时taskParams中会包含类似这样的结构{ localParams: [], resourceList: [], chunjunConfig: { job: { content: [ { reader: { name: streamreader, parameter: {} }, writer: { name: streamwriter, parameter: {} } } ] } } }如果前端在序列化chunjunConfig时没有做正确的转义比如内部 JSON 字符串里包含了未转义的双引号或换行符后端在反序列化TaskParams时就会直接抛JsonProcessingException导致保存链路中断。我在实际运维中见过一个很典型的场景某个用户直接在任务参数里粘贴了一段从 ChunJun 官方文档复制的 JSON格式是美化后的缩进格式并且中间包含了注释行。DS 前端默认用 JSON.stringify 处理时会保留换行符但在某些浏览器或版本下换行符会被转义成\n存在 JSON 里而真正写库时如果字段转义没处理好保存就偶发失败。这个问题不固定复现所以排查起来格外难受。3.3 版本不一致导致的隐性失败还有一个容易被忽略的点DolphinScheduler 前端、api-server、worker 三部分的 ChunJun 插件配置必须对齐。举个例子api-server 上你装了dolphinscheduler-task-chunjun插件保存任务定义时后端能识别 CHUNJUN 类型接口返回成功。但 worker 节点上你没有安装这个插件或者安装的 ChunJun 版本和 api-server 不一致那么保存只是第一步等调度器真正把任务分发到 worker 上时worker 会因为找不到 ChunJun 的任务执行器直接失败。这类问题往往在上线后第一次运行时才暴露但排查时容易被人误解为“保存时就有问题”所以我把这个版本分支也放在根因章节一起讲。4. 完整修复方案从插件部署到参数校验的落地步骤理解根因之后修复其实并不复杂但每一步都要做对否则会反复扑空。下面是我确认可行的完整步骤适用于 DolphinScheduler 3.1.x/3.2.x 常见版本其他版本思路相同路径和参数名微调即可。4.1 确认当前 DS 版本和 ChunJun 插件包版本这一步必须最先做。打开 DS 的bin/dolphinscheduler-daemon.sh或者查看lib目录下的主 jar 文件名确认主版本。同时确认 ChunJun 的发行包版本ChunJun 的版本号通常形如chunjun-dist-1.12-SNAPSHOT或chunjun-dist-1.16-SNAPSHOT取决于它内嵌的 Flink 版本。两者的兼容关系非常重要。DS 3.x ChunJun 1.12/1.16 是常见组合但如果你用的是 DS 2.x 老版本就不能直接拿新版的 ChunJun 插件包塞进去。建议参考 ChunJun 官方文档中关于 DolphinScheduler 部署章节的版本矩阵说明。没有版本矩阵时就用最有把握的组合DS 3.1.8 ChunJun 1.12这个组合我自己测过很多次相对稳定。4.2 正确放置插件 jar 包先把之前错误塞进lib/目录的 chunjun 相关 fat jar 移出。然后确认lib/plugin/task-plugin/目录下是否已经存在 ChunJun 插件包如果没有从 ChunJun 发布包中找出对应的 DS 插件模块 jar。例如 ChunJun 解压目录下会有一个plugins目录里面可能包含dolphinscheduler-plugin之类的模块。把该模块的 jar 复制到所有节点的lib/plugin/task-plugin/下mkdir -p dolphinscheduler-bin/lib/plugin/task-plugin/dolphinscheduler-task-chunjun cp chunjun-dist/plugins/dolphinscheduler/dolphinscheduler-task-chunjun.jar \ dolphinscheduler-bin/lib/plugin/task-plugin/dolphinscheduler-task-chunjun/如果你能拿到源码自己编译也可以直接找到chunjun工程下的chunjun-dolphinscheduler-plugin模块运行mvn clean package -DskipTests把 target 目录下的 jar 放到对应位置。这里务必注意不要整个 chunjun dist 目录都复制过去只复制 DS 插件相关的一个或几个 jar其他依赖 DS 会按需去对应目录找。4.3 清理和调整环境变量避免依赖冲突DS 在启动 api-server 和 worker 时会读取bin/env/dolphinscheduler_env.sh。如果之前有人为 ChunJun 配置过HADOOP_CLASSPATH、FLINK_HOME等变量检查一下是否有指向错误路径的配置。常见的问题是CHUNJUN_HOME指到了一个包含多版本 jar 的目录导致类加载冲突。我建议在环境变量里显式指定 ChunJun 的 dist 目录并且让该目录下只保留你确认兼容的版本export CHUNJUN_HOME/opt/chunjun-dist export PATH$CHUNJUN_HOME/bin:$PATH如果是容器化部署比如 K8s需要确认 api-server 和 worker 的镜像里都执行了同样的配置不能只改 api-server 而 worker 还是旧镜像。4.4 重启服务并确认 SPI 注册成功配置文件改完后重启 DolphinScheduler 的 api-server 和 worker 服务。DS 3.x 版本用bin/dolphinscheduler-daemon.sh start api-server或直接使用bin/dolphinscheduler-daemon.sh start cluster一键重启所有服务。重启完成后第一时间看 api-server 启动日志搜索TaskPlugin或Chunjungrep -i chunjun logs/dolphinscheduler-api-*.log如果能看到类似下面这样的日志说明插件已经成功注册Load TaskPlugin: CHUNJUN successfully如果日志里没有这条说明 jar 位置不对或版本不兼容要回过去检查 4.2 的路径。4.5 用 SQL 验证保存是否真正落库修复后在前端再保存一次任务。保存成功后到数据库里查一下SELECT code, name, task_type, task_params, update_time FROM t_ds_task_definition WHERE name 你的任务名 ORDER BY update_time DESC LIMIT 1;重点看task_type是不是CHUNJUNtask_params里的 JSON 是否完整尤其是chunjunConfig字段是否存在。这个查询能帮你确认保存链路是真正成功了而不是前端“假成功”。除了这个表还要顺带查一下工作流定义表里的引用关系SELECT process_definition_code, task_definition_code FROM t_ds_process_task_relation WHERE task_definition_code 刚才查到的task_code;如果任务定义表有数据但关系表里没有对应记录说明工作流保存时事务回滚了问题还在保存流程里不是单纯任务定义落库的问题。4.6 前端参数 JSON 的规范化建议再补一个实操细节不管你的修复走到了哪一步都建议在前端把 ChunJun 的配置参数统一整理成标准 JSON 格式不要带注释不要带多余换行更不要在字符串值内部塞未转义的双引号。一个相对可靠的做法是在本地先用 Python 的json.loads验证一遍你打算粘贴的配置import json chunjun_json { job: { content: [ { reader: { name: streamreader, parameter: { column: [ {name: id, type: int}, {name: name, type: string} ] } }, writer: { name: streamwriter, parameter: { print: true } } } ] } } data json.loads(chunjun_json) print(json.dumps(data, ensure_asciiFalse))能正常打印出紧凑的 JSON 字符串再粘贴到 DS 前端能省掉很多转义问题。这个习惯我一直保持到现在尤其是在多人共用测试环境时能避免很多人为干扰。5. 复盘与扩展类似失败场景的排查思路这次问题解决之后我重新整理了一遍 DolphinScheduler 任务保存失败的排查路径发现这套思路不仅能用于 ChunJun对 DataX、Seatunnel、Flink 等自定义任务插件也一样适用。梳理成一张排查路径表方便直接在碰到问题时对照。故障现象优先检查点常见根因快速处置前端保存即报错code500Network 响应 msg后端 SPI 未注册该任务类型检查 api-server 插件目录前端保存成功刷新后配置丢失t_ds_task_definition 表事务回滚任务定义未落库看 api-server 日志中的 SQL 异常保存成功任务运行时失败worker 节点日志worker 节点缺插件或版本不一致检查 worker 插件目录保存时偶发失败重启后恢复内存/线程堆栈依赖 jar 冲突导致 api-server 不稳定清理 chunjun fat jar独立插件目录task_params 写入超长SQL 抛 Data too longJSON 配置过长缩短配置或升级字段为 MEDIUMTEXT再补充两个我在实际运维中反复踩到的分支场景它们也属于“保存失败”的大范畴但根因和 ChunJun 不完全一样。分支一DolphinScheduler 工作流里多个任务同时保存时报错。保存工作流定义时DS 会把整个 DAG 上的所有任务定义一次性入事务。如果你有多个 ChunJun 节点其中一个节点的taskParams不合法整个事务都会回滚最终表现为所有节点全部保存失败。所以排查时不能只看第一个节点要逐个检查尤其是配置比较长的节点。分支二前端和 API 版本不一致。有一种情况是你用了旧版浏览器缓存的前端资源而后端已经升级到新版本。旧版前端在序列化某些字段时可能与新后端不兼容导致后端解析失败。遇到这种问题强制刷新浏览器缓存或清空 local 配置重新登录有时“莫名其妙的保存失败”就这么解决了。别笑这个问题我真的帮同事排查过折腾了半小时最后硬刷新页面就好了。还有一个很重要的经验在测试环境里完整复现一次“保存 ChunJun 任务”的操作并记录操作前后的库表数据变化。这个习惯帮我养成了另一个好用的工具习惯——把典型的保存操作写成 API 脚本以后升级或迁移环境时先在测试环境跑一遍脚本确认环境没问题再让业务方上线。否则每次都是业务方报障你再去排查效率会低很多。从我个人这么多年的使用体验来说DolphinScheduler 和 ChunJun 的组合本身是很能打的绝大多数的“保存失败”都不是产品硬伤而是部署细节没对齐。插件目录放没放对、环境变量指没指对、JSON 参数合不合法、版本之间匹不匹配把这几件事梳理清楚这类问题基本都能在十分钟内定位。希望这篇对你有用。
返回列表