iii Workers 运维实战:基于 Compose 声明、添加与实时观测项目级 Workers
【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii
Workers 是 iii 系统中一切能力的载体:任何进程只要通过 WebSocket 连上引擎并注册函数与触发器,就成为可被整个系统路由调用的 worker。本文以官方 how-to 文档 docs/next/using-iii/workers.mdx 为骨架,围绕iii Compose讲解如何声明项目级 worker、从 registry 添加现成包、执行日常运维命令,并结合仓库内iii-compose的源码实现揭示身份命名空间、配置注入与生命周期背后的真实机制。读完你将能独立编写worker-compose.yaml、用iii trigger compose::*完成增删查改,并理解引擎与 worker 之间的连接模型。
Workers 的本质:连接、注册、断开即失效
文档开篇给出了 worker 的最小定义:
- Workers 是通过WebSocket 连接 iii 引擎、并**注册函数(functions)和触发器类型(triggers)**的进程;
- 一旦 worker 断开连接,它注册的函数与触发器立即停止可调用,直到它重新连接;
- 引擎侧会为该连接自动做清理:函数和触发器从实时注册表中移除,正在执行中的调用被取消(返回
invocation_stopped错误)。
这套"断开即失效"模型是理解整个 Compose 编排的前提:进程存活与网络连接是 worker 唯一且全部的存在依据,因此需要 Compose 这样的监督者来保障进程的拉起、重启与依赖顺序。worker 的具体连接方式(SDK 的registerWorker/register_worker/register_worker,以及III_URL约定)见 docs/next/creating-workers/workers.mdx,本文聚焦部署与运维侧。
Worker 身份与命名空间
身份是(namespace, name)二元组
一个 worker 的身份由(namespace, name)唯一确定:
- 两个不同的命名空间可以使用相同的名字互不冲突;
- 在同一命名空间内,名字是独占的;
- 当一个同名同命名空间的 worker 已经在线时,重复的 live owner 会被引擎以
WORKER_NAMESPACE_CONFLICT拒绝。
这一"冲突即拒绝"的语义在技术规格 tech-specs/2026-07-14-worker-compose/namespace.md 中被明确为设计原则:命名空间是一个独立的运行时维度,而非函数重命名——state::get在任何地方都还是state::get,只是路由时多带一个命名空间参数,命中失败返回清晰的FUNCTION_NOT_FOUND并列出该函数实际存在的命名空间。冲突处理的完整错误码表格见 docs/next/using-iii/namespaces.mdx。
两个容易混淆的 namespace
原文档特意区分了两个 namespace 概念:
worker-compose.yaml顶层的namespace:字段:为该项目下所有容器(containers)选择命名空间;- Compose daemon 的
--namespace(CLI 中写作-n):用于定位该 daemon 自己的compose::*控制面函数——它是操作者寻址到"众多 daemon 中那一个"的地址。
从源码看,cli.rs 中--namespace被声明为"该 daemon 应答compose::*的命名空间,并应用到其加载的每个项目;多个 daemon 可以挂到同一引擎,正是靠它区分彼此"。--up模式下省略该参数时,daemon 会继承初始 compose 文件中的命名空间,都没有时回落为default。
SDK 侧的命名空间选择顺序
worker 实际注册到哪个命名空间,由 SDK 按以下优先级决定(详见 docs/next/using-iii/namespaces.mdx):
| 优先级 | 来源 |
|---|---|
| 1 | SDK 中显式传入的namespace选项 |
| 2 | worker 进程环境变量III_NAMESPACE |
| 3 | default(兜底) |
浏览器 SDK 无法读取进程环境,只能显式传入。利用这一机制,同一份 worker 镜像可以通过按部署设置III_NAMESPACE来服务多租户。
用 Compose 声明项目 Workers
worker-compose.yaml的结构
项目级 worker 统一声明在worker-compose.yaml的containers:之下,一个文件描述一个项目(原文档示例):
# namespace: default engine: workers: configuration: {} containers: state: worker: package://api.workers.iii.dev/state version: "0.22.2" config_name: state api: worker: path://./workers/api start_after: [state] scripts: run: pnpm start关键规则:
package://(registry 包)必须给出显式的version,Compose 据此解析包图并写入锁文件;path://(本地目录)的启动命令来自 compose 文件中的scripts.run,或者其iii.worker.yamlmanifest 中的scripts.start;start_after声明启动依赖,仅在同一文件内解析,形成启动 DAG。
启动一个项目
iii compose build --file worker-compose.yaml iii compose --namespace dev --up --file worker-compose.yamliii compose build在启动前下载所有声明的package://worker;--up随后复用共享的包缓存;本地path://worker 会被跳过(它们没有可下载的包);- 文件中出现
engine:块时,daemon 会自己拥有并负责停止引擎;没有该块时,需通过--engine或环境变量III_URL连接一个由别处管理的引擎。
从源码看命令解析
cli.rs 定义默认文件名常量DEFAULT_COMPOSE_FILE = "worker-compose.yaml":不指定--file时直接使用当前目录下这个文件,因此在项目目录内运行无需再命名文件。同时注意:
--file仅在与--up一起使用时合法(requires = "up"),否则报FileRequiresUp错误;iii compose build是唯一的纯本地动作:读取文件、准备 registry 包,不连接也不启动引擎;--namespace在解析阶段即做校验(空串、含路径分隔符等都会被拒绝),因为它既是引擎路由的命名空间,又是~/.iii/compose下的目录名。
容器字段速查表
技术规格 tech-specs/2026-07-14-worker-compose/compose-file.md 给出了完整字段契约:
| 字段 | 必填 | 规则 |
|---|---|---|
worker | 是 | package://(registry)或path://(本地目录) |
version | 仅 package | 精确或范围版本,解析进 lockfile |
start_after | 否 | 仅限同文件内的容器 id |
config_name | 否 | 该容器拥有的配置条目名 |
config_override | 否 | 稀疏 map,合并覆盖拉取到的配置基值 |
scripts | 否(无 manifest 的path://必须提供run) | 见下 |
working_dir | 否 | 默认:path://为 worker 目录,package://为 compose 文件目录 |
校验为硬错误:空containers、未知start_after、依赖环(报错会打印完整路径如api -> queue -> database -> api)、package://上写run、path://既无 manifest 又无run等都会被拒绝。iii compose validate可离线运行整套规则,无需引擎。
scripts钩子契约
同规格文档 tech-specs/2026-07-14-worker-compose/scripts.md 定义了三个钩子:
| 钩子 | 是否阻塞 | 触发时机 | 失败后果 |
|---|---|---|---|
pre_start | 是 | 每次 spawn 前,配置解析后 | 退出码非 0 或超时 → 容器failed,worker 不启动,up回滚本次操作 |
run | 受监督 | worker 进程本体 | 崩溃 → 级联停止本地依赖者 |
post_run | 否 | run退出后恰好一次(任何退出路径) | 告警 +last_error记入 status,不阻塞拆除 |
pre_start_timeout默认60s;钩子在任何 worker 类型上都执行(运行在 daemon 所在主机),run只对path://有意义——package://二进制的启动是隐式的(以标准 CLI 契约--url --namespace --config执行解析产物)。path://的启动优先级为:compose 的run> manifest 的scripts.start。prisma migrate这类项目级准备动作放在pre_start是规格明确推荐的用法。
添加 Registry Worker
compose::add是向运行中的 daemon 添加 worker 的唯一入口:它解析包依赖图、把精确版本写回 compose 文件、然后重启项目:
iii trigger -n dev compose::add worker=state iii trigger -n dev compose::add worker=queue@0.21.5-n dev寻址 dev 命名空间下的 daemon(对应 daemon 的--namespace);- 当 daemon 的工作目录不是项目目录时,用
file=/absolute/path/worker-compose.yaml指定文件; - 若添加的 registry 根包kind 为
engine(引擎已内置供应的包),会被拒绝; - 从 daemon.rs 的实现注释可以看到,
compose::add承诺"写入精确版本",因此不会留下"latest"这种不可复现的解析结果。
已发布的 worker 包可在官方 registry 索引中查找,添加前建议先查看包页面确认其 functions、triggers 与配置 schema。
运维 Workers:状态、日志、重启与下线
原文档给出了一套完整的运维命令集:
iii trigger -n dev compose::status file=worker-compose.yaml iii compose logs state --follow --namespace dev iii trigger -n dev compose::restart file=worker-compose.yaml worker=state iii trigger -n dev compose::update file=worker-compose.yaml worker=state iii trigger -n dev compose::down file=worker-compose.yaml语义对照:
| 命令 | 作用 |
|---|---|
compose::status | 进程归属、PID、最近一次 supervisor 错误 |
compose::restart worker= | 只重启一个容器 |
compose::update worker= | 编辑该 worker 的包 pin(版本锁定)并重启整个项目 |
compose::down | 按逆依赖顺序停止容器 |
iii compose logs <worker> --follow | 实时查看原始 stdout/stderr |
两类观测视图要区分开:
- 引擎的实时连接视图:用
engine::workers::list与engine::workers::info,看的是"当前有哪些 worker 连着引擎"; - 进程级视图:用
compose::status,看的是"谁拥有这些进程、PID 是多少、最后一次 supervisor 错误是什么"。
从 cli.rs 可见iii compose logs还支持--tail(默认行数见 logs.rs 的DEFAULT_TAIL_LINES,且设有上限MAX_TAIL_LINES)、--stream(限定 stdout/stderr 某一流)等参数,日志通过已运行的 daemon 读取,worker 名省略时读取项目内全部 worker 的输出。
配置:默认值、条目与覆盖优先级
每个 worker 包都会携带默认配置。容器层有两个配置相关字段:
config_name:为该容器命名其对应的配置-worker 条目(即注册到configurationworker 下的条目 id);config_override:在容器里就地覆盖具体配置值。
配置的最终生效优先级是:
package 默认值 < 已存储的配置条目值 < config_override原文档示例:
containers: http: worker: package://api.workers.iii.dev/http version: "0.21.3" config_name: http config_override: host: 0.0.0.0 port: 3111配置如何到达 worker:III_CONFIG/III_CONFIG_NAME
Compose 会把合并后的配置值通过III_CONFIG环境变量传给子进程,并在声明了config_name时把条目名发布到III_CONFIG_NAME。完整的配置体系(两层结构:config.yaml引导 seed 与configurationworker 的 schema 校验注册表)见 docs/next/using-iii/configuration.mdx。
从源码看,这是iii-compose精心设计的保留环境变量契约。spawn.rs 定义了 8 个保留键(III_URL、III_NAMESPACE、III_COMPOSE_NAMESPACE、III_COMPOSE_FILE、III_COMPOSE_DIR、III_CONFIG、III_CONFIG_NAME、III_WORKER_NAME),用户在environment/env_file中声明这些键会在解析期被拒绝,而不是被静默覆盖;子进程环境是"宿主基线 + 用户 env + 保留契约"三层合并的结果(spawn_plan)。其中:
III_URL是 daemon 自己的连接,就绪观测也走它,因此一个容器指向别的引擎对 daemon 不可见;III_NAMESPACE与III_WORKER_NAME是一对"就绪观测"键;III_CONFIG与III_CONFIG_NAME是同一份交付的两半:合并值写入文件、条目发布到 configuration 注册表(configuration.rs 的模块注释说明 worker 只需凭III_CONFIG的文件路径读取,无需任何凭据去主动拉取配置)。
引擎托管的例外:哪些 worker 不能由项目声明
以下 worker始终由引擎拥有,不属于项目 containers:
configurationiii-worker-manageriii-http-functionsiii-streamiii-sandbox
它们应当放在engine.workers下(受 Compose 管理时),或者——仅当外部 supervisor 拥有引擎时——放在引擎的config.yaml中。另有三个内部 worker 会被自动注入:iii-engine-functions、iii-telemetry、iii-observability,它们绝不能被添加为 Compose 的 package 根。
一个常见需求:要为不可信 worker 配置 RBAC 监听器,就在engine.workers(或直连引擎模式的config.yaml)中声明iii-worker-manager,其完整 schema 与命名空间级expose_functions规则见 docs/next/creating-workers/worker-manager.mdx。
Compose 之外的 Workers
Compose 并不是运行 worker 的唯一方式,它只是一个可选的进程监督者。对于由Kubernetes、systemd、其他主机或SDK 驱动的开发命令管理的进程,只需要两样东西:
- 引擎 URL;
- 一个 worker 名(以及可选命名空间)。
一旦连接成功,它就参与同一个函数与触发器网络,与其他 worker 完全对等;Compose 只拥有它在文件中声明的那些进程。这正对应 docs/next/creating-workers/workers.mdx 中"连接字符串是 worker 与 iii 实例之间的唯一耦合"的说法——worker 进程可以部署在网络可达的任何位置,SDK 侧读取III_URL即可加入。
从旧版本迁移
0.23 版本移除了iii worker命令、worker::*控制面函数以及引擎侧的"项目 worker 自启动"能力。如果你有一个旧项目,在启动前请先按 upgrading 目录 中的迁移指南处理:旧项目的 worker 声明方式需要转换到worker-compose.yaml+ Compose daemon 的模型上来,否则引擎不会再替你拉起这些进程。
编写 Worker:从部署回到开发
部署与运维解决的是"进程如何活着",而"worker 里装了什么"由 SDK 代码与 manifest 决定,入口统一在 docs/next/creating-workers/workers.mdx。该文档覆盖:SDK 连接代码(III_URL约定、namespace选项)、iii.worker.yamlmanifest、函数与触发器注册、以及发布流程。几个与本文直接相关的要点:
- manifest:项目根目录的
iii.worker.yaml声明name、可选description(一行人/LLM 可读摘要)与scripts.start;Compose 容器可用自己的scripts.run覆盖之,并用pre_run/post_run做生命周期钩子; - 生命周期状态:
connecting → connected → available / busy → disconnected;engine::workers::list等发现函数把拓扑变化暴露给系统其余部分; - 断线处理:调用方应捕获
invocation_stopped(视为取消,重试需等 worker 重连);需要感知拓扑变化时可绑定engine::workers-available/engine::functions-available触发器; - 优雅关闭:SDK 的
shutdown会干净地关闭 WebSocket,引擎随之移除注册、触发worker_disconnected事件并取消在途调用——特别适合一次性(one-shot / ephemeral)worker,如 Kubernetes Job、serverless 容器或定时脚本。
小结
把本文的运维命令与 docs/next/using-iii/configuration.mdx、docs/next/using-iii/namespaces.mdx 两篇配合使用,即可完整覆盖"声明 → 添加 → 配置 → 观测 → 下线"的项目级 worker 全生命周期。其背后的实现证据可以在 crates/iii-compose/src/cli.rs、crates/iii-compose/src/spawn.rs、crates/iii-compose/src/daemon.rs 以及 tech-specs/2026-07-14-worker-compose/ 系列规格中逐一核对。
【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考