iii Quickstart 全流程实战:用 iii 搭建跨语言、可实时组合与扩展的服务系统
【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii
本篇文章以 iii 官方 Quickstart 教程(对应仓库文档 docs/0-17-0/quickstart.mdx.skill.md)为主线,带你从零构建一个由 Python Worker 与 TypeScript Worker 组成、经由 iii 引擎实时编排的运行系统:包括项目脚手架、引擎启动、跨语言函数调用、持久化状态与 REST 端点暴露。读完本文,你将掌握iii project init、iii worker add、iii trigger、iii console等核心命令的完整用法,并理解 Worker、Function、Trigger、Engine 四要素如何协同工作。
0. 前置准备:安装 iii 并确认版本
Quickstart 假定你已经安装好了 iii。安装命令位于仓库文档 docs/0-17-0/install.mdx:
iii --version该命令应返回一个版本号。官方文档特别提醒:引擎(Engine)与各语言 SDK 包在同一 minor 版本线内可以有不同的 patch 版本,但请尽量让引擎和 SDK 保持在同一 minor 版本(例如0.17.x),除非对应发布说明明确允许混用。
本文所有命令默认在项目根目录(存放config.yaml的目录)中执行。
1. 创建项目
使用iii project init脚手架命令创建 Quickstart 项目:
iii project init quickstart --template quickstart cd quickstart该命令会生成两个后续将要运行的 Worker:
- Python Worker(
math-worker):实现"两数相加并把结果写入状态"的逻辑; - TypeScript Worker(
caller-worker):暴露一个 HTTP 端点,并通过 iii 引擎调用 Python Worker。
生成后的目录结构如下:
workers/ math-worker/ math_worker.py # Python worker caller-worker/ src/worker.ts # TypeScript worker从仓库源码看,iii project init背后由 crates/scaffolder-core 这一脚手架核心支撑:它以"模板清单"(crates/scaffolder-core/src/templates/manifest.rs)描述每个模板可用的语言、必选/可选语言、文件清单以及按语言重命名的规则(例如把iii.worker.ts.yaml折叠为目标iii.worker.yaml),quickstart正是仓库测试用例中反复出现的模板名(见 crates/scaffolder-core/tests/fetcher.rs 与 crates/scaffolder-core/src/telemetry.rs)。也就是说,Quickstart 项目不是手工拼出来的,而是模板系统按清单渲染出的标准产物。
2. 启动引擎
在quickstart目录下启动 iii 引擎:
iii --config config.yaml启动后引擎监听在ws://localhost:49134。保持该终端开启,另开一个终端进入quickstart目录执行后续命令。
关于这个地址,仓库源码给出了直接证据:引擎的默认端口由DEFAULT_PORT(49134)定义(见 engine/src/cli_trigger/mod.rs 对iii::workers::worker::DEFAULT_PORT的引用),bridge_url等配置项也默认指向ws://localhost:49134(见 engine/src/workers/configuration/README.md)。引擎进程本身承担"实时注册表 + 路由器"的角色——它持有每个已连接 Worker、每个已注册 Function 与 Trigger 的实时清单,所有 Worker 之间的调用都经由它转发,Worker 之间不存在直连流量(见 engine/src/workers/engine_fn/README.md)。
引擎的配置文件结构也很简单:config.yaml只有一个顶层键workers:,每个条目包含name(注册表 slug 或本地 Worker 名)与config块(具体形状由该 Worker 定义),并支持${VAR:default}环境变量展开(详见 docs/0-17-0/using-iii/engine.mdx)。例如:
workers: - name: iii-http config: port: ${HTTP_PORT:3111} host: 127.0.0.1如果想先跑起来做实验而不写配置文件,也可以使用iii --use-default-config启动一组默认 Worker。
3. 启动 Python Worker
iii worker add ./workers/math-worker正常情况下你会看到类似输出:
✓ Worker math-worker added to config.yaml Path /Users/tony/iii/projects/testing/quickstart/workers/math-worker ✓ Using cached deps (use --force to reinstall) ✓ math-worker started (pid: 12345) ✓ Worker auto-started这个 Worker 向引擎注册了函数math::add。你现在就可以用下面的命令直接调用它:
iii trigger math::add a=2 b=3不过,官方教程点明了一个关键观点:这种调用与直接运行一段等价脚本差别不大,iii 的真正价值在于——你可以把任意功能放进 Worker,然后通过引擎把该 Worker 与其他 Worker 组合起来,无论每个 Worker 运行在哪里、用什么语言编写。
提示:Worker 被添加后需要一点时间安装运行时依赖。如果看到
"message": "Function math::add not found",等几秒再重试即可。
iii trigger命令的细节可以从源码确认(engine/src/cli_trigger/mod.rs):它接受一个位置参数FUNCTION_PATH(如math::add),之后可跟若干key=value负载参数(a=2 b=3),也支持--json '{"a":1}'传入 JSON 负载(kv 与 JSON 混用时 kv 会覆盖 JSON 中同名键);连接参数--address(默认localhost)、--port(默认引擎端口49134)、--timeout-ms(默认 30000)以及跨命名空间解析用的--namespace都可显式指定。
4. 启动 TypeScript Worker
iii worker add ./workers/caller-worker预期输出:
✓ Worker caller-worker added to config.yaml Path /Users/tony/iii/projects/testing/quickstart/workers/caller-worker ✓ Using cached deps (use --force to reinstall) ✓ caller-worker started (pid: 23456) ✓ Worker auto-started这个 Worker 向引擎注册了函数math::add_two_numbers。至此,两个不同语言、不同运行时(Python 与 TypeScript)的独立进程都已连接到同一个引擎。
关于"Worker 可以运行在任何地方":Worker 与引擎之间唯一的耦合是 WebSocket 连接串(官方惯例通过
III_URL环境变量注入,见 docs/0-17-0/creating-workers/workers.mdx)。因此 Worker 可以跑在本机、云端、Kubernetes 副本甚至微 VM 里,只要网络可达即可。Worker 连接后经历connecting → connected → available/busy → disconnected的状态流转,这些状态由引擎跟踪并通过发现函数暴露给系统其他部分。
5. 跨语言调用
调用 TypeScript Worker,它会通过引擎调用 Python Worker 并返回结果:
iii trigger math::add_two_numbers a=10 b=20{ "c": 30 }从架构上看(见 docs/0-17-0/understanding-iii/index.mdx 的拓扑图):CLI 与引擎之间、每个 Worker 与引擎之间都是 WebSocket 连接,caller-worker调用math::add时,请求先到引擎,引擎在其实时注册表中查找math::add当前所在位置,再路由给math-worker。调用方不需要知道目标 Function 由哪个 Worker 提供、用什么语言编写、运行在哪里——这正是"任意语言、任意运行时"在实践中的含义。
6. 添加持久化状态
iii worker add可以从 Worker 注册表增量地把新 Worker 加入正在运行的系统。先添加状态 Worker,它让每个函数都能访问一个持久化的键值存储:
iii worker add iii-state然后打开workers/math-worker/math_worker.py,取消注释状态相关代码,让 handler 变成下面这样:
def add_handler(payload: dict) -> dict: a = payload.get("a", 0) b = payload.get("b", 0) logger.info(f"math::add called in Python with a={a}, b={b}") result = {"c": a + b} running_total = worker.trigger( { "function_id": "state::get", "payload": {"scope": "math", "key": "running_total"}, } ) new_total = (running_total or 0) + result["c"] worker.trigger( { "function_id": "state::set", "payload": {"scope": "math", "key": "running_total", "value": new_total}, } ) result["running_total"] = new_total return result保存文件,然后连续调用几次:
iii trigger math::add a=2 b=3{ "c": 5, "running_total": 5 }iii trigger math::add a=10 b=20{ "c": 30, "running_total": 35 }可以看到running_total在多次调用之间持续累积,而且包括经由math::add_two_numbers进入的调用也共享同一份累计值——因为caller-worker最终调用的同样是math::add,状态作用域(scope: "math")与键都一致。
这段代码展示了 iii 的一个核心机制:Function 之间的组合通过worker.trigger()完成。Python Worker 里的worker.trigger({...})与 CLI 的iii trigger走的是同一条路由路径——请求发往引擎,引擎在注册表中解析state::get/state::set并路由到提供该函数的 Worker。任何通过registerFunction()注册的函数天然具备这种"直接可调用"能力(详见 docs/0-17-0/understanding-iii/index.mdx 的"Direct invocation"小节),无需额外注册 Trigger。
7. 添加 HTTP 端点
再添加一个 HTTP Worker,把函数暴露成 REST 端点:
iii worker add iii-http打开workers/caller-worker/src/worker.ts,取消文件底部 HTTP 块的注释:
worker.registerFunction( "http::add_two_numbers", async (payload: { body: { a: number; b: number } }) => { const result = await worker.trigger({ function_id: "math::add_two_numbers", payload: payload.body, }); return { status_code: 200, body: { c: result.c, running_total: result.running_total }, headers: { "Content-Type": "application/json" }, }; }, ); worker.registerTrigger({ type: "http", function_id: "http::add_two_numbers", config: { api_path: "/math/add-two-numbers", http_method: "POST" }, });保存文件后用 curl 调用新端点:
curl -X POST http://localhost:3111/math/add-two-numbers \ -H 'Content-Type: application/json' \ -d '{"a": 100, "b": 200}'{ "c": 300, "running_total": 335 }注意:running_total从 35 累加到 335,说明 HTTP 请求与 CLI 调用共享同一个状态作用域。之前响应iii trigger的那些函数,现在无需修改 handler 就能响应 HTTP 请求——变化的只是触发器(Trigger)的注册,函数代码本身没有改动。
从 Trigger 的构成看(见 docs/0-17-0/understanding-iii/index.mdx 的"Trigger components"):一个 Trigger 有三部分——type(事件种类,如http)、config(该类型的具体配置,如路径与 HTTP 方法)、function_id(要调用的函数)。iii-httpWorker 拥有 HTTP socket;当请求到达POST /math/add-two-numbers时,iii-http查找匹配的 Trigger 并触发一次针对http::add_two_numbers的调用,引擎路由到caller-worker,响应再沿原路返回。math::add函数从头到尾看不到 HTTP 请求,它看到的只是一个 payload,和任何其他调用一样。
8. 原理剖析:Worker、Function、Trigger、Engine 四要素
Quickstart 用到的全部概念都可以归入 iii 的四个基本构件(详见 docs/0-17-0/understanding-iii/index.mdx):
| 构件 | 职责 | Quickstart 中的例子 |
|---|---|---|
| Worker | 连接引擎并注册 Function 与 Trigger 的独立进程,可运行在任何语言、任何位置 | math-worker(Python)、caller-worker(TypeScript)、iii-state、iii-http |
| Function | Worker 内具名 handler,接收 payload、返回 result;标识符遵循service::name约定 | math::add、math::add_two_numbers、state::get、state::set |
| Trigger | 触发 Function 运行的绑定:type + config + function_id | CLI 的iii trigger、worker.trigger()、HTTP Trigger(POST /math/add-two-numbers) |
| Engine | 协调器:维护 Worker/Function/Trigger 实时注册表,负责路由 | 监听于ws://localhost:49134的引擎进程 |
几个值得注意的设计要点:
- Function 标识符稳定:
service::name中service是命名空间/作用域(如math),name是具体 handler。函数 ID 在 Worker 重启后保持稳定——math-worker停机重启时,调用方无需感知,继续调用math::add即可,引擎会把调用路由到当前提供该函数的实例。 - 一个 Function 可有多个 Trigger:同一个函数可以同时被 cron 调度、队列消息、CLI 直呼和 HTTP 请求触发,只需注册多个共享同一
function_id的 Trigger,函数代码零改动。 - Worker 隔离:每个 Worker 与引擎之间是独立的 WebSocket。一个 Worker 崩溃不会影响其他 Worker——断连后它的 Function 与 Trigger 自动从路由表中移除,其余 Worker 继续服务。
- Trigger 生命周期:Trigger 经历
registered → active → invoked → unregistered四个状态;拥有它的 Worker 断连时,其所有 Trigger 与 Function 会被自动注销。
9. 查看运行中的系统:iii Console
在另一个终端运行:
iii console这会打开 iii Console——一个交互式 UI,可以查看 Worker、函数、触发器、日志、追踪与状态。完整的控制台说明见 docs/0-17-0/using-iii/console.mdx。
此外,想以编程方式查看当前注册表状态时,可以调用引擎自带的发现函数(见 docs/0-17-0/creating-workers/workers.mdx):
| 函数 | 返回内容 |
|---|---|
engine::workers::list | 每个已连接 Worker 及其指标 |
engine::functions::list | 每个已注册 Function(可用include_internal过滤) |
engine::triggers::list | 每个已注册 Trigger(可用include_internal过滤) |
engine::trigger-types::list | 每个已公布的 Trigger 类型及其配置/调用 schema |
例如在 Node SDK 中:
const { functions } = await worker.trigger({ function_id: "engine::functions::list", payload: { include_internal: false }, });10. Worker 的日常运维命令
Quickstart 只覆盖了iii worker add,但同一套 CLI 覆盖 Worker 全生命周期(详见 docs/0-17-0/using-iii/workers.mdx):
iii worker list # 列出 config.yaml 中声明的所有 Worker 及状态 iii worker start <name> # 启动一个 Worker iii worker stop -y <name> # 停止一个 Worker(-y 跳过确认) iii worker restart <name> # 重启 iii worker status <name> # 查看配置、沙箱状态与近期日志 iii worker logs <name> # 流式查看日志 iii worker exec <name> -- <command> # 在 Worker 沙箱内执行命令 iii worker reinstall <name> # 强制重新下载(等价 add --force) iii worker update [name] # 重新解析锁版本并写回 iii.lock iii worker remove -y <name> # 从 config.yaml 移除并停止进程 iii worker clear -y <name> # 同时删除磁盘上的下载产物版本管理方面:Worker 遵循 semver,解析后的精确版本记录在项目根目录的iii.lock(YAML)中,使安装跨机器、跨平台可复现;iii worker sync/iii worker sync --frozen/iii worker verify直接操作锁文件。要固定版本而不是跟踪最新版,可以:
iii worker add iii-state@1.2.0官方文档建议把iii.lock与config.yaml一起提交,以获得可复现的安装。
11. 下一步
你刚刚完成了一次完整的 iii 上手体验:脚手架生成项目、以两种不同语言启动两个 Worker、跨语言调用函数、增量添加持久化状态、最终把所有能力通过 HTTP 暴露出来——整个过程都是在运行中的系统上增量完成的。
想继续深入,官方文档给出了两条路线:
- 在实战中使用 iii:参考 Use iii(docs/0-17-0/using-iii),涵盖引擎配置、触发器、函数、Worker 注册表、部署与 Console 的使用;
- 理解核心概念:参考 Understanding iii(docs/0-17-0/understanding-iii),从概念层面吃透函数、触发器与 Worker 的设计哲学——本文档正是以 Quickstart 项目为完整工作示例展开讲解的。
【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考