☰
DolphinScheduler 核心流程全解析:状态机、依赖与告警机制
2026/10/21 9:32:58 网站建设 项目流程

如果你正在用或者准备用 DolphinScheduler,那么有一件事最好在动手前先搞明白:它不是简单地把脚本丢到 cron 里就完事的工具,而是一套有状态、有依赖、有告警的分布式任务编排引擎。所谓 dolphinscheduler 核心流程,指的就是一条任务从提交、解析、调度、分发、执行、回调再到最终落库和告警的完整链路。我最近拿它编排了一条边缘 AI 巡检流水线,每天定时去拉摄像头 RTSP 流、跑模型推理、按规则发告警、给截图加水印后上传对象存储,最后汇总推送日报——整套流程跑通的过程,也把它的核心流程翻了个底朝天。这篇文章就沿着这条链路往下拆,适合刚上手 DolphinScheduler、卡在“任务为什么没跑”“为什么失败”“下游为什么不触发”这类问题上的同学。

1. 先搞清楚 DolphinScheduler 核心流程在说什么

1.1 不是单机编排:一套任务要经过四个服务、一条链路

正常情况下,一条 DolphinScheduler 任务从产生到执行完,要经过 API Server、Master Server、Worker Server 和 Alert Server 四个服务,此外背后还有注册中心和数据库。很多新手把环境搭起来后,直接在 Web 界面点“运行”,以为后台就是把脚本执行一遍,其实不是。Web 界面只是 API Server 的前台,真正干活的是 Master 和 Worker。

API Server 的工作是承接前端交互、解析用户请求,把“运行工作流”“创建定时调度”“查看实例状态”这些操作落成数据库中的记录。Master Server 是整个系统的调度大脑,它负责拉取到点需要执行的工作流实例,把 DAG 拆解成一个个具体任务项,然后决定每个任务项应该派给哪个 Worker、什么时候派、失败后怎么处理。Worker Server 是真正卖苦力的执行者,Shell、Python、SQL、HTTP 之类的任务插件都在这里运行,它会生成日志文件,执行完再回报状态。Alert Server 则相对独立,只在需要发通知时介入,负责根据告警组配置把消息发出去。

我习惯把这条链路理解成装修项目:老板接单并登记合同(API Server),项目经理看施工图纸(DAG),按工序拆开任务,分给水电工、瓦工、油漆工(Worker),工人干完活向项目经理汇报,质检发现哪道工序有问题后,由行政通知客户(Alert Server)。只要把这个类比里的角色对齐,再看日志里的各种状态流转就会觉得顺很多。

需要特殊说明的是,Master 和 Worker 都是可以水平扩展的,而且天然依赖注册中心做服务发现和选主。集群模式下,Master 挂掉会自动切换,Worker 挂掉则上面正在跑的任务会超时并走失败重试逻辑。这些能力都建立在核心流程之上,理解了单条任务的生命周期,才能理解集群容错到底在哪一步发挥作用。

1.2 状态机:从 SUBMITTED_SUCCESS 到 SUCCESS/FAILURE,中间到底发生了什么

在 Web 界面里看到的“运行中”,背后其实是一串状态流转。一个工作流实例从被触发到执行结束,通常会经历这些状态:SUBMITTED_SUCCESS(已提交)、SCHEDULED(已被 Master 调度到)、DISPATCH(任务实例已派发给 Worker)、RUNNING_EXECUTION(正在执行)、SUCCESS、FAILURE、PAUSE、STOP,另外还有 READY_PAUSE、KILL 这种非正常中止状态。

每一个状态不是凭空冒出来的,它们在数据库和日志里都有对应事件。很多朋友说“任务卡住了”,实际往往是卡在某一个状态跳不过去。比如一直停在 SUBMITTED_SUCCESS,通常是 Master 还没把这个实例拉起来;一直停在 DISPATCH 或 RUNNING_EXECUTION,很可能是 Worker 端异常或者任务执行超时。

我把核心流程拆成五步来讲:

  1. 提交:用户点击运行或定时触发后,工作流定义连同参数字段被实例化成一条工作流实例记录,写入数据库。
  2. 解析:Master 根据实例化时间,把 DAG 里的节点逐个分析,检查上游依赖条件是否全部满足。首次满足条件的节点,进入待调度队列。
  3. 调度:Master 根据任务优先级、Worker 资源组、最大并行度等参数,计算出该任务应该分给谁,并等待分配时机。
  4. 分发:Master 将具体任务请求发送给目标 Worker,Worker 接单后开始执行任务插件,生成日志。
  5. 执行与回调:Worker 执行完往 Master 回报任务状态与日志路径,Master 更新任务实例和工作流实例状态,再判断是否触发下游节点、重试、告警或进入失败策略。

这五步看起来简单,但每个环节都可能被各种配置影响。接下来把最常见的三个关键环节单独拎出来说:依赖、参数、告警与重试。

2. 核心流程里的关键环节:依赖、参数、告警与重试

2.1 DAG 依赖:上游失败之后,下游到底动不动

DolphinScheduler 里的 DAG 决定了任务之间的先后顺序。默认情况下,上游任务执行成功,下游才会开始;上游失败,下游会被标记为不执行,或者整个工作流直接失败。但“默认情况”只是冰山一角,实际项目里往往要改好几处配置。

工作流定义里有一个“失败策略”,End 表示只要一个节点失败,整个工作流立刻终止;Continue 表示继续跑其他不受影响的分支。任务节点上还有“失败重试”设置,包含重试次数和重试间隔。重试耗尽后,再按照失败策略走。还有一个容易忽略的点:如果 DAG 里两个上游节点都指向同一个下游,下游必须等两个上游全部完成才会启动,这是 AND 关系。如果你只想“任选其一”,就要用条件分支节点或者子流程来编排。

我以前在巡检项目里踩过一个典型的坑:拉流推理节点偶尔因为网络超时失败,下游的水印截图节点根本不执行,整条链路直接断掉。检查 DAG 发现下游直接依赖失败任务,工作流失败策略又是 End。后来我把失败策略改成 Continue,发现那个分支还是会被标记为“上游失败跳过”。最终方案是给拉流推理节点配置重试 3 次、间隔 60 秒,让偶发网络抖动在重试层面消化掉,下游就再也没莫名其妙断过。

这种“失败后重试优先于分支决策”的顺序,是 DolphinScheduler 核心流程里很重要的一条原则。排查问题的时候,别一上来就改失败策略,先确认是不是重试次数不够、任务本身是不是有偶发故障,这样才能精准定位。

2.2 参数传递:核心流程中最容易被忽略的“接力棒”

DolphinScheduler 的参数体系有两层:工作流全局参数和节点局部参数。全局参数对所有节点可见,适合放公共配置,比如数据库地址、接口地址、Webhook 地址;局部参数只对当前节点可见,适合放节点自己的逻辑配置。除此之外,DolphinScheduler 还区分输入参数和输出参数。上游节点执行完可以把结果写进输出参数,下游节点通过引用这个参数来接力。

实际项目中,最容易踩的坑是跨节点参数引用无效。我有一次做边缘巡检,上游“拉流与推理”节点把检测结果文件路径写到一个输出参数里,比如/data/output_${yyyyMMdd}.json,下游“水印截图”节点需要读这个路径。结果下游一直读到空串,排查了半天才发现,不是参数名写错,而是 DolphinScheduler 的跨节点输出参数并不会自动注册到下游,需要在任务节点的参数配置里显式引用上游任务的输出,并且参数名必须完全一致。

日期类内置参数在定时任务里非常常用,比如$[yyyy-MM-dd]、$[yyyyMMdd]、$[yyyyMMddHHmmss]。这里要注意,DolphinScheduler 对日期参数有自己的一套扩展语法,不是完全照搬 Linux 的 date 格式,秒级和时区相关场景建议先在手动运行里打出来验证一下。还有一个细节:如果全局参数和局部参数重名,局部优先。这个优先级关系在处理复杂 DAG 时特别容易出幺蛾子,命名尽量带上业务前缀能省不少事。

2.3 告警和重试:流程里的保险丝

DolphinScheduler 的告警不是只有“失败才告警”,而是分成功、失败、超时三类。每类可以绑定告警组,告警组里可以配置邮件、钉钉 Webhook、企业微信等渠道。如果你不想每天被成功邮件轰炸,通常只在失败和超时开告警,成功告警留到关键节点上用。

重试方面,任务节点可以设置重试次数和重试间隔。比如某个任务配置重试 2 次、每次间隔 1 分钟,那它在第一次失败后会进入“重试”状态,等间隔到了重新提交给 Worker。如果重试 2 次仍然失败,才判定该任务失败,然后触发失败策略。这里有个实际心得:重试虽然好,但并不是所有任务都适合重试。像“推送钉钉消息”“发送邮件”“上传对象存储”这类非幂等操作,重复执行可能造成重复消息、重复上传。建议这类任务设置重试次数为 0,或者在脚本内部按“任务实例 ID + 业务日期”做幂等去重,避免同一个事件被报三遍。

还有一个容易被忽略的点:DolphinScheduler 的告警触发和重试逻辑都发生在核心流程的状态迁移过程中,不是独立于流程之外的特性。也就是说,如果 Master 或者 Worker 自己进程都挂了,告警就可能丢失。因此生产环境建议把 Alert Server 单独部署,同时把数据库和服务日志的监控也纳入告警体系,不要让 DolphinScheduler 成为“唯一报警器”。

3. 实操拆解:用 DolphinScheduler 编排一条“边缘 AI 巡检”工作流

3.1 先梳理业务,再画 DAG

我最近做的一个模拟项目,是在某个园区布设边缘 AI 巡检盒子,通过摄像头 RTSP 拉流,边缘盒子跑 YOLO/ONNX 模型,识别占用消防通道、垃圾桶满溢这类事件。识别到异常后需要告警、截图留证、加水印上传,还要每天出一份汇总报告。整套流程带定时、带依赖、带告警,非常典型,很适合拿来说明 DolphinScheduler 的编排能力。

我把整条链路拆成 5 个节点:

节点名称任务类型职责依赖
rtsp_pull_inferPython拉取多路 RTSP 流,用 ONNX Runtime 推理,产出检测结果无(定时触发)
alert_rulePython对检测结果做规则判断,包括置信度阈值、去重、冷却时间rtsp_pull_infer
watermark_uploadPython对命中事件截图加水印、时间戳、设备编号、经纬度,上传对象存储alert_rule
daily_reportPython汇总当天事件,生成日报 JSONalert_rule
webhook_pushShell调用钉钉/企业微信 Webhook,推送日报或者异常提醒daily_report、watermark_upload

这里每个节点的职责都尽量单一。把视频流处理和规则判断分开,是因为推理任务很吃 GPU,如果重试次数设置不当,可能在失败后反复抢显存;而规则判断很轻,可以放心多设几次重试。DAG 分层也符合 DolphinScheduler 的设计习惯,上游出问题时不至于把下游的轻量任务一并拖死。

3.2 部署一个最小可用环境

先把环境跑起来,推荐先用 standalone 模式,一个进程把 API、Master、Worker、Alert 全部拉起来,适合学习和验证核心流程。生产环境当然要拆成真正的集群,但那套部署会牵扯到数据库初始化、注册中心、Nginx 反代等一堆事情,先跑通流程更重要。

以 3.x 版本为例,大致步骤是这样的:

# 解压安装包后进入目录 cd dolphinscheduler-bin # 按官方文档初始化数据库(MySQL 为例,把 sql 目录里的初始化脚本导入) # 修改 conf 下的数据库连接配置、注册中心配置 # 启动 standalone 模式 sh bin/dolphinscheduler-daemon.sh start standalone-server

启动后访问 Web 界面,默认端口一般是 12345 附近,具体以你下载版本的 release note 为准。第一次登录需要创建租户和用户,注意 DolphinScheduler 的任务执行是以租户身份去跑的,Worker 机器上最好有对应的系统用户,否则会出现各种权限问题。这个细节很多人忽略,后面排查日志空白的坑时经常碰到。

3.3 创建工作流:节点、参数、定时调度

部署完成后,进入 Web UI 创建工作流。第一步新建项目,第二步进入“工作流定义”,点击创建工作流,第三步拖入 5 个节点并按前面的依赖关系连线。

连线之后,配置全局参数。我通常把公共配置放在全局参数里:

rtsp_urls = rtsp://camera1/stream,rtsp://camera2/stream conf_threshold = 0.45 alert_cooldown = 60 webhook_url = https://oapi.dingtalk.com/robot/send?access_token=xxx

每个节点再单独配局部参数,比如“水印截图”节点需要设备编号、经纬度、对象存储路径;“日报汇总”节点需要前一天日期。局部参数只在当前节点生效,写错影响面小,有问题也好定位。

DolphinScheduler 支持 Python、Shell、SQL、HTTP 等多种任务插件。以 Python 节点为例,脚本大致长这样:

# rtsp_pull_infer 节点脚本(示意) import os import cv2 import onnxruntime import json rtsp_urls = os.environ["rtsp_urls"].split(",") session = onnxruntime.InferenceSession("/models/yolov8.onnx") results = [] for url in rtsp_urls: cap = cv2.VideoCapture(url) # 逐帧或抽帧推理… # 把命中结果 append 到 results cap.release() # 把结果写到一个文件,路径通过输出参数传给下游 output_path = f"/data/output_{os.environ['yyyyMMdd']}.json" with open(output_path, "w") as f: json.dump(results, f) # 声明输出参数路径 print(f"output_result={output_path}")

这里我给的是精简示意,真正跑的时候还要处理视频抽帧、模型前处理、后处理等细节。核心在于:Python 节点所在的 Worker 机器必须提前装好 opencv、onnxruntime 这些依赖,否则日志里会报 ModuleNotFoundError。

定时配置是另一个重点。DolphinScheduler 的 cron 表达式和 Linux crontab 不完全一样,它按秒级解析,格式是秒 分 时 日 月 周 年。比如每天 8 点 30 分触发,写成:

0 30 8 * * ? *

注意第 6 位是“周”,用?表示不指定,这是 Quartz 风格的约定,和传统 crontab 的*有区别。我刚上手时直接搬 Linux crontab 的写法,结果调度器一直不触发,后来一查是周字段解析报错。

3.4 跑通一次核心流程:从手动运行到定时调度

第一次建议先手动运行,不要直接挂定时。手动运行能即时在页面上看到工作流实例创建、任务实例逐个执行、日志刷新的过程。点开每个任务实例的“日志”,能看到 Worker 端的实时输出,这比定时半夜跑崩了再爬起来看日志舒服多了。

我通常会按下面的顺序验证:

  1. 先跑一个空 DAG,只有一个 Shell echo 节点,确认整个链路通。
  2. 再加入“拉流与推理”节点,单独跑一次,确认 GPU 资源够、模型加载正常。
  3. 逐步补齐其他节点,每次只加一个,避免多个节点同时出问题时定位困难。
  4. 全部手动跑通后,再配置定时调度并上线工作流。

上线后,定时任务到点会自动生成工作流实例。实例页面能看到每个节点状态逐步变成 SUCCESS,任务日志按时间归档在 Worker 机器上,每天的日报最终通过 webhook_push 推送到群里。这一步跑通,你对核心流程的感知基本就建立起来了。

4. 核心流程踩坑与排查实录

4.1 常见问题速查表

我整理了一张高频问题表,很多都是团队里新人问过我的:

现象可能原因排查方法
工作流一直停在 SUBMITTED_SUCCESS,不往下走Master 没拉取到实例;Worker 资源组不足;任务队列堆积看 Master 日志;检查 Worker 分组和资源配额;查数据库里的任务实例表
任务日志点了空白Worker 日志目录权限不对;任务还没被分发到 Worker登录 Worker 机器看 logs/task 目录;确认 Worker 心跳正常
上游成功,下游不执行依赖方向连错;失败策略不当;下游被标记为 SKIP看 DAG 连线;看下游任务实例状态是 SKIP 还是 WAITING
重试没生效重试次数为 0;失败策略先终止了流程;Worker 进程崩溃检查节点配置;查看任务实例的重试记录;看 Worker 端日志
定时任务不触发工作流未上线;cron 表达式无效;服务器时区不对确认定时管理里已启用;用页面自带的 cron 校验;统一时区为 Asia/Shanghai
下游拿不到上游参数参数名不一致;没显式引用上游输出;全局参数覆盖了局部参数检查上游输出参数配置;在下游节点参数列表里看引用关系
任务执行时间总是差 8 小时数据库时区和应用时区不一致统一 JDBC 连接、服务器、容器的时区设置

4.2 独家排障技巧与实战心得

第一个技巧:把“日志”当第一现场。DolphinScheduler 的日志确实有一个痛点,就是分散在多个 Worker 机器上,Web 界面有时点不开或者打开很慢。这时候直接在 Worker 机器上查日志最有效:

# 查看某个任务实例的运行日志(以实际路径为准) tail -f logs/task/20250305/xx_task_xxx.log

通过日志里的关键词能定位绝大概率的问题,比在页面上反复刷新有用得多。

第二个技巧:AI 推理任务必须控制并发。DolphinScheduler 的 Worker 分组和任务优先级看起来简单,但在 GPU 场景里乱设会导致多个推理任务同时抢显存,轻则 OOM,重则整机卡死。我一般把推理类任务单独分配一个 Worker 分组,只放一台实例,或者用资源组限制并发数为 1。这样哪怕误配了重试,也不会出现多个进程同时抢卡的情况。

第三个技巧:边缘盒子上跑 DolphinScheduler,环境变量和 PATH 极易踩坑。Shell 节点执行某个程序时,调度进程带过来的环境是“最小化环境”,经常找不到 Python 或 CUDA 库路径。我习惯在脚本第一行加:

export PATH=/usr/local/bin:/usr/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH

或者干脆全用绝对路径调用解释器和脚本,这样能省掉大量“命令找不到”“动态库加载失败”的诡异问题。

第四个技巧:处理断网续传时,重试间隔别设太短。边缘巡检场景里摄像头可能临时掉线,DolphinScheduler 的重试机制可以自动重连,但如果重试间隔设成 10 秒,摄像头还没恢复,重试反而把带宽打爆。我通常把重试间隔设在 120 秒以上,重试次数控制在 2 到 3 次,配合冷却时间去重,既能恢复,又不至于雪上加霜。

最后说点我自己的体会。DolphinScheduler 这套核心流程,说复杂也不复杂,无非是提交、调度、执行、回调这条链路,但真正上手后你会发现,90% 的问题都不是功能做不到,而是没有把状态流转吃透。尤其是那些看起来像“卡住”的任务,其实每一秒都在数据库里留下日志和状态记录,花一点时间理清状态机,比瞎点页面有用得多。如果你也在做边缘 AI 巡检这类带定时、带依赖、带告警的任务编排,建议先拿一个小 DAG 跑通,再慢慢加节点;我在实际项目中每次增加一个任务节点,都会先手动跑一次,再放上定时,这套习惯帮我省掉了大量夜里爬起来处理失败任务的时间。希望这篇关于核心流程的拆解对你有帮助。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询