在n8n中TaskOffer有什么用
TaskOffer 是 launcher 和 runner 向 task broker 发送的消息,用于表明它们愿意执行特定类型的任务。
主要功能
- 注册任务处理能力:当 launcher 或 runner 连接到 task broker 后,它们会发送
runner:taskoffer消息来表明可以处理哪种类型的任务(例如 "javascript")。 - 非过期 offer(Launcher) :Launcher 发送的 task offer 设置
ValidFor: -1,表示这是一个永不过期的 offer,使 launcher 可以无限期地等待任务分配。 - 触发任务分配:当 task broker 接受一个 task offer 时,它会发送
broker:taskofferaccept消息,这会触发 launcher 启动实际的 runner 进程来执行任务。
工作流程
在握手过程中,task offer 的使用流程如下:
- Launcher 注册后发送非过期的 task offer
- 等待 task broker 接受这个 offer
- 收到接受后,launcher 延迟任务(
runner:taskdeferred)并启动真正的 runner 进程 - Runner 进程再次发送 task offer(这次是有过期时间的)来接收被延迟的任务。
launcher、runner、 task broker 的关系是怎样的,完整的交互流程
注册位置
Launcher 在启动时通过 WebSocket 连接注册到 Task Broker(即 n8n 实例)。
Task Broker 的默认 URI 是 http://127.0.0.1:5679,可以通过环境变量 N8N_RUNNERS_TASK_BROKER_URI 配置。
三者关系
核心关系:
- Task Broker: 中央协调者,负责接收任务请求、管理 runner 注册、分配任务
- Launcher: 轻量级守护进程,伪装成 runner 注册到 broker,按需启动真正的 runner
- Runner: 实际执行任务的进程,由 launcher 启动,完成任务后自动关闭
完整流程
1. Launcher 初始化阶段
Launcher 启动后执行以下步骤:
- 切换到配置的工作目录
- 准备环境变量
- 检查 Task Broker 是否就绪(通过 HTTP
/healthz端点) - 获取 launcher 的 grant token(用于身份验证)
2. WebSocket 握手阶段
Launcher 与 Task Broker 建立 WebSocket 连接并完成注册:
关键点:Launcher 发送的 task offer 设置 ValidFor: -1,表示永不过期的 offer。
3. 任务到达与 Runner 启动
当有任务需要执行时:
Launcher 收到 offer 接受后:
- 发送
runner:taskdeferred消息延迟任务 - 关闭 WebSocket 连接
- 获取新的 grant token 给 runner launch
- 启动 runner 进程
4. Runner 执行与自动关闭
Runner 进程:
- 连接到 Task Broker 并注册
- 发送有期限的 task offer
- Broker 将延迟的任务匹配给 runner
- Runner 执行任务并返回结果
- 空闲超时后自动关闭(默认 15 秒)
5. 循环重启
Runner 退出后,launcher 检测到进程结束:
- 取消健康监控
- 清理 grant token
- 重新开始循环,回到步骤 1(检查 broker 就绪)
Notes
这种设计的优势是资源优化:Launcher 作为轻量级占位符持续运行,只在有任务时才启动完整的 runner 进程,任务完成后 runner 自动关闭,从而最小化资源使用。整个系统通过 WebSocket 消息协议进行协调,使用 grant token 进行身份验证。
延迟任务(Task Deferred)
延迟任务是指 Launcher 收到 Task Broker 的任务分配后,因为自己无法执行任务,所以通过发送 runner:taskdeferred 消息告诉 Broker "暂时搁置这个任务,等待真正的 Runner 来接手"。
工作原理
当 Launcher 收到 broker:taskofferaccept 消息时,它会: handshake.go:181-200
- 发送
runner:taskdeferred消息,包含任务 ID - 关闭 WebSocket 连接
- 启动真正的 Runner 进程
这个"延迟"操作的目的是让 Task Broker 保留这个任务,等待即将启动的 Runner 进程来认领。
为什么 Runner 发送有期限的 Offer
Runner 发送有期限的 Offer(与 Launcher 的非过期 Offer 不同)是因为 Runner 是实际执行任务的进程,它有自动关闭机制。
Launcher vs Runner 的 Offer 差异
Launcher 的 Offer:
ValidFor: -1(非过期)- 目的:作为轻量级占位符,可以无限期等待任务
Runner 的 Offer:
- 有过期时间(expiring offers)
- 目的:反映 Runner 的实际生命周期
设计原因
- 资源优化:Runner 进程在空闲一段时间后会自动关闭(默认 15 秒) setup.md:50 ,所以它的 Offer 应该有时效性,避免 Broker 将任务分配给即将关闭的 Runner。
- 生命周期匹配:有期限的 Offer 确保 Task Broker 知道 Runner 的可用性窗口,当 Runner 即将超时关闭时,Broker 不会再分配新任务给它。
- 循环机制:Runner 关闭后,Launcher 会重新启动握手流程,再次发送非过期 Offer 等待下一个任务。
Notes
这种双层 Offer 机制的核心优势是:Launcher 作为持久的轻量级守护进程保持与 Broker 的连接,而 Runner 作为按需启动的重量级进程只在需要时运行。 有期限的 Offer 确保系统能够正确处理 Runner 的自动关闭行为,避免任务分配到已经不存在的进程。
JS Runner 配置文件
{
"task-runners": [
{
"runner-type": "javascript",
"workdir": "/home/runner",
"command": "/usr/local/bin/node",
"args": [
"--disallow-code-generation-from-strings",
"--disable-proto=delete",
"/opt/runners/task-runner-javascript/dist/start.js"
],
"health-check-server-port": "5681",
"allowed-env": [
"PATH",
"GENERIC_TIMEZONE",
"NODE_OPTIONS",
"N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT",
"N8N_RUNNERS_TASK_TIMEOUT",
"N8N_RUNNERS_MAX_CONCURRENCY",
"N8N_SENTRY_DSN",
"N8N_VERSION",
"ENVIRONMENT",
"DEPLOYMENT_NAME"
],
"env-overrides": {
"NODE_FUNCTION_ALLOW_BUILTIN": "crypto",
"NODE_FUNCTION_ALLOW_EXTERNAL": "moment",
"N8N_RUNNERS_HEALTH_CHECK_SERVER_HOST": "0.0.0.0"
}
},
{
"runner-type": "python",
"workdir": "/home/runner",
"command": "/opt/runners/task-runner-python/.venv/bin/python",
"args": ["-m", "src.main"],
"health-check-server-port": "5682",
"allowed-env": [
"PATH",
"N8N_RUNNERS_LAUNCHER_LOG_LEVEL",
"N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT",
"N8N_RUNNERS_TASK_TIMEOUT",
"N8N_RUNNERS_MAX_CONCURRENCY",
"N8N_SENTRY_DSN",
"N8N_VERSION",
"ENVIRONMENT",
"DEPLOYMENT_NAME"
],
"env-overrides": {
"PYTHONPATH": "/opt/runners/task-runner-python",
"N8N_RUNNERS_STDLIB_ALLOW": "",
"N8N_RUNNERS_EXTERNAL_ALLOW": ""
}
}
]
}