跳到主要内容

01. n8n 总览:把工作流做成运行时

1.1 核心判断:n8n 不是“节点集合”,而是图执行平台

节点只是扩展点。n8n 真正稳定的内核是:

输入事件 → 工作流图 → 执行上下文 → 节点执行 → 运行结果 → 状态与观测

这条链路由不同包共同实现:n8n-workflow 定义跨层数据契约,n8n-core 负责图推进与节点上下文,n8npackages/cli)负责服务边界、持久化和进程编排,n8n-editor-ui 负责编辑和手动执行体验。

1.2 四层架构

四层之间的依赖方向很重要:UI 不应该知道 Bull job 的细节;节点不应该直接操作数据库;执行器通过 IWorkflowExecuteAdditionalData、节点上下文和接口拿到需要的能力。这样同一份工作流可以被手动执行、Webhook 执行、队列执行和测试执行复用。

1.3 一次执行的最小状态机

对单次 execution,可以把状态抽象成:

new → running → waiting → running → success
↘ error / canceled

waiting 不是简单的 sleep。等待 Webhook、延迟或外部事件时,执行数据需要持久化,进程可以释放;外部事件到达后,系统根据 resume token 或执行 id 恢复上下文。因此执行记录既是审计日志,也是恢复点。

1.4 源码入口

关注点入口
跨层类型、节点接口、执行数据packages/workflow/src/Interfaces.ts
工作流图与节点关系packages/workflow/src/workflow.ts
执行推进packages/core/src/execution-engine/workflow-execute.ts
服务进程和命令packages/cli/src/commands/start.ts
执行排队与落地packages/cli/src/workflow-runner.ts

1.5 设计取舍

为什么把类型单独放在 workflow 包? 因为节点包、编辑器、执行器和 CLI 都需要共享 INodeIConnectionIRun 等契约。类型包不持有数据库连接和 HTTP 状态,降低了循环依赖风险。

为什么执行器不直接负责排队? 执行器只关心“给我一个工作流和执行上下文,我如何运行”。是否本地运行、进入 Redis 队列、由 worker 处理,属于 CLI 编排层。这个边界让测试和手动执行不必启动完整的分布式设施。

为什么需要很多上下文接口? 节点执行时需要读取输入、解析表达式、访问凭证、发 HTTP 请求、写二进制数据、触发等待等能力。把这些能力封装到上下文,既方便给不同节点提供合适权限,也能在测试中替换为 mock。

1.6 阅读检查题

  1. WorkflowExecute 为什么返回可取消的 PCancelable,而不是普通 Promise
  2. Webhook 到达时,哪个对象决定同步响应还是异步 execution?
  3. 工作流图中的 main 连接和 ai_tool 连接为什么不能使用同一个遍历规则?