拓冰建站拓冰建站
首页 / 资讯中心 / 正文

iii 服务扩展指南:以 Worker 为单元构建系统能力,用 Trigger 与 Function 组合任意服务

iii 服务扩展指南以 Worker 为单元构建系统能力用 Trigger 与 Function 组合任意服务【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iiiiii 的核心设计原则是系统中的每一个新能力都是通过创建一个 Worker 来加入的——队列、调度器、HTTP 边缘、浏览器标签页、Agent、CRM 集成、沙箱无一例外。本指南基于docs/0-19-0/creating-workers/index.mdx.skill.md及其同节文档讲解 Worker / Trigger / Function 三大核心概念、Worker 的脚手架、连接、生命周期与清单manifest配置以及自定义 Trigger 类型的完整流程。读完本文你将掌握如何为 iii 系统新增一种能力的标准路径写一个 Worker注册 Trigger 与 Function让 Engine 完成路由而 Engine 本身永远不需要修改。Worker 就是服务iii 的能力单元模型在 iii 中Worker 是能力的单元也是服务的本体。当你需要新的行为时正确做法是编写一个新的 Worker或从 registry 安装一个已有的 Worker而不是去给 Engine 打补丁、加插件或 fork 它。Engine 是一个固定的协调者它负责在 Worker 之间路由调用invocation它维护着一张实时注册表live registry记录每个 Worker 当前提供了什么。所有让 iii 系统变得有用的逻辑都运行在 Worker 里。这也是为什么如何给 iii 加 X这类问题几乎总有一个标准答案添加一个 Worker。如果某项能力缺失缺口由 Worker 填补而不是由修改 iii 本身来填补。关于 Worker、Trigger、Function 与 Engine 的完整心智模型参见 docs/0-19-0/understanding-iii/index.mdx而如何动手构建 Worker则继续阅读本节的 Workers、Triggers 与 Functions 三篇文档。四个基本构件理解 iii 只需要四个概念其余一切皆是变体构件角色关键事实Worker承载工作的进程能连上 Engine 并注册 Trigger 与 Function 的任何东西可运行在笔记本、容器、浏览器标签页或 microVM 上任何能打开 WebSocket 的语言都可以Trigger触发工作的原因有一个 typehttp、cron、队列消息、状态变更、另一个 Function 调用trigger、一份 config路径、调度、队列和一个它要调用的function_idFunction工作本身Worker 内部的具名处理器接收 payload、返回结果标识符遵循service::name约定跨语言、跨重启保持稳定Engine协调者接受 Worker 连接、维护可用 Function 与 Trigger 的实时注册表把调用路由到当前提供目标 Function 的那个 WorkerWorker 隔离与任何语言、任何运行时Worker 被设计为相互独立的进程一个 Worker 崩溃不会影响其他 Worker。Engine 与每个 Worker 通过独立的 WebSocket 连接只把调用路由到当前已连接的 Worker某个 Worker 崩溃、重启或网络分区时它的 Function 与 Trigger 会在断开连接时从路由表中移除其余 Worker 继续提供服务。Worker 的契约足够小——只要能使用 WebSocket 和 JSON就能实现。Python Worker 与 TypeScript Worker 可以是不同语言、不同运行时、甚至不同机器上的独立进程互不感知对方上下文一切由 Engine 协调。这就是任何语言、任何运行时在实践中的含义。一、构建 Worker脚手架、连接与生命周期1. 脚手架一个新 Workeriii worker init可以从零创建一个独立 Worker命令会写入一个语言专属的项目目录包含已安装的 iii SDK、一份iii.worker.yaml清单以及可替换的示例 Function 与 Trigger 注册代码# 交互式提示选择语言 iii worker init my-worker # 完全脚本化传 --language 跳过提示 iii worker init my-worker --language typescript支持的--language取值typescriptts、javascriptjs、pythonpy、rustrs。位置参数NAME是目标目录名可用--directory覆盖。注意对已包含 iii Worker存在.iii/worker.ini的目录重复执行iii worker init不会做任何改动默认对非空目录会失败需加--allow-non-empty才能在任意非空目录中脚手架。如果不想从零编写而是安装 registry 中已有的 Worker则使用iii worker add详见 docs/0-19-0/using-iii/workers.mdx。2. 连接 Engine唯一的耦合点是连接字符串Worker 通过 WebSocket 连接 Engine。约定是使用环境变量III_URL设置 Engine 地址也可以显式传给register_worker。连接字符串是 Worker 与所加入 iii 实例之间的唯一耦合因此 Worker 进程可以部署在网络可达的任何位置。从源码看Node SDKsdk/packages/node/iii/src/iii.ts的地址解析优先级为显式地址 III_URL环境变量 默认值ws://127.0.0.1:49134DEFAULT_ENGINE_URL刻意使用 IPv4 回环地址避免localhost解析到::1而 Engine 只监听 IPv4 时连不上。此外SDK 会优先采用III_WORKER_NAME环境变量由 supervisor 为 Compose 托管 Worker 注入作为 Worker 名其次才用代码内显式名称或hostname:pid兜底。// Node / TypeScript import { registerWorker } from iii-sdk; const url process.env.III_URL; if (!url) throw new Error(III_URL must be set); const worker registerWorker(url, { workerName: my-worker, workerDescription: One-line summary of what this worker does, });# Python import os from iii import register_worker, InitOptions worker register_worker( os.environ.get(III_URL), InitOptions( worker_namemy-worker, worker_descriptionOne-line summary of what this worker does, ), )// Rust use iii_sdk::{InitOptions, WorkerMetadata, register_worker}; let url std::env::var(III_URL).expect(III_URL must be set); let worker register_worker( url, InitOptions { metadata: Some(WorkerMetadata { name: my-worker.into(), description: Some(One-line summary of what this worker does.into()), ..Default::default() }), ..Default::default() }, );3. Worker 生命周期状态机Worker 连接后经过一组有限状态connecting → connected → available / busy → disconnected。connectingWebSocket 握手阶段connectedWorker 已加入 Engine 的注册表available/busy描述 Worker 当前是否正在处理调用disconnectedWebSocket 关闭后的终态。Engine 会跟踪这些状态转移并通过 discovery 函数向其他 Worker 与工具暴露让系统其余部分可以做出反应。4. 检查实时注册表想查看当前连接了哪些 Worker可以调用engine::*::list系列 Function 获取注册表快照Function返回内容engine::workers::list每个已连接 Worker 及其指标engine::functions::list每个已注册 Function可用include_internal过滤engine::triggers::list每个已注册 Trigger可用include_internal过滤engine::trigger-types::list每个已公布的 Trigger type 及其 config / call schema这些 Function 由 Engine 内置的engine_fnWorker 提供见 engine/src/workers/engine_fn/mod.rs 与 engine/src/workers/registry.rs。调用示例// engine::functions::list const { functions } await worker.trigger({ function_id: engine::functions::list, payload: { include_internal: false }, });5. 处理断开与优雅关闭当 Worker 的 WebSocket 关闭时Engine 自动清理它的 Function 与 Trigger 离开实时注册表正在处理中的调用被取消。in-flight 请求会收到invocation_stopped错误——请像处理取消一样捕获它在拥有该 Function 的 Worker 重连之前重试都会失败。推荐的恢复方式是订阅 Engine 的 discovery 事件Trigger触发时机engine::workers-available有 Worker 连接或断开engine::functions-available有 Function 被注册或注销worker.registerFunction( discovery::on-functions, async (data: { event: string; functions: { function_id: string }[] }) { // functions 是变更后的完整快照 const ids data.functions.map((f) f.function_id); }, ); worker.registerTrigger({ type: engine::functions-available, function_id: discovery::on-functions, config: {}, });优雅关闭则调用 SDK 的shutdownEngine 移除该 Worker 的 Function 与 Trigger、发出engine::workers-available的worker_disconnected事件并用invocation_stopped取消针对它们的 in-flight 调用。若进程被强制退出Engine 在发现 socket 断开后也会达到同样的状态但优雅关闭让这一过程确定且更快。这在一次性 / 临时 WorkerKubernetes Job、serverless 容器、定时脚本中尤其有用连接、注册、干完活、shutdown()退出。二、Worker 清单iii.worker.yaml 完整字段iii.worker.yaml位于 Worker 根目录告诉 iii 如何配置运行时、安装依赖、启动进程并透传配置。Engine 在从config.yaml启动托管 Worker 以及iii worker start/stop/restart时读取它详见 docs/0-19-0/creating-workers/worker-manifest.mdx。注意直接运行 Worker 进程例如node ./myworker/src/index.js不需要iii.worker.yaml。用 SDK 连接 Engine 的 Worker无论是否由 iii 启动行为完全一致——清单只是如何启动的元数据。name: math-worker description: Evaluate math expressions over iii functions. runtime: # 作为 Worker rootfs 的基础 OCI 镜像可覆盖以锁定版本 base_image: docker.io/iiidev/python:latest scripts: install: pip install -e . start: watchfiles python src/math_worker.py字段速查name必填必须满足 registry 的 Worker 命名规则Worker 不能把自己列为依赖。runtime.base_image可选覆盖默认 OCI rootfs。必须是合法 OCI 引用字母数字加. _ - / : 最长 512 字符非法引用会被丢弃并告警回退到默认镜像。例如oven/bun:1或ghcr.io/astral-sh/uv:bookworm-slim。scripts生命周期脚本。setup在沙箱供给时运行一次装系统级依赖install安装项目依赖start启动 Worker 进程。省略scripts时Engine 会依据runtime.kind与runtime.package_manager推断install与start。常用示例scripts: setup: apt-get update apt-get install -y build-essential install: npm install start: npx tsx watch src/index.ts # Node 开发热重载env注入 Worker 进程的环境变量映射键值均为字符串。III_URL与III_ENGINE_URL会被静默过滤——连接 URL 由 Engine 自行设置。dependenciesworker-name: semver 范围映射按 registry 解析。每条范围须为合法 semver 需求如^1.2、~0.5.0、2 3重复键是错误不能依赖自身prerelease 范围语法上可接受但默认 resolver 只提供稳定版本所以最终会以version_not_found失败。dependencies: iii-http: ^0.19 iii-state: ^0.19resources可选沙箱的 CPU 与内存请求超出上限会被钳制cap 内。cpus默认2、上限4memory默认2048MiB、上限4096MiB。对 bundle 类 Worker超限时安装会发出W182 BundleResourceClamped告警。resources: cpus: 2 memory: 2048三、编写 FunctionWorker 贡献的能力Worker 通过注册 Function 向系统贡献能力。每个 Function 有一个service::name形式的id、一个接收 payload 并返回结果的 handler以及可选的请求/响应 JSON Schema详见 docs/0-19-0/creating-workers/functions.mdx。注册 Function// Node / TypeScript worker.registerFunction(math::add, async (payload: { a: number; b: number }) { return { c: payload.a payload.b }; });# Python def add_handler(payload: dict) - dict: return {c: payload[a] payload[b]} worker.register_function(math::add, add_handler)// Rust worker.register_function(RegisterFunction::new(math::add, |input: AddInput| { Ok(serde_json::json!({ c: input.a input.b })) }));附加请求 / 响应 SchemaSchema 与 Function 一并存储会出现在 iii console、iii trigger --help等地方。注意运行时校验目前尚不支持——附加的 Schema 仅作契约文档供调用方、Agent 与 console 阅读Engine 不会拒绝不符合 Schema 的 payload 或返回值。Node 传入原生 JSON Schema 对象Python 可直接传 dictRust 则通过schemars::JsonSchema从入参/出参结构体自动生成。HTTP 可调用 Function把外部端点注册为普通 Function除了进程内 handler你还可以注册一个外部 HTTP 端点作为 FunctionEngine 在该 Function 被调用时发起 HTTP 请求Worker 只需声明端点。这适合把现有 API Gateway、webhook、serverless 平台Lambda、Azure Functions、Google Cloud Functions或任意第三方 API 暴露为常规 iii Function——注册后它和任何其他 Function 一样可被worker.trigger、iii trigger以及任意 Trigger typequeue、cron、state、http调用。HttpInvocationConfig字段字段类型默认值说明urlstring必填Engine 在调用时请求的端点methodGET \| POST \| PUT \| PATCH \| DELETEPOSTHTTP 方法timeout_msnumber30000单请求超时毫秒headersRecordstring, string{}每次调用附加的请求头authHttpAuthConfig无bearer、hmac或api_key之一配合token_key/secret_key/value_key安全要点token_key、secret_key、value_key指定的是环境变量的名字而非密钥本身。Engine 在注册时从其自身进程环境解析密钥只留在 Engine 主机上绝不会通过 SDK 的 WebSocket 传输。错误处理上Engine 把调用 payload 作为 JSON 请求体发送任何非 2xx 响应或网络错误都被视为调用失败并回传给调用方。HTTP 调用的 Function 会出现在engine::functions::list中与进程内 handler 一样可通过 console 发现。返回值、错误与注销Function 要么返回值由 handler 负责匹配文档化的 response schema要么返回错误。handler 内抛出的错误会作为 invocation error 传给调用方并附带 Worker 的堆栈Node 转发error.stackPython 转发traceback.format_exc()Rust 转发底层错误的堆栈。Engine 不会吞掉它们。可以用这个区别表达预期失败返回结构化错误值与非预期失败throw / raise / 返回Err。registerFunction返回一个带unregister()方法的句柄可在运行时把 Function 从 Engine 移除Worker 断开时其全部 Function 会被自动移除挂起的调用报错退出。四、编写 Trigger绑定事件源或发布自己的 Trigger 类型Trigger 告诉 iii 何时调用哪个 Function。一个 Trigger 由三部分组成type事件种类如http、cron、config该类型的具体细节如 HTTP path 或 cron 表达式、function_id要调用的 Function。还可以指定可选的condition_function_idTrigger 触发时Engine 先用相同 payload 调用条件函数返回真值才执行 handler否则跳过调用——何时做Trigger与做什么Function由此保持分离详见 docs/0-19-0/creating-workers/triggers.mdx。绑定既有 Trigger 类型大多数时候Worker 是 Trigger 的消费者把函数绑定到其他 Worker 已发布的类型上如http来自 iii-http把函数暴露为端点、cron来自 iii-cron按计划执行、队列消息iii-queue、stateiii-state响应数据变更。绑定使用worker.registerTrigger({ type, function_id, config })// Node / TypeScript worker.registerTrigger({ type: http, function_id: math::add, config: { api_path: /math/add, http_method: POST }, });注册时发布该 Trigger type 的 Worker 必须在线否则注册失败。config的形状由各 Trigger type 自行定义记录在发布该类型的 Worker 文档中。给 Trigger 附加元数据每次绑定都可带一个可选的metadataJSON 对象由消费者在注册时设置。Engine 原样存储并在两处呈现发布方 Worker 的TriggerHandler.registerTrigger(config)回调通过config.metadata看到它——发布方可据此做优先级提示、审计标签、路由键等记账engine::triggers::list会在每条TriggerInfo上返回它console 与做 discovery 的 Worker 都能读取。注意区分metadata由消费者在每个绑定上设置自由标签袋schemas由发布方声明 Trigger type 时设置描述消费者交互的 JSON 形状如http类型的绑定 config 是{ api_path, http_method }调用 payload 是{ method, headers, query_params, body }。发布自己的 Trigger 类型mini-http 完整示例当你的 Worker 想成为事件发布方HTTP 请求、webhook、文件变更、数据库更新时需要声明自己的 Trigger type。一个 Trigger type 由两部分组成一个字符串id消费者绑定时的type: mini-http一份由你的 Worker 在进程内维护的 per-binding 路由表。Engine 的注册表只是规范地记录绑定engine::triggers::list返回的正是它但 Engine 不负责分发——它把网络上任何消费者 Worker 的 bind/unbind 以回调形式转发给发布方 Worker由发布方决定如何处理。启动时用worker.registerTriggerType({ id, description }, handler)声明。你需要实现的TriggerHandler接口暴露两个回调registerTrigger(config)有消费者绑定时运行config携带绑定实例的id、消费者的function_id和符合该类型形状的config存进路由表与unregisterTrigger(config)解绑时从路由表删除。下面是一个迷你版 iii-http 的完整示例声明mini-http类型维护bindings表并在收到 HTTP 请求时查找匹配绑定// Node / TypeScript import { registerWorker } from iii-sdk; import type { TriggerConfig, TriggerHandler } from iii-sdk; const url process.env.III_URL; if (!url) throw new Error(III_URL must be set); const worker registerWorker(url); type MiniHttpConfig { api_path: string; // 前导斜杠如 /orders http_method?: GET | POST | PUT | DELETE; }; const bindings new Mapstring, TriggerConfigMiniHttpConfig(); const httpHandler: TriggerHandlerMiniHttpConfig { async registerTrigger(config) { bindings.set(config.id, config); }, async unregisterTrigger(config) { bindings.delete(config.id); }, }; worker.registerTriggerType( { id: mini-http, description: Routes HTTP requests to bound functions }, httpHandler, );Python 版使用worker.register_trigger_type(RegisterTriggerTypeInput(idmini-http, description...), HttpHandler())Rust 版使用RegisterTriggerType::new(mini-http, Routes HTTP requests to bound functions, HttpHandler::default())。分发事件没有特殊的 fire API当底层事件源送来内容一个入站 HTTP 请求、一次 cron 滴答、一次 webhook 命中时发布方 Worker 从bindings表里查匹配项然后用普通的worker.trigger(...)调用每个匹配函数——没有任何特殊 API// 在 Worker 的 HTTP listener 内部methodpath 匹配到 bindings 表项之后 const binding bindings.get(matchedTriggerId); await worker.trigger({ function_id: binding.function_id, payload: { method, headers, body }, });每次分发时Engine 会评估消费者的config与可选的condition_function_id再把匹配的调用路由到绑定函数并把结果返回给调用方。附加 Schema 与注销 Trigger TypeTrigger type 可携带两个可选 JSON Schematrigger_request_format消费者绑定时传入的 per-bindingconfig的 schema与call_request_format触发时你交付给绑定函数的 payload 的 schema。它们会喂给 iii console、Agent 可读的 skills 和engine::trigger-types::list输出让消费者知道该发什么、会收到什么。运行时校验同样尚未支持Schema 只是契约文档。需要在不重启 Worker 的情况下下线某个类型底层资源进入维护模式、feature flag 关闭、滚动更换 schema时可调用worker.unregisterTriggerType(...)Node / Python 传完整输入对象但只用其id字段Rust 只传id字符串Node 的registerTriggerType还会返回带.unregister()快捷方法的TriggerTypeRef。Worker 断开时其公布的所有 Trigger type 会自动移除因此这一步只在 Worker 保持连接时需要。五、源码印证SDK 与 Engine 的落地实现以上 API 并非纸面设计仓库中均有对应实现SDK 连接与地址解析Node SDK 的registerWorker与DEFAULT_ENGINE_URL实现在 sdk/packages/node/iii/src/iii.ts第 87–117 行附近的resolveWorkerName/resolveAddress地址优先级为显式参数 III_URLws://127.0.0.1:49134。同一文件还定义了 Worker 注册时的命名空间冲突处理WORKER_NAMESPACE_CONFLICT同名 Worker 已在线上连接被关闭致命与FUNCTION_NAMESPACE_CONFLICT同命名空间内函数 id 冲突连接保持非致命。Engine 注册表engine::workers::list、engine::functions::list、engine::triggers::list、engine::trigger-types::list等 discovery Function 由 engine/src/workers/engine_fn/mod.rs 提供注册表核心逻辑在 engine/src/workers/registry.rs。Worker 清单解析iii.worker.yaml的解析与校验在 iii-compose crate 的 crates/iii-compose/src/manifest.rs 中体现Engine 侧对scripts.start、base_image等字段的处理可在 engine/src 的 Worker 管理相关模块中找到。结语从概览到实战的完整路径回顾本节的完整脉络Worker 是能力单元workers.mdx解决如何写一个能连上 Engine 并部署的服务Triggertriggers.mdx解决什么事件触发这些 Function 运行Functionfunctions.mdx解决Worker 贡献哪些可调用能力配套的 worker-manifest.mdx 与 workers-registry.mdx 分别覆盖清单字段与发布/安装到 registry 的机制。实践要点回顾新能力 新 Worker脚手架用iii worker init安装现成的用iii worker add连接只需一个 WebSocket 地址III_URLWorker 可在任何语言、任何位置运行registerFunction让函数可被worker.trigger/iii trigger直接调用每个注册函数天然获得直接调用能力一个 Function 可绑定多个 Trigger同一个函数可同时被 HTTP 请求、cron 调度与队列消息触发函数代码不变需要新事件源时用registerTriggerType发布自己的 Trigger typeEngine 只转发绑定回调路由由你的 Worker 在进程内维护注册表是可观测的engine::*::list系列函数与engine::*‑available事件让系统拓扑对每个 Worker 透明。基于此任何如何给 iii 加 X的问题答案都指向同一条路径写一个 Worker。【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门