news 2026/9/14 8:50:43

iii 项目函数(Functions)完全指南:注册、触发调用、命名空间路由与内置函数体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
iii 项目函数(Functions)完全指南:注册、触发调用、命名空间路由与内置函数体系

iii 项目函数(Functions)完全指南:注册、触发调用、命名空间路由与内置函数体系

【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii

导读

在 iii 项目中,函数(Function)是"可被任意位置调用的最小业务单元":Worker 通过registerFunction/register_function注册一个service::name形式的函数 ID,此后无论是iii trigger命令行、进程内 SDK 调用worker.trigger,还是 http、cron、queue、state 等事件源绑定,都能以完全相同的调用路径命中该函数。本文以 docs/next/using-iii/functions.mdx 为主线,结合 Node/TypeScript、Python、Rust 三套 SDK 的源码实现与引擎侧路由逻辑,完整讲解函数注册、触发方式、TriggerAction投递语义、命名空间路由规则、CLI 调试技巧,以及引擎自带的engine::*内置函数族,读完即可在自己的 iii 项目里写函数、调函数、排函数。

注册一个函数

在 Worker 内部,worker.registerFunction(id, handler)(Python 为register_function)把一个函数注册进 iii 系统,使其可以从系统的任何位置被调用。函数id遵循service::name形式(例如math::addservice即"服务/模块"前缀,name为函数名);handler接收调用的 payload,并返回结果。

从 SDK 源码看,注册在底层是一条 REGISTER_FUNCTION 协议消息:Python 端 iii_types.py 中的 RegisterFunctionMessage 携带function_idhandler与可选的response_formatmetadatainvocation等字段;Node 端registerFunction注册后会返回一个带unregister()方法的句柄,底层发送MessageType.UnregisterFunction消息完成注销(见 sdk/packages/node/iii/src/iii.ts)。

三套语言的注册示例如下(完整代码来自关联文档):

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, { namespace: "orders" }); worker.registerFunction("math::add", async (payload: { a: number; b: number }) => { return { c: payload.a + payload.b }; });

Python

import os from iii import register_worker, InitOptions worker = register_worker( os.environ.get("III_URL"), InitOptions( worker_name="math-worker", namespace="orders", ), ) def add_handler(payload: dict) -> dict: return {"c": payload["a"] + payload["b"]} worker.register_function("math::add", add_handler)

Rust

use iii_sdk::{InitOptions, RegisterFunction, register_worker}; let url = std::env::var("III_URL").expect("III_URL must be set"); let worker = register_worker( &url, InitOptions { namespace: Some("orders".into()), ..Default::default() }, ); worker.register_function("math::add", RegisterFunction::new(|input: AddInput| { Ok(serde_json::json!({ "c": input.a + input.b })) }));

三个版本均以III_URL环境变量指向引擎地址,并在初始化时声明命名空间(Python 的worker_name用于标识 Worker 自身)。Node 与 Rust 的 handler 返回值会被序列化为函数结果;Python 的register_function还支持传入description,并会在function_id为空或重复注册时抛出ValueError(见 sdk/packages/python/iii/src/iii/iii.py)。

注册时附加 JSON Schema

要让iii trigger <function> --help能展示函数的参数与返回结构,注册函数时可以为请求 payload 和响应形状附加 JSON Schema。Node 端registerFunctionRegisterFunctionOptions以及 Python 端的response_format字段(iii_types.py)都支持声明格式。关于在注册函数时创建这些 Schema 的完整方法,参见 docs/next/creating-workers/functions.mdx 中"Attach request and response schemas"一节。

触发(调用)函数

函数在触发器(Trigger)触发时运行。同一个函数可以同时被多种触发类型调用:直接 CLI 调用(iii trigger)、进程内 SDK 调用(worker.trigger)、以及绑定到 http、cron、queue、state、iii-stream 等事件源 Worker 的触发器。所有调用路径都不会改变 handler 本身的实现——handler 只关心"payload 进来、结果出去"。

直接调用函数最常见的两种方式是:在 Worker 代码里用worker.trigger,或在终端里用命令iii trigger。引擎会把调用路由到注册了该函数的任意 Worker,整个过程不涉及触发器注册action字段控制投递语义:默认(不设置action)调用会等待函数返回结果,或等待配置的超时触发;传入不同的TriggerAction可以改变这一行为。

CLI 调用

iii trigger math::add a=2 b=3

Node / TypeScript

import { TriggerAction } from "iii-sdk"; const result = await worker.trigger({ function_id: "math::add", payload: { a: 2, b: 3 }, namespace: "default", // target a specific namespace // action: TriggerAction.Void(), // fire-and-forget // action: TriggerAction.Enqueue({ queue: "math" }), // route through queue });

Node 端trigger的返回值类型由action决定(见 iii.ts 的 JSDoc 表格):不设action时返回Promise<TOutput>(同步等待函数返回);TriggerAction.Enqueue(...)返回Promise<EnqueueResult>(引擎确认入队);TriggerAction.Void()返回Promise<undefined>(即发即忘)。

Python

from iii import TriggerAction result = worker.trigger({ "function_id": "math::add", "payload": {"a": 2, "b": 3}, "namespace": "default", # target a specific namespace # "action": TriggerAction.Void(), # fire-and-forget # "action": TriggerAction.Enqueue(queue="math"), # route through queue }) # result = await worker.trigger_async({...}) # awaitable form for asyncio callers

Rust

use iii_sdk::TriggerAction; use iii_sdk::protocol::TriggerRequest; use serde_json::json; let result = worker .trigger(TriggerRequest { function_id: "math::add".into(), payload: json!({ "a": 2, "b": 3 }), action: None, // action: Some(TriggerAction::Void), // fire-and-forget // action: Some(TriggerAction::Enqueue { queue: "math".to_string() }), // route through queue timeout_ms: None, } .namespace("default"), // target a specific namespace ) .await?;

Rust 的TriggerRequest还显式暴露timeout_ms字段:不设置时使用引擎侧配置的默认超时,设置后则覆盖为自定义超时(毫秒)。

常见的 TriggerAction 语义

  • 默认(同步):不设置action。调用等待函数返回结果,或等待配置的超时触发。
  • TriggerAction.Void():即发即忘(fire-and-forget)。调用立即返回;函数仍然运行,但调用方看不到结果。
  • TriggerAction.Enqueue({ queue }):由 queue Worker 提供(详见 docs/next/using-iii/queues.mdx)。把调用路由进一个命名队列,带重试语义;调用在消息入队后即返回。

从 Python SDK 的类型定义看(iii_types.py),TriggerActionEnqueue要求queueworker 出现在worker-compose.yaml中,并且该 worker 下要有匹配的queue_configs条目,否则触发会以enqueue_error拒绝(无队列提供方);TriggerActionVoid的类型字面量固定为"void",不返回任何响应。

另外,Worker 可以提供自己的TriggerAction——每个 Worker 支持哪些 action 类型,需要查阅对应 Worker 的文档。在 Python 中,所有阻塞方法都有对应的 awaitable 孪生方法(trigger_asyncshutdown_asynccreate_channel_async),供asyncio环境使用,详见 docs/next/reference/sdk-python.mdx。

路由到指定命名空间

Worker 及其关联的函数可以被命名空间(namespace)隔离。函数 ID 在每个命名空间内唯一,而不是全局唯一state::get可以同时在defaultordersanalytics三个命名空间各注册一次。在触发调用时设置namespace字段即可选中其中一个。

命名空间解析是**严格(strict)**的:

  • 调用显式指定orders,则只在该命名空间解析,不会回退到别处;
  • 调用未指定命名空间,则在调用方 Worker 自身的命名空间内解析;
  • 解析失败返回function_not_found错误,且错误信息会列出该函数 ID 实际存在的命名空间,方便定位。

引擎侧的function_not_found错误码在 engine/src/workers/engine_fn/mod.rs 与 engine/src/engine/mod.rs 中均有定义,并由引擎测试覆盖(例如 engine/src/engine/mod.rs 断言缺失函数的错误码、engine/src/engine/mod.rs 断言无关命名空间解析失败)。

注意事项:

  • iii trigger默认访问default命名空间,除非传入--namespace <NS>;跨命名空间调用 Worker 代码的方法参见 docs/next/using-iii/namespaces.mdx 的 "Trigger a function in a namespace" 一节。
  • 函数也可以注册(绑定)到触发器上,例如一个http请求、一个cron调度、一次state变更等。把函数绑定到事件源的方法参见 docs/next/using-iii/triggers.mdx 的 "Register a trigger" 一节。

从 CLI 触发函数

iii trigger是开发阶段的实用工具:不用写临时代码,就能从终端直接运行系统中的任何函数。给任意函数传--help,可以看到它的参数与功能描述:

iii trigger function::id --help

从 CLI 实现看,iii trigger走的是与系统其余部分完全相同的调用路径--help分支会向引擎查询engine::functions::info获取函数元数据(见 engine/src/cli_trigger/help.rs),而执行分支则构造带function_id的触发请求并发往引擎(见 engine/src/cli_trigger/exec.rs)。--namespace <NS>标志存在的原因正是命名空间路由是严格的——注册在其它命名空间的函数只有显式指定命名空间才可达(见 engine/src/cli_trigger/mod.rs 的参数文档)。

正因为走的是同一条代码路径,你可以直接在终端里完成真实工作:查看函数是做什么的、应用数据库修改、做状态变更,或交互式地尝试任何新的改动。这使得iii trigger成为开发与调试的利器。

值得强调的是:iii trigger <function> --help之所以能工作,靠的是函数携带的请求/响应格式(JSON Schema)。注册函数时如何为请求 payload 与响应形状创建这些 Schema,参见 docs/next/creating-workers/functions.mdx 的 "Attach request and response schemas" 一节。

内置函数(Common functions)

iii 引擎与标准 Worker 自带一批函数,几乎每个 iii 项目都会用到。它们看起来与你自己注册的函数没有任何区别,调用方式也完全一样(通过iii triggerworker.trigger),唯一特殊之处是:你不用注册它们

引擎函数(engine::*

引擎自身注册了一小组内省(introspection)与生命周期函数。完整的请求/响应 Schema 参见 docs/next/reference/engine-protocol.mdx 的 "Engine discovery functions" 一节。

函数作用
engine::functions::list列出所有已注册函数。传{ include_internal: true }可包含引擎内部函数。
engine::workers::list列出所有已连接 Worker 及其指标。传{ worker_id: "<uuid>" }可查询单个 Worker。
engine::triggers::list列出所有已通告的触发器类型,包含其配置与调用 Schema。
engine::registered-triggers::list列出所有已注册的触发器实例(绑定)。
engine::channels::create分配一对流式通道 reader/writer。SDK 将之封装为iii-sdk/helpers中的createChannel辅助函数;一般无需直接调用。
engine::workers::register发布调用方 Worker 的元数据(runtime、version、OS、PID、可选namespace、可选description)。SDK 在连接时会自动调用。

这些函数在引擎侧由 engine/src/workers/engine_fn/mod.rs 实现。从源码注释可以印证文档描述:engine::registered-triggers::list产出的config_summary字符串长度上限为 80 字符(CONFIG_SUMMARY_MAX_LEN,见该文件第 29-31 行);Worker 自报的description被视为不可信的自由文本(会渲染到控制台、CLI 与 LLM Agent 界面),因此在入口处截断到 280 字符并剥离控制字符(WORKER_DESCRIPTION_MAX_LEN,第 33-36 行)。engine::functions::list默认隐藏标记为metadata.internal == true的引擎内部 handler,传入include_internal: true才可见(第 228-232 行注释);engine::workers::list返回的WorkerSummary携带namespace字段——这正是"两个 Worker 可以在不同命名空间暴露相同函数 ID"这一语义的结构基础(第 362-365 行注释)。

引擎还在同一家族中发布两个订阅触发器(subscription triggers),把函数绑定到其中之一即可对注册表变化做出响应:

触发器触发时机
engine::functions-available有函数被注册或注销时。
engine::workers-available有 Worker 连接或断开时。

这两个常量的定义见 engine/src/workers/engine_fn/mod.rs(TRIGGER_FUNCTIONS_AVAILABLE/TRIGGER_WORKERS_AVAILABLE)。

常见 Worker

下面每个 Worker 都由一个独立 Worker 进程发布。其函数 ID、payload 形状与每个函数的具体行为,以各 Worker 自身文档为准:

  • State:KV 风格的状态存储,带作用域的 key 命名空间(与本文讲的路由命名空间是两回事,区别见 docs/next/understanding-iii/namespaces.mdx),并提供 create/update/delete 上的响应式触发器。
  • Stream:通过 WebSocket 向已连接客户端实时推送(iii-stream)。
  • Queue:持久化、有序的任务处理,支持重试、并发限制与死信队列(dead-letter queue)。
  • Pub/Sub:轻量的引擎内主题订阅,用于扇出(fan-out),不提供持久性保证。
  • Observability:Trace、日志、指标、告警、采样规则与汇总(rollup)。

这些内置 Worker 与"函数"机制的组合,正是 iii 项目"组合、扩展、实时观测每个服务"(Effortlessly compose, extend, and observe every service in real-time)这一设计目标的落点:你注册的函数可以被任意触发器以任意投递语义调用,而引擎与标准 Worker 提供的engine::*与各 Worker 函数则为内省、状态、流、队列、Pub/Sub 与可观测性提供了开箱即用的基础能力。

延伸阅读

  • docs/next/using-iii/triggers.mdx:把函数绑定到 http、cron、state 等事件源。
  • docs/next/using-iii/namespaces.mdx:命名空间路由与跨命名空间调用。
  • docs/next/creating-workers/functions.mdx:注册函数时为请求/响应附加 JSON Schema。
  • docs/next/reference/engine-protocol.mdx:engine::*内置函数的完整协议与 Schema。
  • docs/next/reference/sdk-python.mdx:Python SDK 的阻塞/异步双接口。
  • SDK 源码:Node sdk/packages/node/iii/src/iii.ts、Python sdk/packages/python/iii/src/iii/iii.py、Rust sdk/packages/rust/iii/src/iii.rs。

【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 8:50:10

RN鸿蒙开发中的Git与工程配置优化实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 8:48:09

Superpowers框架:AI编程助手的工程化开发实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 8:46:50

Chrome+Postman接口测试实战:从抓包到自动化回归

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华