n000000

71 阅读5分钟

在n8n中TaskOffer有什么用

TaskOffer 是 launcher 和 runner 向 task broker 发送的消息,用于表明它们愿意执行特定类型的任务。 

主要功能

  1. 注册任务处理能力:当 launcher 或 runner 连接到 task broker 后,它们会发送 runner:taskoffer 消息来表明可以处理哪种类型的任务(例如 "javascript")。
  2. 非过期 offer(Launcher) :Launcher 发送的 task offer 设置 ValidFor: -1,表示这是一个永不过期的 offer,使 launcher 可以无限期地等待任务分配。
  3. 触发任务分配:当 task broker 接受一个 task offer 时,它会发送 broker:taskofferaccept 消息,这会触发 launcher 启动实际的 runner 进程来执行任务。

工作流程

在握手过程中,task offer 的使用流程如下: 

  1. Launcher 注册后发送非过期的 task offer
  2. 等待 task broker 接受这个 offer
  3. 收到接受后,launcher 延迟任务(runner:taskdeferred)并启动真正的 runner 进程
  4. 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 配置。

三者关系

image.png

核心关系:

  • Task Broker: 中央协调者,负责接收任务请求、管理 runner 注册、分配任务
  • Launcher: 轻量级守护进程,伪装成 runner 注册到 broker,按需启动真正的 runner
  • Runner: 实际执行任务的进程,由 launcher 启动,完成任务后自动关闭

完整流程

1. Launcher 初始化阶段

Launcher 启动后执行以下步骤:

  1. 切换到配置的工作目录
  2. 准备环境变量
  3. 检查 Task Broker 是否就绪(通过 HTTP /healthz 端点)
  4. 获取 launcher 的 grant token(用于身份验证)

2. WebSocket 握手阶段

Launcher 与 Task Broker 建立 WebSocket 连接并完成注册:

image.png

关键点:Launcher 发送的 task offer 设置 ValidFor: -1,表示永不过期的 offer。

3. 任务到达与 Runner 启动

当有任务需要执行时: 

image.png

Launcher 收到 offer 接受后:

  1. 发送 runner:taskdeferred 消息延迟任务
  2. 关闭 WebSocket 连接
  3. 获取新的 grant token 给 runner launch
  4. 启动 runner 进程

4. Runner 执行与自动关闭

Runner 进程:

  • 连接到 Task Broker 并注册
  • 发送有期限的 task offer
  • Broker 将延迟的任务匹配给 runner
  • Runner 执行任务并返回结果
  • 空闲超时后自动关闭(默认 15 秒)

5. 循环重启

Runner 退出后,launcher 检测到进程结束:

  1. 取消健康监控
  2. 清理 grant token
  3. 重新开始循环,回到步骤 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

  1. 发送 runner:taskdeferred 消息,包含任务 ID
  2. 关闭 WebSocket 连接
  3. 启动真正的 Runner 进程

这个"延迟"操作的目的是让 Task Broker 保留这个任务,等待即将启动的 Runner 进程来认领。 

image.png

为什么 Runner 发送有期限的 Offer

Runner 发送有期限的 Offer(与 Launcher 的非过期 Offer 不同)是因为 Runner 是实际执行任务的进程,它有自动关闭机制。

Launcher vs Runner 的 Offer 差异

Launcher 的 Offer

  • ValidFor: -1 (非过期)
  • 目的:作为轻量级占位符,可以无限期等待任务

Runner 的 Offer

  • 有过期时间(expiring offers)
  • 目的:反映 Runner 的实际生命周期

设计原因

  1. 资源优化:Runner 进程在空闲一段时间后会自动关闭(默认 15 秒) setup.md:50 ,所以它的 Offer 应该有时效性,避免 Broker 将任务分配给即将关闭的 Runner。
  2. 生命周期匹配:有期限的 Offer 确保 Task Broker 知道 Runner 的可用性窗口,当 Runner 即将超时关闭时,Broker 不会再分配新任务给它。
  3. 循环机制:Runner 关闭后,Launcher 会重新启动握手流程,再次发送非过期 Offer 等待下一个任务。

image.png

Notes

这种双层 Offer 机制的核心优势是:Launcher 作为持久的轻量级守护进程保持与 Broker 的连接,而 Runner 作为按需启动的重量级进程只在需要时运行。 有期限的 Offer 确保系统能够正确处理 Runner 的自动关闭行为,避免任务分配到已经不存在的进程。

JS Runner 配置文件

github.com/n8n-io/n8n/…

{
	"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": ""
			}
		}
	]
}