ARTICLE DETAIL

资讯详情

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

iii 引擎(Engine)核心机制解析:启动流程、热重载、路由与实时注册表

iii 引擎(Engine)核心机制解析:启动流程、热重载、路由与实时注册表 iii 引擎Engine核心机制解析启动流程、热重载、路由与实时注册表【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii导读本文以 iii 项目的 Engine 概念文档为主体系统讲解 iii 进程通信引擎的运行时核心机制从启动流程、三大职责连接管理、注册表维护、调用路由到 Worker 断连清理、config.yaml热重载、架构无关路由以及基于实时注册表的发现能力。读完本文你将掌握 iii Engine 的完整工作模型理解 any language, any runtime 是如何在引擎层被实现的并能在自己的 Worker 中熟练使用engine::*::list快照调用与engine::workers-available/engine::functions-available订阅事件。Engine 是 iii 中让 Workers、Triggers 与 Functions 的价值得以发生的那一层薄薄的运行时核心。整个系统由 Engine 统一接纳来自各种语言、各种运行时环境的 Worker 连接并负责在它们之间路由调用。本文依据 docs/0-16-0/understanding-iii/engine.mdx 展开并结合仓库内 engine/src 的 Rust 实现与测试进行纵深印证。Engine 在 iii 架构中的位置在 iii 中Worker 是承载 Functions 与 Triggers 的执行单元可以是一个 Python Agent、一个浏览器标签页里的 TypeScript Worker、一个 microVM 中的 Rust 二进制或 Kubernetes 上的 OCI 镜像。而 Engine 扮演的是这些异构 Worker 之间的共同语言层它不关心 Worker 的语言、运行时或部署位置只负责三件事——维持连接、维护注册、路由调用。从仓库的入口代码可以印证这一分工engine/src/main.rs 中 CLI 的about描述即为 Process communication engine进程通信引擎而 Engine 的实际启动逻辑则通过EngineBuilder构建并调用engine.serve()进入服务循环let engine EngineBuilder::new() .with_config(config) .with_config_path(config_path) .build() .await?; engine.serve().await?;Engine 启动流程当一个 Engine 进程启动时它依次完成四件事解析命令行参数。Engine 本身是一个二进制通过 clap 解析参数包括--config path指定配置文件路径、--version、--no-update-check禁用后台更新与安全通告检查等无子命令时默认进入 serve 模式见 engine/src/main.rs 与run_serve逻辑。加载配置文件通常是config.yaml。如果启动目录下不存在配置文件CLI 会在交互式终端上询问是否创建No config.yaml found. Create it and start the engine? [Y/n]在非交互会话容器、CI、服务管理器中则直接创建一份带空 workers 列表的默认配置保证无人值守启动见 engine/src/main.rs 的ensure_config_file。应用配置中的 Worker 声明逐个启动声明的 Worker 进程。配置文件中workers列表里的每一项都对应一个由 Engine 托管生命周期的进程。开始接受连接。完成这一序列后Engine 就绪可以接受来自 Worker 的 WebSocket 连接并在它们之间路由调用。以下是一份真实的 Engine 配置示例仓库自带 engine/config.yaml可以看到workers列表的声明结构以及一个针对 Engine 自身生命周期管理的顶层参数registration_namespace_grace_ms: 5000 # Only workers that are part of the engine lifecycle belong here. Project # workers such as http, state, cron, queue, pubsub, and bridge belong in # worker-compose.yaml. workers: - name: iii-stream config: port: ${STREAM_PORT:3112} host: 127.0.0.1 adapter: name: redis config: redis_url: redis://localhost:6379 - name: configuration config: adapter: name: fs config: directory: ./config ttl_seconds: 0其中值得注意两点registration_namespace_grace_ms: 5000对应源码中的REGISTRATION_NAMESPACE_GRACE常量5 秒它决定一个刚连接的 Worker 在命名空间尚未到达时其注册消息被缓冲等待的最长时间详见下文注册缓冲与命名空间宽限配置注释明确说明只有属于 Engine 生命周期管理的 Worker 才放在这里如iii-stream、configuration而 http、state、cron、queue、pubsub、bridge 等项目级 Worker 属于worker-compose.yaml的管辖范围这体现了 Engine 与 compose 编排体系之间的职责划分。Engine 的三大运行时职责Engine 在运行时承担的职责覆盖三个关注点接受 Worker 的 WebSocket 连接并维护当前有哪些 Worker 在线的实时注册表。这是整个系统拓扑信息的源头连接来了要登记连接断了要清理。跟踪每个已连接 Worker 注册的 Functions 与 Triggers并将它们暴露为一个统一的系统级表面。对调用方而言无论目标 Function 实际由哪个 Worker 承载都表现为同一个可寻址的全局命名空间。路由调用当一个 Trigger 触发或一个 Function 被调用时Engine 找到提供目标 Function 的那个 Worker并将调用派发过去。从实现看这三项职责分别对应 engine/src/engine/mod.rs 中的连接管理WorkerConnection、WorkerConnectionRegistry、注册表FunctionsRegistry、TriggerRegistry、ServicesRegistry与调用处理InvocationHandler三组核心数据结构。Engine 自身并不执行业务逻辑它只做找到对的人、把话带到这也是薄层定位的体现。Worker 断连清理当某个 Worker 断开连接时Engine 会自动清理该 Worker 在实时注册表中的足迹它的 Functions 与 Triggers 从注册表移除针对这些 Functions 的在途调用被取消系统其余部分继续提供服务不受影响。在途调用invocation_stopped错误码对于在断连瞬间正在执行的在途请求调用方会收到invocation_stopped错误应将其视作一次取消cancellation处理。需要特别注意的是在该 Function 所属的 Worker 重新连接之前重试会一直失败。因此正确的做法是捕获该错误后配合发现事件等待 Worker 回归。以下是 Node/TypeScript 与 Python 中的典型处理方式完整多语言示例见 docs/0-16-0/creating-workers/workers.mdx 的 Handling Worker disconnects 一节import { IIIInvocationError } from iii-sdk; try { const result await worker.trigger({ function_id: math::add, payload: { a: 1, b: 2 }, }); } catch (err) { if (err instanceof IIIInvocationError err.code invocation_stopped) { // Worker disconnected mid-invocation. Subscribe to engine::functions-available // to know when to retry. return; } throw err; }from iii import IIIInvocationError try: result worker.trigger({ function_id: math::add, payload: {a: 1, b: 2}, }) except IIIInvocationError as err: if err.code invocation_stopped: # Worker disconnected mid-invocation. Subscribe to engine::functions-available # to know when to retry. return raise断连后发现事件及其一致性语义断连清理不是静默发生的——Engine 会发出发现事件让其他 Worker 与工具可以感知拓扑变化触发事件触发时机engine::workers-available某个 Worker 连接或断开时engine::functions-available某个 Function 被注册或注销时这两个事件常被用于等待 Worker 回归后继续工作的场景例如在invocation_stopped之后订阅engine::functions-available等目标 Function 重新出现再重试调用。关于断连清理相关的错误码与发现事件的完整一致性语义可参见 docs/0-16-0/creating-workers/workers.mdx。从源码看这两个发现事件的注册类型定义在 engine/src/workers/engine_fn/mod.rspub const TRIGGER_FUNCTIONS_AVAILABLE: str engine::functions-available; pub const TRIGGER_WORKERS_AVAILABLE: str engine::workers-available;Engine 在启动时通过register_trigger_type将二者作为内置 Trigger 类型注册任何 Worker 都可以直接对它们绑定 Trigger 函数。配置热重载Config hot-reloadconfig.yaml在运行时会被持续监听watched。当文件内容发生变化时Engine 会执行完整的重载流水线解析 → 差异比较diff→ 校验 → 提交commit。关键语义是未变化的 Worker 在重载期间保持运行只有新增、移除或变更的 Worker 才会被重启非法配置会导致 Engine 退出如果新配置存在解析错误或校验失败Engine 会选择退出而不是进入一个不确定indeterminate的中间状态。这是明确的 fail-fast 设计。从实现层面看这一机制位于 engine/src/workers/reload.rs。diff_entries将新旧配置的 Worker 条目按名字做结构比较WorkerEntry::PartialEq比较name、image与config并精确分成四类pub struct ReloadDiff { pub added: VecWorkerEntry, pub removed: VecString, pub changed: VecWorkerEntry, pub unchanged: VecString, }added/changed携带完整条目供提交阶段使用removed/unchanged只携带名字。提交阶段只对added、removed、changed三类 Worker 执行销毁/启动unchanged的 Worker 则通过其专属的shutdown_txwatch channel继续保持运行从而做到无变化则零打扰。此外parse_and_normalize在解析时还会自动补齐缺失的强制 Workermandatory注册项并对重名条目分配唯一实例 IDassign_instance_ids。仓库中对应了完整的重载单测与端到端测试例如 engine/tests/config_reload_e2e.rs、engine/tests/reload_diff_unit.rs 与 engine/tests/reload_scope_unit.rs可用于进一步研究 diff 与提交的边界行为。架构无关路由Architecture-agnostic routing路由与语言、运行时、部署位置完全无关。Engine 对以下所有形态的 Function 都施加同一条路由路径笔记本上运行的Python Agent浏览器标签页里的TypeScript WorkermicroVM 中的Rust 二进制Kubernetes 上的OCI 镜像。这正是 any language, any runtime 在 iii 中成为具体可验证的属性而非一句口号的原因路由决策只基于哪个 Worker 注册了目标 Function 标识而与该 Worker 的底层形态无关。无论 Worker 是进程、线程、浏览器标签页还是隔离的 VM 沙箱只要它通过 WebSocket 连上 Engine 并完成注册就进入同一套寻址与派发体系。这一点也在 iii-supervisor 与 iii-init 等 crates 中得到了配套支撑Engine 关注的是协议层面的连接与路由而进程的隔离、挂载与监督由 crates/iii-supervisor 与 crates/iii-init 负责二者解耦使 Worker 形态可以自由演进而不影响路由逻辑。发现与实时注册表Discovery and the live registryEngine 维护着一份注册表记录每一个已连接的 Worker每个 Worker 注册的 Functions绑定到这些 Functions 上的 Triggers。其他 Worker 和工具既可以按需读取注册表的快照也可以订阅注册表的变化。快照engine::*::list系列函数要查看当前有哪些东西连接在 Engine 上调用engine::*::list系列 Functions 即可获取注册表当前状态Function返回内容engine::workers::list每个已连接 Worker 及其指标metricsengine::functions::list每个已注册 Function可按include_internal过滤engine::triggers::list每个已注册 Trigger可按include_internal过滤engine::trigger-types::list每个已通告的 Trigger 类型含其 config 与 call schemasinclude_internal用于决定是否包含 Engine 内置internal的注册项传false时只返回用户注册的 Function/Trigger。Node / TypeScript 调用示例// engine::workers::list, pass { worker_id: uuid } to look up one worker const { workers } await worker.trigger({ function_id: engine::workers::list, payload: {}, }); // engine::functions::list const { functions } await worker.trigger({ function_id: engine::functions::list, payload: { include_internal: false }, }); // engine::triggers::list const { triggers } await worker.trigger({ function_id: engine::triggers::list, payload: { include_internal: false }, }); // engine::trigger-types::list const { trigger_types } await worker.trigger({ function_id: engine::trigger-types::list, payload: { include_internal: false }, });Python 调用示例# engine::workers::list, pass {worker_id: uuid} to look up one worker workers worker.trigger({ function_id: engine::workers::list, payload: {}, })[workers] # engine::functions::list functions worker.trigger({ function_id: engine::functions::list, payload: {include_internal: False}, })[functions]Rust 调用示例use iii_sdk::TriggerRequest; use serde_json::json; // engine::workers::list, pass json!({ worker_id: uuid }) to look up one worker let workers worker .trigger(TriggerRequest { function_id: engine::workers::list.into(), payload: json!({}), action: None, timeout_ms: None, }) .await?;Rust 版本的engine::functions::list、engine::triggers::list、engine::trigger-types::list与上述结构一致仅 payload 变为json!({ include_internal: false })完整示例见 docs/0-16-0/creating-workers/workers.mdx。订阅发现事件除了按需读取快照还可以通过注册 Trigger 来订阅拓扑变化从而在 Worker 回归时继续未完成的工作worker.registerFunction( discovery::on-workers, async (data: { event: string; worker_id: string }) { if (data.event worker_connected) { // A Worker just joined the registry; its Functions are callable now. } }, ); worker.registerTrigger({ type: engine::workers-available, function_id: discovery::on-workers, config: {}, }); worker.registerFunction( discovery::on-functions, async (data: { event: string; functions: { function_id: string }[] }) { // functions is the full snapshot after the change. const ids data.functions.map((f) f.function_id); }, ); worker.registerTrigger({ type: engine::functions-available, function_id: discovery::on-functions, config: {}, });注意engine::functions-available事件负载中的functions字段是变更后的完整快照full snapshot after the change而不是增量这保证了订阅方始终能拿到一致的最新视角。更多多语言示例见 docs/0-16-0/creating-workers/workers.mdx 的 Subscribe to changes 一节。源码纵深注册缓冲、命名空间宽限与遥测帧注册消息的按序缓冲从源码结构看Engine 对连接的首个命名空间解析做了精细处理由于连接的命名空间不是以协议消息到达的而是附着在engine::workers::register引擎调用上而部分 SDK 会先刷新自己的RegisterFunction队列、后发送该注册调用因此注册消息经常在命名空间未知时就已到达。为此 engine/src/engine/mod.rs 为每个连接维护一个命名空间状态机Pending命名空间未知注册消息按到达顺序排队Draining命名空间已知但队列未清空新到的注册消息追加到队尾保证到达顺序Resolved命名空间已知且队列已清空注册走直通路径Aborted连接正在拆除携带命名空间以便清理函数仍能释放对应注册。需要缓冲的不仅仅是RegisterFunction——UnregisterFunction也必须入队否则它可能抢在对应的RegisterFunction之前执行造成注销先于注册的顺序反转从而复活一个已被 Worker 退役的函数。RegisterTrigger同样入队以保持与同连接RegisterFunction的相对到达顺序见is_namespaced_registration。这就是配置中registration_namespace_grace_ms: 5000的底层语义宽限期内默认 5 秒真实 SDK 都会发出注册调用队列在微秒级即可排空该宽限只为那些从不发送注册调用的手写客户端设一个上限。WebSocket 上的遥测帧Engine 的 WebSocket 连接不仅是协议消息通道也是遥测通道。从源码看Engine 识别三种带魔数前缀的二进制帧并直接摄入见 engine/src/engine/mod.rsOTLP前缀OTLP trace spansingest_otlp_jsonMTRC前缀OTEL metricsingest_otlp_metricsLOGS前缀OTEL logsingest_otlp_logs。这意味着 SDK 可以在同一条连接上随调用一起上报 trace、metrics 与 logs而无需额外的传输设施。与之呼应原文档也提示查询 traces、logs 与 metrics 由 iii-observability Worker 文档覆盖见 engine/src/workers/observabilityEngine 只负责在传输层摄入与转发。小结iii 的 Engine 是一层刻意保持薄的进程通信核心启动时解析参数、加载配置、拉起声明的 Worker、开始接受 WebSocket 连接运行时维护谁在线、注册了什么的实时注册表并在 Trigger 触发或 Function 被调用时把调用精确派发到承载目标 Function 的 WorkerWorker 断连时自动清理其足迹并取消在途调用以invocation_stopped通知调用方配置变更时通过 parse → diff → validate → commit 流水线实现最小化重启的热重载非法配置则直接退出。而发现与实时注册表机制——engine::*::list快照加engine::workers-available/engine::functions-available订阅——让整个系统的拓扑对任何语言、任何运行时的 Worker 都透明可见这正是 any language, any runtime 由口号变为可验证属性的落点。【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表