ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

iii Functions 实战指南:函数注册、跨语言调用与 engine::* 内置函数体系

iii Functions 实战指南:函数注册、跨语言调用与 engine::* 内置函数体系 iii Functions 实战指南函数注册、跨语言调用与 engine::* 内置函数体系【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii导读在 iii 这个WebSocket 路由的 worker 网格WebSocket-routed worker mesh中**Function函数**是连接一切的核心原语每个 worker 通过registerFunction(id, handler)把一段可调用逻辑暴露给整个系统任何调用方CLI、其他 worker、事件源触发器只需知道service::name形式的函数 ID 即可发起调用。本文以 docs/0-17-0/using-iii/functions.mdx 为骨架结合 SDK 与引擎源码完整讲解如何在 Node/TypeScript、Python、Rust 中注册函数、直接或事件驱动地调用函数、用 JSON Schema 定义请求/响应契约以及引擎内置的engine::*自省函数和标准 worker 提供的常用函数。读完本文你将能独立编写、注册并调试一个可被整个 iii 系统任意调用的函数并能借助内置函数快速盘点当前引擎上的函数、worker 与触发器。函数模型service::name与调用方 → 引擎 → 处理方在 iii 中一次函数调用永远走caller → engine → handler这条路径引擎进程默认端口49134见 engine/src/workers/engine_fn/README.md持有实时注册表记录每个已连接 worker、worker 暴露的每个函数以及绑定到这些函数上的触发器。worker 之间没有直接流量每一次调用都经由引擎路由而函数 ID 是任意两个 worker 之间唯一的契约。函数 ID 采用service::name形式例如math::addservice标识提供者workername是具体的处理逻辑名。handler 接收调用的 payload并返回结果。注册函数三种语言的完整示例在 worker 内部调用worker.registerFunction(id, handler)即可把函数暴露给整个 iii 系统。SDK 会通过 WebSocket 把注册消息发送给引擎引擎将其记入注册表。Node / TypeScriptimport { registerWorker } from iii-sdk; const url process.env.III_URL; if (!url) throw new Error(III_URL must be set); const worker registerWorker(url); worker.registerFunction(math::add, async (payload: { a: number; b: number }) { return { c: payload.a payload.b }; });Pythonimport os from iii import register_worker, InitOptions worker register_worker( os.environ.get(III_URL), InitOptions(worker_namemath-worker), ) def add_handler(payload: dict) - dict: return {c: payload[a] payload[b]} worker.register_function(math::add, add_handler)Rustuse 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::default()); worker.register_function(RegisterFunction::new(math::add, |input: AddInput| { Ok(serde_json::json!({ c: input.a input.b })) }));源码层面的关键细节连接入口三种 SDK 都以III_URL环境变量为引擎地址未设置时 SDK 有内置默认值。以 Python SDK 为例sdk/packages/python/iii/src/iii/iii_constants.py 定义了DEFAULT_ENGINE_URL ws://127.0.0.1:49134特意使用 IPv4 回环地址避免localhost解析到::1而引擎只监听 IPv4 的情况。worker 命名与 namespacePython 的InitOptions支持worker_name、namespace、invocation_timeout_ms默认30000ms等配置namespace 未显式设置时回退到III_NAMESPACE环境变量再未设置则由引擎套用defaultnamespace见 iii_constants.py。注册冲突处理从 sdk/packages/python/iii/src/iii/iii.py 的_handle_registration_rejected实现可以看到若另一存活 worker 已占用同一个函数 IDFUNCTION_NAMESPACE_CONFLICT引擎只拒绝该函数的注册worker 连接保持、其余导出继续服务若(namespace, worker_name)冲突WORKER_NAMESPACE_CONFLICT则引擎关闭连接且不再重连。这意味着函数 ID 在 namespace 内必须全局唯一。断线重连SDK 在连接建立后会重放所有注册函数、触发器、触发器类型并在重连时先发送REATTACH携带上一轮worker_id与reattach_token以完成身份交接见 iii.py 的_on_connected。调用函数直接调用与事件驱动函数在触发器触发时运行。同一个函数可以被多种触发器同时调用CLI 直接调用iii trigger、进程内 SDK 调用worker.trigger或者绑定到事件源 worker如 iii-http、iii-cron、iii-queue、iii-state、iii-stream。无论走哪条路径handler 本身不需要任何改动。最常见的两种直接调用方式是从 worker 代码调用worker.trigger或从终端调用iii triggerNode / TypeScriptconst result await worker.trigger({ function_id: math::add, payload: { a: 2, b: 3 }, });Pythonresult worker.trigger({ function_id: math::add, payload: {a: 2, b: 3}, })Python 中每个阻塞方法都有对应的 awaitable 版本如trigger_async供asyncio调用者使用同步的trigger通过asyncio.run_coroutine_threadsafe把协程投递到后台事件循环线程执行见 iii.py 的_run_on_loop。Rustuse iii_sdk::TriggerRequest; use serde_json::json; let result worker .trigger(TriggerRequest { function_id: math::add.into(), payload: json!({ a: 2, b: 3 }), action: None, timeout_ms: None, }) .await?;CLIiii trigger math::add a2 b3CLI 的完整语法为iii trigger function-id [argvalue ...]引擎会把调用路由到注册该函数的 worker不涉及任何触发器注册见 docs/0-17-0/using-iii/cli.mdx。调用动作TriggerAction同步、即发即弃与队列路由默认情况下worker.trigger与iii trigger都是同步的——调用会一直等待函数返回结果或等待配置的超时触发SDK 默认超时为30000ms。action字段可以改变投递语义常用动作包括默认同步不设置action等待函数返回结果或超时。TriggerAction.Void()即发即弃调用立即返回函数仍会执行但调用方看不到结果。TriggerAction.Enqueue({ queue: math })队列路由由 iii-queue 提供把调用投递到命名队列带重试策略调用在消息入队后即返回。此外还支持各 worker 自定义的 action、条件门控condition gating以及绑定事件源触发器详见 docs/0-17-0/using-iii/triggers.mdx。从引擎侧看同步调用会分配invocation_id并等待匹配的InvocationResultVoid动作不携带invocation_id、不期望回复Enqueue则把调用交给队列 worker 持久化并按重试策略重新调用目标函数见 docs/0-17-0/sdk-reference/engine-sdk.mdx。调用在 SDK 内部如何被处理从 sdk/packages/python/iii/src/iii/iii.py 的_handle_invoke可以看出引擎下发的INVOKE_FUNCTION消息如何被处理SDK 根据function_id在本地注册表查找 handler若不存在则回function_not_found错误若存在则解析 payload 中可能携带的 channel 引用把StreamChannelRef解析为ChannelReader/ChannelWriter实例见_resolve_channels然后调用 handler并通过_invoke_with_otel_context在 OpenTelemetry span命名为execute function_id中记录输入/输出 payload最后把InvocationResult连同响应 traceparent 回传给引擎。这意味着函数调用天然贯穿分布式追踪上下文。定义请求与响应格式JSON Schema 契约函数可以为请求 payload 和响应形状携带 JSON Schema。Schema 随函数一起存储并供 iii console 与 Agent 可读的 skills 使用。以math::add为例注册时通过request_format/response_format附加 SchemaNode / TypeScriptworker.registerFunction( math::add, async (payload) ({ c: payload.a payload.b }), { request_format: { type: object, properties: { a: { type: number }, b: { type: number } }, required: [a, b], }, response_format: { type: object, properties: { c: { type: number } }, required: [c], }, }, );Pythonworker.register_function( math::add, add_handler, request_format{ type: object, properties: {a: {type: number}, b: {type: number}}, required: [a, b], }, response_format{ type: object, properties: {c: {type: number}}, required: [c], }, )Rustuse iii_sdk::{InitOptions, RegisterFunction, register_worker}; use schemars::JsonSchema; use serde::Deserialize; #[derive(Deserialize, JsonSchema)] struct AddInput { a: f64, b: f64 } #[derive(serde::Serialize, JsonSchema)] struct AddOutput { c: f64 } let url std::env::var(III_URL).expect(III_URL must be set); let worker register_worker(url, InitOptions::default()); // Rust 通过 schemars::JsonSchema 从闭包的输入/输出类型自动派生 // request_format 与 response_format无需手动编写 JSON Schema。 worker.register_function(RegisterFunction::new( math::add, |input: AddInput| Ok(serde_json::json!({ c: input.a input.b })), ));关于如何附加 Schema 的完整说明见 docs/0-17-0/creating-workers/functions.mdx。需要特别注意的是当前引擎尚未支持运行时校验。附加的 Schema 只是元数据——引擎不会强制 payload 或返回值匹配 Schema见 creating-workers/functions.mdx 的说明。请把这些 Schema 当作函数调用、Agent 与 console 的契约文档而不是执行期的校验器。另外函数除了进程内 handler还可以配置为 HTTP 调用HttpInvocationConfig支持url、method、timeout_ms、headers、auth等字段其中认证密钥以环境变量名而非明文指定引擎把调用 payload 作为 JSON 请求体发送任何非 2xx 响应或网络错误都会作为调用失败回传给调用方。引擎内置函数engine::*自省与生命周期体系引擎本身注册了一小批自省introspection与生命周期lifecycle函数。它们看起来和你自己注册的函数完全一样也通过iii trigger或worker.trigger调用——唯一的特殊之处在于你不必注册它们。完整的请求/响应 Schema 见 docs/0-17-0/sdk-reference/engine-sdk.mdx。函数作用engine::functions::list列出每个已注册的函数。传{ include_internal: true }可包含引擎内部函数。engine::workers::list列出每个已连接 worker 及其指标。传{ worker_id: uuid }查询单个 worker。engine::triggers::list列出每个已注册的触发器绑定。engine::trigger-types::list列出每个已宣告的触发器类型连同其配置与调用请求 Schema。engine::channels::create分配一对流式 channel 的读/写端。SDK 将其封装为worker.createChannel()很少直接调用。engine::workers::register发布调用方 worker 的元数据运行时、版本、OS、PID。SDK 在连接时自动调用。源码级佐证这些函数在引擎中的注册位置在 Python SDK 中这些 ID 被定义为常量EngineFunctions.LIST_FUNCTIONS engine::functions::list、LIST_WORKERS engine::workers::list、LIST_TRIGGERS engine::triggers::list、REGISTER_WORKER engine::workers::register等与 Node SDK 保持对等见 iii_constants.py。引擎侧由内置的强制 workeriii-engine-functions在进程内实现整个engine::*表面处理函数在 engine/src/workers/engine_fn/mod.rs 中以id engine::channels::createL1553、id engine::functions::listL1571、id engine::triggers::listL1773、id engine::workers::listL1898、id engine::workers::registerL1947等形式注册。该 worker 内置于引擎始终可用无需安装步骤也不会出现在config.yaml中见 engine/src/workers/engine_fn/README.md。过滤参数engine::functions::list支持prefix对function_id精确前缀匹配、search对function_id与description大小写不敏感的子串匹配、worker精确 worker 名匹配和include_internal是否包含engine::*行默认false四个过滤器。默认情况下engine::functions::list、engine::triggers::list等会隐藏内部engine::*行除非传入include_internal: true。从源码结构看引擎还注册了同族的engine::functions::info、engine::workers::info、engine::registered-triggers::list等更细粒度的自省函数需要精确的请求/响应 Schema 时可调用engine::functions::info { function_id: engine::… }获取。注册表订阅触发器响应函数/worker 变化引擎还发布两个同族的订阅触发器。把函数绑定到其中之一即可对注册表变化作出反应触发器触发时机engine::functions-available有函数被注册或注销时。engine::workers-available有 worker 连接或断开时。引擎侧这两个触发器的 ID 常量定义在 engine/src/workers/engine_fn/mod.rsTRIGGER_FUNCTIONS_AVAILABLE、TRIGGER_WORKERS_AVAILABLEPython SDK 中对应EngineTriggers.FUNCTIONS_AVAILABLE engine::functions-available见 iii_constants.py。functions-available的 payload 包含event: functions_changed与当前函数列表从实现看引擎会以轮询方式每 5 秒检测函数注册表变化并触发该触发器。标准 worker 提供的常用函数几乎每个项目都会用到除了引擎内置函数引擎与标准 worker 还随附一小批常用函数。它们看起来与你自行注册的函数无异调用方式也一样iii trigger或worker.trigger唯一特殊之处仍然是你不必注册它们。每个函数都由独立的 worker 发布函数 ID、payload 形状与逐函数行为以对应 worker 的文档为准Stateiii-stateKV 风格的带作用域命名空间状态支持 create/update/delete 上的响应式触发器。Streamiii-stream通过 WebSocket 向已连接客户端实时推送数据。Queueiii-queue持久化、有序的作业处理支持重试、并发限制与死信队列DLQ。Pub/Subiii-pubsub引擎内的轻量级 topic 订阅用于扇出fan-out场景不提供持久化保证。Observabilityiii-observability提供 traces、logs、metrics、alerts、采样规则与聚合rollups。在动手编写新 worker 之前官方建议先做两件事调用engine::functions::list用prefix或search过滤盘点引擎上已有的函数再查询公共注册表中的 worker优先复用现成能力如compose::add { worker: … }两者都无果时才手工编写新 worker见 engine/src/workers/engine_fn/README.md。小结注册worker.registerFunction(id, handler)ID 为service::name形式handler 接收 payload 并返回结果SDK 通过 WebSocket 与引擎默认ws://127.0.0.1:49134通信断线自动重连并重放注册namespace 内函数 ID 必须唯一。调用worker.trigger({ function_id, payload })或iii trigger function-id a1 b2默认同步等待结果TriggerAction.Void()即发即弃、TriggerAction.Enqueue()走队列路由也可通过 http、cron、queue、state、stream 等事件源触发器驱动handler 无需改动。契约request_format/response_format附加 JSON SchemaRust 可由类型自动派生目前仅作元数据文档不做运行时校验。自省engine::functions::list、engine::workers::list、engine::triggers::list、engine::trigger-types::list、engine::channels::create、engine::workers::register构成完整的引擎自省表面配合engine::functions-available/engine::workers-available订阅触发器可以实时感知整个 iii 系统的注册状态。如需继续深入可阅读 docs/0-17-0/using-iii/triggers.mdx触发器与调用动作全解、docs/0-17-0/creating-workers/functions.mdx编写 worker 与附加 Schema、HTTP 调用函数、docs/0-17-0/sdk-reference/engine-sdk.mdx引擎协议与发现函数以及 engine/src/workers/engine_fn/mod.rsengine::*函数的进程内实现。【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表