Apache DolphinScheduler任务提交流程:从Web UI到Worker执行全链路
在数据处理和工作流调度中,用户最关心的问题莫过于:我的任务究竟是如何从点击"运行"按钮到最终在服务器上执行的? 本文将以Apache DolphinScheduler为原型,详细拆解任务从Web UI提交到Worker节点执行的完整链路,让你一文掌握分布式调度系统的核心运作机制。
一、Web UI提交:用户交互的起点
任务提交的旅程始于用户在Web界面上的操作。DolphinScheduler的前端界面采用Vue.js构建,核心交互逻辑集中在DAG编辑器和流程实例管理模块。
1.1 DAG编辑器的任务配置
当用户在DAG画布上拖拽组件并配置任务属性时,前端会构建一个包含完整任务元数据的JSON对象。关键代码位于DAG编辑器组件中:
src/js/conf/home/pages/dag/_source/dag.vue文件实现了DAG图的渲染和任务配置功能。用户完成配置后,点击"运行"按钮会触发提交逻辑,将任务定义转换为API请求参数。
1.2 提交请求的构建与发送
任务提交的核心逻辑在src/js/conf/home/pages/dag/definitionDetails.vue中实现。该组件会收集以下关键参数:
- 流程定义ID(processDefinitionCode)
- 执行策略(failureStrategy)
- 优先级(processInstancePriority)
- Worker分组(workerGroup)
- 环境变量(environmentCode)
这些参数通过Axios发送到后端API,典型的请求示例如下:
this.$http.post(`/projects/${projectCode}/executors/start-process-instance`, {
processDefinitionCode: this.processDefinitionCode,
failureStrategy: "CONTINUE",
processInstancePriority: "MEDIUM",
workerGroup: "default",
environmentCode: -1
})
二、API服务层:请求的接收与验证
任务请求首先到达API服务层,这一层负责请求验证、权限检查和参数处理。
2.1 请求入口:ExecutorController
API服务的核心入口是src/main/java/org/apache/dolphinscheduler/api/controller/ExecutorController.java,其中startProcessInstance方法处理任务提交请求:
@PostMapping(value = "start-process-instance")
public Result startProcessInstance(
@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@PathVariable long projectCode,
@RequestParam(value = "processDefinitionCode") long processDefinitionCode,
@RequestParam(value = "failureStrategy", required = true) FailureStrategy failureStrategy,
// 其他参数...
) {
// 权限验证和参数校验
return executorService.startProcessInstance(loginUser, projectCode, processDefinitionCode, ...);
}
2.2 业务逻辑处理:ExecutorService
请求经过验证后,会调用src/main/java/org/apache/dolphinscheduler/api/service/ExecutorService.java的实现类处理核心业务逻辑。主要步骤包括:
- 检查用户对项目的操作权限
- 验证流程定义的合法性和状态
- 创建流程实例记录(ProcessInstance)
- 生成命令对象(Command)并写入数据库
public Result startProcessInstance(User loginUser, long projectCode, long processDefinitionCode, ...) {
// 权限检查
Project project = projectService.checkProjectAndAuth(loginUser, projectCode, ProjectAction.EXECUTE);
// 获取流程定义
ProcessDefinition processDefinition = processDefinitionService.findProcessDefinitionByCode(projectCode, processDefinitionCode);
// 创建流程实例
ProcessInstance processInstance = processInstanceService.createProcessInstance(..., processDefinition, ...);
// 创建并保存命令
Command command = commandService.createCommand(..., processInstance, ...);
return Result.success(processInstance.getId());
}
三、Master服务:命令分发与任务调度
任务请求在API层完成持久化后,就进入了Master服务的处理流程,这是整个调度系统的核心。
3.1 命令消费:MasterSchedulerService
Master服务通过src/main/java/org/apache/dolphinscheduler/server/master/scheduler/MasterSchedulerService.java定期从数据库拉取命令:
@Override
public void run() {
while (running) {
// 从数据库获取命令
List<Command> commands = commandMapper.queryCommandList(
ServerConfig.getInstance().getMasterExecThreads(),
CommandType.START_PROCESS,
CommandType.RECOVER_PROCESS,
// 其他命令类型...
);
// 处理命令
for (Command command : commands) {
processCommand(command);
}
// 休眠固定时间后再次拉取
Thread.sleep(interval);
}
}
3.2 流程实例执行线程:WorkflowExecuteThread
每个命令会被封装为一个src/main/java/org/apache/dolphinscheduler/server/master/runner/WorkflowExecuteThread.java线程执行:
@Override
public void run() {
try {
// 初始化流程执行上下文
initProcessInstanceContext();
// 构建DAG图
ProcessDag processDag = generateFlowDag();
// 执行DAG
processDag.run();
} catch (Exception e) {
logger.error("workflow execute thread failed", e);
processInstance.setStatus(ExecutionStatus.FAILURE);
} finally {
// 清理资源
}
}
3.3 DAG执行与任务分发
流程实例线程会解析流程定义生成DAG图,并按照依赖关系依次调度任务节点。当任务满足执行条件时,Master会通过src/main/java/org/apache/dolphinscheduler/server/master/execution/ExecutorDispatcher.java将任务分发到合适的Worker节点:
public Boolean dispatch(final ExecutionContext context) {
// 根据任务类型选择执行器管理器
ExecutorManager executorManager = executorManagers.get(context.getExecutorType());
if (executorManager == null) {
logger.error("can not find executor manager for type: {}", context.getExecutorType());
return false;
}
// 执行任务分发
return executorManager.execute(context);
}
3.4 Worker节点选择:HostManager
在分发任务前,Master需要选择合适的Worker节点。DolphinScheduler提供了多种节点选择策略,默认实现是src/main/java/org/apache/dolphinscheduler/server/master/selector/CommonHostManager.java:
@Override
public Host select(ExecutionContext context) {
// 根据Worker分组获取可用节点列表
List<HostWorker> hostWorkers = workerNodeManager.getAvailableWorkerList(context.getWorkerGroup());
// 根据选择策略选择节点
HostSelector selector = HostSelector.of(masterConfig.getHostSelector());
HostWorker selected = selector.select(hostWorkers);
return new Host(selected.getHost(), selected.getPort());
}
四、Worker服务:任务接收与执行
任务经过Master调度后,最终会发送到选定的Worker节点执行。
4.1 任务接收:Netty服务
Worker节点通过src/main/java/org/apache/dolphinscheduler/server/worker/netty/WorkerNettyServer.java监听并接收Master发送的任务请求。任务请求由src/main/java/org/apache/dolphinscheduler/server/worker/processor/TaskExecuteProcessor.java处理:
@Override
public void process(Channel channel, Command command) {
try {
// 解析命令内容
TaskExecutionContext context = JSONUtils.parseObject(command.getBody(), TaskExecutionContext.class);
// 创建任务执行线程
TaskExecuteThread taskExecuteThread = new TaskExecuteThread(context);
// 提交到线程池执行
workerExecService.submit(taskExecuteThread);
// 发送ACK响应
sendAck(channel, command, context.getTaskInstanceId());
} catch (Exception e) {
logger.error("process task execute command error", e);
}
}
4.2 任务执行线程:TaskExecuteThread
每个任务会由独立的src/main/java/org/apache/dolphinscheduler/server/worker/runner/TaskExecuteThread.java线程执行:
@Override
public void run() {
// 初始化任务执行上下文
TaskExecutionContext context = buildTaskExecutionContext();
// 更新任务状态为运行中
taskInstanceService.updateTaskInstanceStatus(context.getTaskInstanceId(), ExecutionStatus.RUNNING);
try {
// 创建任务执行器
AbstractTask task = TaskManagerFactory.newInstance(context);
// 执行任务
task.run();
// 更新任务状态为成功
taskInstanceService.updateTaskInstanceStatus(context.getTaskInstanceId(), ExecutionStatus.SUCCESS);
} catch (Exception e) {
logger.error("task execute failed", e);
// 更新任务状态为失败
taskInstanceService.updateTaskInstanceStatus(context.getTaskInstanceId(), ExecutionStatus.FAILURE);
}
}
4.3 任务执行器:Task实现类
不同类型的任务由不同的执行器实现,例如Shell任务的执行器是src/main/java/org/apache/dolphinscheduler/server/worker/task/shell/ShellTask.java:
@Override
public void run() {
// 创建进程执行器
ProcessExecutor processExecutor = new ProcessExecutor();
// 构建命令行
String command = buildCommand();
// 执行命令并获取结果
ProcessResult result = processExecutor.execute(command,
context.getTaskProps().getTaskTimeout(),
logHandler);
// 处理执行结果
handleResult(result);
}
4.4 任务结果回调
任务执行完成后,Worker会将结果通过Netty发送回Master,由src/main/java/org/apache/dolphinscheduler/server/master/processor/TaskResponseProcessor.java处理:
@Override
public void process(Channel channel, Command command) {
// 解析任务执行结果
TaskResponseEvent responseEvent = JSONUtils.parseObject(command.getBody(), TaskResponseEvent.class);
// 更新任务实例状态
int taskInstanceId = responseEvent.getTaskInstanceId();
ExecutionStatus status = responseEvent.getState();
// 通知流程实例线程任务已完成
WorkflowExecuteThread workflowThread = processInstanceExecMaps.get(responseEvent.getProcessInstanceId());
workflowThread.handleTaskComplete(taskInstanceId, status, responseEvent.getVarPool());
}
五、全链路流程图
下面是任务从提交到执行的完整流程图:
六、总结与最佳实践
6.1 任务提交流程关键点
- 状态持久化:流程实例和任务的状态会实时写入数据库,确保系统故障后可以恢复
- 异步处理:从命令创建到任务执行,整个流程采用异步处理,提高系统吞吐量
- 可扩展性:Master和Worker均可水平扩展,通过注册中心自动发现和负载均衡
- 容错机制:Master和Worker均有故障检测和自动恢复机制
6.2 常见问题排查
- 任务提交后无反应:检查API服务日志和数据库连接状态
- 任务卡在"等待运行"状态:检查Master服务是否正常运行,命令表是否有堆积
- 任务分配失败:检查Worker节点状态和网络连通性,查看Worker分组配置
- 任务执行失败:查看Worker节点日志和任务执行日志,通常位于
logs/task目录
6.3 性能优化建议
- 根据任务类型合理配置Worker分组,避免资源竞争
- 调整Master的命令拉取间隔和Worker的心跳频率
- 对长时间运行的任务设置合理的超时时间
- 监控系统资源使用情况,及时扩容Master和Worker节点
通过了解DolphinScheduler的任务提交流程,不仅可以帮助用户更好地使用系统,还能为定制化开发和问题排查提供指导。在实际应用中,建议结合系统日志和监控指标,深入理解任务在各个阶段的状态变化,从而更好地优化工作流设计和系统配置。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



