在 iii 中让函数随状态变化自动执行Reactive State Pattern 完整实战指南【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iiiReactive State Pattern 是 iii 中一类事件驱动的集成模式当某个函数的触发条件是某份状态的变更而不是一次请求或一个定时任务时把函数绑定到状态 Worker 所发布的state触发器上引擎便会在被监听 key 或 scope 每次变化时自动调用该函数。本文将先讲清模式的定义与适用场景再基于仓库源码逐层拆解触发器配置、事件载荷、匹配规则与端到端示例让你能在自己的 Worker 中直接落地数据一变、逻辑即跑的响应式工作流。模式是什么把数据变了变成一等触发条件在 iii 中函数Function的执行由触发器Trigger驱动。常见的触发方式有两类请求驱动on demandHTTP 端点被调用、消息被消费时才执行调度驱动on schedule按 cron 表达式在固定时间执行。Reactive State Pattern 则提供第三种语义状态驱动on state change。当函数关心的不是谁调用了它而是某份数据发生了变化时不需要任何人显式调用它——引擎会在被监听的状态发生变化时自动触发。从源码看这是 iii 内置触发器体系中的一等公民。在 engine/src/trigger_formats.rs 中引擎为state触发器定义了独立的配置结构与调用载荷结构在 engine/src/trigger.rs 中state与state触发器类型被显式注册到触发器目录中与http、cron、queue、subscribe等内置类型并列。该模式也是 iii 官方顶层文档中单独成篇的架构模式之一见 docs/patterns/reactive-state-pattern.mdx。什么时候使用该模式当业务动作在概念上是每当这份数据变化时就做这件事whenever this data changes, do this而不是按调度或按需时就适合使用该模式。典型例子重建派生索引当源状态变化时重新计算并写回派生的索引数据扇出通知当用户记录更新时向相关服务或订阅者广播变更通知跨 scope 传播计算值当某组输入变化时把计算结果传播到另一个 scope 中。反面情况也很清晰如果动作只关心定时执行用cron触发器或有人来调用用 HTTP 等请求型触发器就不属于该模式的应用范畴。模式的三段式结构该模式的实现结构由三个角色组成状态 Worker 持有源状态source-of-truth状态以scope命名空间key的方式组织任何 Worker 都可以通过state::set/state::update等内置函数读写某个 Worker 中的函数负责处理变化计算派生值、发送通知等函数代码与普通函数完全一致状态 Worker 广告的state触发器绑定函数触发器声明它关心哪个scope/key引擎在每次变化时评估并触发绑定函数。关键点在于函数代码本身没有任何特殊之处唯一的差异只体现在触发器注册这一步。同一份函数逻辑既可以挂在 HTTP 触发器下按需调用也可以挂在state触发器下随状态变化自动执行。状态 Worker 的读写表面state::*内置函数Reactive State Pattern 的数据源由状态 Worker 提供它暴露一组state::*内置函数作为读写接口各 SDK 中均有类型化封装见 sdk/packages/node/iii/src/state.ts 的IState接口函数作用触发的状态事件state::set设置创建或覆盖一个值键不存在时触发state:created存在时触发state:updatedstate::get读取一个值无state::delete删除一个值触发state:deletedstate::update用一组操作原子更新值按键是否存在触发state:created/state:updatedstate::list列出 scope 内所有值无state::list_groups列出所有含数据的 scope无其中state::set的输入为{ scope, key, value }返回值包含old_value旧值键不存在时为null与new_value新写入值。state::update支持set、merge、increment、decrement、append、remove等原子操作序列可以避免读-改-写竞态。state触发器配置scope 与 key 的匹配规则state触发器的配置结构在引擎侧定义于 engine/src/trigger_formats.rspub struct StateTriggerConfig { /// State scope to watch (exact match filter) pub scope: OptionString, /// State key to watch (exact match filter) pub key: OptionString, /// Optional function ID to evaluate before invoking handler pub condition_function_id: OptionString, }三个字段的匹配语义如下字段类型语义scopestring可选精确匹配。仅当状态变化发生在该 scope 内时触发省略时匹配所有 scopekeystring可选精确匹配。仅当状态变化发生在该 key 上时触发省略时匹配所有 keycondition_function_idstring可选条件函数。引擎先把状态事件交给它评估返回false时不调用处理器函数注意scope与key都是精确匹配exact match过滤配置{ scope: users }会监听users下所有 key 的变化配置{ scope: users, key: profile }则只监听users/profile这一个键的变化。两者都省略时该触发器会响应所有scope 下的所有状态变化——除非确有全局需求否则建议至少限定scope避免无关变更引发大量无效调用。Rust SDK 提供了对应的构建器风格 APIsdk/packages/rust/iii/src/builtin_triggers.rslet config StateTriggerConfig::new() .scope(users) .key(profile) .condition(conditions::emailChanged);状态事件载荷处理器收到的数据当触发器被匹配并触发时处理器会收到一个结构化的状态事件对象。引擎侧的线格式定义于StateCallRequestengine/src/trigger_formats.rsSDK 侧的类型化封装见StateEventDatasdk/packages/node/iii/src/state.ts字段类型说明typestring固定为stateevent_typeenum变更类型state:created、state:updated、state:deletedscopestring发生变更的 scopekeystring发生变更的 keyold_valueany变更前的值新建键时为null删除事件时携带被删值new_valueany变更后的值删除事件时为null三种事件类型在引擎与各 SDK 中保持一致枚举定义StateEventType例如 Node SDK 中为Created state:created、Updated state:updated、Deleted state:deletedsdk/packages/node/iii/src/state.ts。由于事件载荷同时携带old_value与new_value处理器天然具备比较前后差异的能力——这正是后面条件触发与差异计算的基础。端到端实战Node / TypeScript 示例下面是一个完整的响应式流程先写入一条用户状态再注册一个监听usersscope 的state触发器函数最后再次set触发它。import { iii } from ./iii import { StateEventType } from iii-sdk/state // 1. 注册处理器函数代码与普通函数无异 const onUserUpdated iii.registerFunction( state::onUserUpdated, async (event) { if (event.type state event.event_type StateEventType.Updated) { console.log(State changed:, event.event_type, event.key) console.log(Previous:, event.old_value) console.log(Current:, event.new_value) } return {} }, ) // 2. 注册 state 触发器绑定函数到 users scope iii.registerTrigger({ type: state, function_id: onUserUpdated.id, config: { scope: users }, }) // 3. 写入状态 → 引擎评估触发器 → 自动调用 onUserUpdated await iii.trigger({ function_id: state::set, payload: { scope: users, key: user-123, value: { name: Alice, email: aliceexample.com } }, })这段流程被仓库中的集成测试完整覆盖在 sdk/packages/node/iii/tests/state.test.ts 的reactive state用例中测试先state::set写入初始值注册监听{ scope, key }的state触发器函数再次state::set写入新值后断言处理器确实被调用且收到的new_value等于新写入的数据。测试还展示了对称的清理流程trigger?.unregister()与stateUpdatedFunction?.unregister()。SDK 示例仓库中也有同样的最小写法sdk/packages/node/iii-example/src/trigger-types.tsiii.registerFunction(example::on_user_updated, async (data: { event_type?: string }) ({ processed: true, event: data.event_type, })) iii.registerTrigger({ type: state, function_id: example::on_user_updated, config: { scope: users }, })Python 与 Rust 示例同一模式在 Python 与 Rust SDK 中完全等价。Python 侧的状态类型定义StateEventType、StateEventData位于 sdk/packages/python/iii/src/iii/state.pydef on_user_updated(event): print(State changed:, event[event_type], event[key]) print(Previous:, event.get(old_value)) print(Current:, event.get(new_value)) return {} iii.register_function(state::onUserUpdated, on_user_updated) iii.register_trigger({ type: state, function_id: state::onUserUpdated, config: {scope: users, key: profile}, })Rust 侧使用RegisterTriggerInput配合StateTriggerConfigsdk/packages/rust/iii/src/builtin_triggers.rsuse iii_sdk::{RegisterFunctionMessage, RegisterTriggerInput}; use serde_json::json; iii.register_function( RegisterFunctionMessage::with_id(state::onUserUpdated.into()), |event| async move { println!(State changed: {} {}, event[event_type], event[key]); println!(Previous: {:?}, event.get(old_value)); println!(Current: {:?}, event.get(new_value)); Ok(json!({})) }, ); iii.register_trigger(RegisterTriggerInput { trigger_type: state.into(), function_id: state::onUserUpdated.into(), config: json!({ scope: users, key: profile }), metadata: None, })?;条件触发只处理真正相关的变更condition_function_id允许在触发与执行之间插入一道闸门引擎会先把状态事件交给条件函数评估只有返回false以外的结果时才继续调用处理器。这在状态确实变了但并非每次变化都值得处理的场景中非常有用。例如只在邮箱字段真正变化时才发送验证邮件const conditionFn iii.registerFunction( conditions::emailChanged, async (event) event.event_type state:updated event.old_value?.email ! event.new_value?.email, ) const fn iii.registerFunction(state::onEmailChange, async (event) { await sendVerificationEmail(event.new_value.email) return {} }) iii.registerTrigger({ type: state, function_id: fn.id, config: { scope: users, key: profile, condition_function_id: conditionFn.id, }, })条件的编写方式在 docs/0-16-0/how-to/use-trigger-conditions.mdx 中有系统讲解可与本文结合阅读。引擎视角触发器如何被匹配与调度理解引擎侧的调度逻辑能帮助你更准确地预估触发行为。从源码结构看iii 引擎的核心职责包括维护已连接 Worker 的实时注册表、跟踪各 Worker 注册的函数与触发器、以及在触发器命中时把调用路由到提供目标函数的 Worker参见 docs/0-16-0/understanding-iii/engine.mdx。state触发器类型在引擎触发器目录中注册engine/src/trigger.rs其配置与载荷的 JSON Schema 由JsonSchema派生自动生成engine/src/trigger_formats.rs因此 CLI 与工具链可以程序化地发现该触发器的配置形状。一次完整的状态触发链路为某个 Worker 调用state::set/state::update/state::delete写入状态引擎持久化到状态适配器并得到old_value与new_value引擎把变更事件与触发器注册表中的所有state触发器逐一匹配scope / key 精确匹配命中后若有condition_function_id先评估条件函数把StateCallRequest载荷路由到绑定函数的宿主 Worker 并调用。需要说明的是原模式文档在 TODO 中列出的ordering guarantees顺序保证与bursts of changes变更突发两个话题尚未在文档中展开。从现有材料可以确认的边界包括state::update本身是原子操作一组ops要么整体生效这为一次写入产生一次事件提供了基础而若要过滤高频变更中的无关部分最直接的手段就是condition_function_id。具体的跨事件排序语义建议以对应版本 Worker 文档的后续更新为准。配置状态 Worker适配器选择状态 Worker 本身可以配置不同的持久化与分布后端。参考同系列 Worker 文档 docs/0-11-0/workers/iii-state.mdx该文档对state触发器的 Worker Docs 做了完整展开内置适配器包括- name: iii-state config: adapter: name: kv config: store_method: file_based file_path: ./data/state_store save_interval_ms: 5000适配器说明关键配置kv内置键值存储支持内存与文件持久化store_methodin_memory/file_based、file_path、save_interval_ms默认5000redis以 Redis 作为状态后端redis_url支持${REDIS_URL:...}环境变量占位符bridge通过 Bridge Client 转发状态操作到远端 III 引擎实例无选择持久化适配器时需注意in_memory模式在 Worker 重启后数据会丢失生产环境建议使用file_based或redis以保证响应式流程所依赖的源状态可靠。实战注意事项小结函数复用处理函数与普通函数无异可同时被多个触发器绑定HTTP state复用同一逻辑精确匹配优于模糊监听scope/key均为精确匹配尽量限定监听范围避免全量监听带来无效调用善用condition_function_id把是否需要执行的判断从处理器中抽离配合old_value/new_value做差异比较清理注册在 Worker 卸载或测试结束时调用trigger.unregister()与函数unregister()参考 sdk/packages/node/iii/tests/state.test.ts避免悬挂注册原子更新需要读-改-写时优先使用state::update的 ops 序列减少中间态事件事件类型三态state:created/state:updated/state:deleted是三种独立事件处理器可按event_type分流old_value为null即新建、new_value为null即删除。延伸阅读模式文档源文件docs/patterns/reactive-state-pattern.mdx0-16-0 版本位于 docs/0-16-0/patterns/reactive-state-pattern.mdx状态 Worker 完整文档配置、函数、事件载荷、错误码docs/0-11-0/workers/iii-state.mdx触发器类型定义与 JSON Schema 生成engine/src/trigger_formats.rs触发器注册与调度engine/src/trigger.rsNode SDK 状态类型与接口sdk/packages/node/iii/src/state.tsPython SDK 状态类型sdk/packages/python/iii/src/iii/state.pyRust SDK 触发器配置构建器sdk/packages/rust/iii/src/builtin_triggers.rs响应式触发集成测试sdk/packages/node/iii/tests/state.test.ts触发器条件编写指南docs/0-16-0/how-to/use-trigger-conditions.mdx【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考