​DAG 最大并发 100,到底是 100 个流程还是 100 个任务同时跑?

Apache DolphinScheduler 的并发控制横跨 Master、单个流程实例、Worker 三个层级,用户遇到的绝大多数"并发不达预期"问题,本质上都是没有分清这三层参数各自管的是什么。

Apache DolphinScheduler 的并发控制横跨 Master、单个流程实例、Worker 三个层级,用户遇到的绝大多数"并发不达预期"问题,本质上都是没有分清这三层参数各自管的是什么。

一、从一个最常见的疑问说起

几乎所有并发相关的困惑,都可以追溯到同一个经典问题:一个 DAG 最大并发 100,是指 100 个工作流实例同时跑,还是单个工作流里 100 个任务同时跑?官方 FAQ 对此给出过明确拆解。

答案是三层参数各管一段,互不重叠:

层级参数作用
Master 节点master.exec-threads限制该 Master 节点同时执行的流程实例总数
单个流程实例旧版 master.exec.task.number限制单个工作流内并行任务的最大数量
Worker 节点worker.physical-task-config.task-executor-thread-size限制该 Worker 节点同时执行的任务实例总数


1786527792548b352b19a832e0380e589168d7fca1c0b

并发控制架构图

弄清这张表之后,再看后面两种典型故障现象,就能很快定位是哪一层出了问题。

二、当参数配置本身没问题,但任务还是"卡住"

如果三层参数都调得足够大,用户接下来常遇到的现象是"任务提交后一直停在'提交成功'状态"。这类问题的排查路径和并发参数是分开的两件事——FAQ 给出的思路是先确认 WorkerServer 是否存活,再看 Master 是否把任务成功派发到 Worker,最后检查是否指定了没有在线机器的 Worker 分组。

换句话说,这往往不是"配置错了",而是执行链路上某一环节掉了线,配置再高也无法生效。

排除掉链路问题之后,还有一种更隐蔽的情况:服务本身正常、Worker 分组也在线,但任务依然被"拒绝"接收——这就要引入新版本才有的过载保护机制了。

三、容易被忽略的隐藏瓶颈:过载保护

3.x 版本引入了 server-load-protection:当 CPU、内存、磁盘使用率超过阈值时,Master 或 Worker 会主动拒绝继续接收任务,表现为"服务正常但任务不动"。

这个机制和前面的线程数配置是两套独立的开关,同时生效、相互叠加,很容易被误判成"并发参数没配对"。理解了这一点,再回到具体的配置项,就能对症下药了。

四、落地配置:Master 与 Worker 该怎么调

Master 端(master-server/conf/application.yaml

关键并发参数集中在这里:

  • master.exec-threads(默认 100):能同时跑多少个工作流的总闸门。

  • master.pre-exec-threads(默认 10):限制并行准备执行的 command 数量。

  • master.dispatch-task-number(默认 3):每批次向 Worker 派发的任务数,太小会拖慢派发速度。

  • master.server-load-protection.*:CPU/内存/磁盘阈值默认 0.7,是隐藏的"并发瓶颈"来源。

  • master.server-load-protection.max-concurrent-workflow-instances:Master 最大并发工作流实例数上限。

Worker 端(worker-server/conf/application.yaml

与 Master 端的调优逻辑相呼应,Worker 端主要看:

  • worker.physical-task-config.task-executor-thread-size(默认 100):直接决定单 Worker 能同时跑多少个任务。

  • worker.server-load-protection.*:默认阈值 0.8,同样是隐性的并发限制因素。

实际测试环境里也能看到这些参数被调小用于压测场景,例如把 exec-threads 设为 10,这也提示我们:同一套参数在不同环境(开发/测试/生产)应该给不同的值,而不是一套配置到处套用。

如果是 Kubernetes 部署

如果通过 Helm Chart 部署,上述 Master 参数不再直接改 yaml 文件,而是对应到环境变量,统一在 values.yaml 里调整,如 MASTER_EXEC_THREADSMASTER_EXEC_TASK_NUMMASTER_DISPATCH_TASK_NUMMASTER_SERVER_LOAD_PROTECTION_ENABLED,详细含义可查 Helm README。

五、把上面几步串成一条排查链路

结合前面几节的因果关系,实际排障可以按下面顺序走一遍:

  1. 先定位瓶颈层级:是流程实例排队(调 master.exec-threads)、单流程内任务排队(调 pre-exec-threads/任务并发),还是 Worker 端排队(调 task-executor-thread-size)。

  2. 确认过载保护有没有介入:查看 Master/Worker 日志是否有过载保护拒绝调度的记录;如果资源本身充足,适当调高 max-*-usage-percentage-thresholds 系列阈值。

  3. 回头检查 Worker 分组是否在线:参数配置正确但任务仍堆积时,常见原因还是分组内机器掉线。

  4. 调优遵循小步快跑:先小幅提升 dispatch-task-number 观察吞吐变化,再逐步调 exec-threads,避免一次性调大导致数据库或网络压力骤增。

  5. k8s 环境统一走 values.yaml,不要直接改容器内文件,否则 Pod 重启后配置会丢失。

Notes

  • 旧版本(1.2.x)使用 master.properties/worker.properties 中的 master.exec.threadsmaster.exec.task.numberworker.exec.threads;3.x 迁移到 application.yaml 下的 master.exec-threadsworker.physical-task-config.task-executor-thread-size,升级时要注意新旧参数名的映射关系,不能照抄旧配置。

  • server-load-protection 是独立于线程数配置的新机制,两者会叠加影响调度吞吐,排查时容易被忽略。

  • 索引限制下未能展示 configuration.md 全文,建议实际调优前用 Devin session 拉取完整配置文档核对默认值。