实战:用 state 触发器把“数据变化“变成函数执行)
iii 响应式状态模式Reactive State Pattern实战用 state 触发器把数据变化变成函数执行【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii本文是 iii 项目中Reactive State Pattern响应式状态模式的完整技术指南。该模式解决一个非常具体的问题当某个函数的触发条件是某段状态发生了变化而不是一次请求或一个定时任务时如何让引擎自动、可靠地驱动这段逻辑。读完本文你将掌握 iii 中state触发器的注册方式、状态事件state:created/state:updated/state:deleted的语义、scope/key匹配规则以及如何用condition_function_id做条件化执行——并能在 Node / Python / Rust 三套 SDK 中落地完整可运行的示例。模式核心让状态变化本身成为触发器iii 的函数Function既可以由请求、定时任务驱动也可以由状态变化驱动。Reactive State Pattern 的要点是当某个函数对应某段状态发生变化而不是某个请求到来或到点了时把这个函数绑定到状态工作器state worker所发布的 state-change 触发器上。引擎会在被监视的 key 或 scope 发生变化时自动触发该函数。与普通函数相比函数体本身没有任何区别——它接收事件、做处理、返回结果唯一不同的是触发器的注册方式。这是该模式最核心的设计思想响应式逻辑是附加在状态之上的而不是通过显式调用来驱动的。原文档位于 docs/0-17-0/patterns/reactive-state-pattern.mdx本文围绕它展开并补充了仓库内状态工作器文档、引擎源码与 SDK 实现作为佐证。何时使用该模式该模式适用于每当这份数据变了就去做这件事的场景而不是定时或按需场景。原文档给出的典型例子重建派生索引当源状态source state变化时重新计算派生数据广播通知当某个用户记录更新时把变更扇出fan out给相关方跨作用域传播当某个输入发生变化时把一个计算后的值传播到另一个 scope。换句话说如果你发现自己在轮询一份状态、或者在每次写入后手动调用一串下游函数那么 Reactive State Pattern 就是更贴合 iii 设计意图的替代方案。模式结构三个角色原文档给出了清晰的三段式结构状态工作器持有状态真相source of truth——所有状态写入都经由它完成某个工作器中的函数负责处理变化——计算派生值、发送通知等状态工作器发布的触发器把函数与相关 key 或 scope 绑定——引擎在每次变化时触发它。上图来自仓库中的状态工作器参考文档 docs/0-11-0/workers/iii-state.mdx它完整展示了这一链路写入方调用state::set→ 引擎持久化到适配器 → 适配器回传 old new value → 引擎把变更交给触发器注册表匹配 → 命中后触发处理函数。状态工作器模式的真相源要使用该模式首先需要一个承载状态的工作器。在 iii 中内置状态工作器的触发器类型、配置形态与读写接口由其 Worker Docs 定义原文档特别指出了这一点。仓库内的权威参考是 docs/0-11-0/workers/iii-state.mdx它描述的是分布式 key-value 状态存储按 scope 组织、并带响应式触发器。启用与适配器配置状态工作器在配置中声明并选择持久化适配器- name: iii-state config: adapter: name: kv config: store_method: file_based file_path: ./data/state_store save_interval_ms: 5000配置项说明adapter状态持久化与分发的适配器不指定时默认kvadapter.name: kv内置 key-value 存储支持内存与文件两种持久化store_methodin_memory重启丢失或file_based持久化到磁盘file_path文件存储的目录路径每个 scope 存为一个独立文件save_interval_ms自动落盘间隔毫秒默认5000adapter.name: redis使用 Redis 作为后端需配置redis_urladapter.name: bridge通过 Bridge Client 把状态操作转发到远端 III Engine更早版本的 docs/0-10-0/how-to/react-to-state-changes.mdx 也给出了模块化配置形态modules::state::StateModuleKvStore适配器二者在功能上等价可根据你使用的版本选择。状态读写函数变化从哪来状态工作器暴露了六个内建函数它们是状态写入与读取的入口也是触发器事件产生的来源函数作用触发的事件state::set设置一个值value参数兼容data别名key 不存在时state:created已存在时state:updatedstate::update用一组操作原子地更新值同上取决于 key 是否存在state::delete删除一个值state:deletedstate::get读取一个值无不产生事件state::list列出某个 scope 内的所有值无state::list_groups列出所有包含状态数据的 scope无其中state::update支持set、merge、increment、decrement、append、remove六种操作详细参数与错误码见 docs/0-11-0/workers/iii-state.mdx是更新对象的一部分而不整段覆盖的推荐方式——它同样会触发state:created/state:updated事件因此同样能驱动响应式函数。state 触发器配置形态与匹配规则这是 Reactive State Pattern 的关键所在。状态工作器注册了名为state的触发器类型其配置由引擎侧的结构体StateTriggerConfig定义见 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, }三个字段的语义如下配置项类型语义scope可选字符串只在该 scope 内的状态变化时触发省略则匹配所有 scope精确匹配过滤器key可选字符串只在该 key 的状态变化时触发省略则匹配所有 keycondition_function_id可选字符串条件函数的 ID引擎会用状态事件调用它若返回false则不调用处理函数注意两点匹配规则scope/key均为精确匹配过滤器不是前缀或通配符早期文档docs/0-10-0/how-to/react-to-state-changes.mdx建议每个触发器监视一对scope/key需要监听多个 key 时分别为每个 key 注册函数与触发器而按 docs/0-11-0/workers/iii-state.mdx 的配置说明省略scope或key即可实现全 scope / 全 key级别的监听。你可以按需选择粒度。从引擎侧看state触发器类型是内置注册的在 engine/src/trigger.rs 中state Self::schema_for::StateTriggerConfig()为它提供了配置的 JSON Schemastate Self::schema_for::StateCallRequest()提供了事件负载call request的 Schema——这印证了触发器类型与配置形态由 Worker Docs 定义的说法。状态事件负载处理函数能拿到什么当触发器命中时处理函数收到一个状态事件对象。引擎侧由StateCallRequest与StateEventType定义engine/src/trigger_formats.rspub enum StateEventType { #[serde(rename state:created)] Created, #[serde(rename state:updated)] Updated, #[serde(rename state:deleted)] Deleted, } pub struct StateCallRequest { pub message_type: String, // 恒为 state pub event_type: StateEventType, pub scope: String, pub key: String, pub old_value: OptionValue, pub new_value: Value, }事件负载字段一览字段说明type恒为stateevent_type变化类型state:created/state:updated/state:deletedscope变化发生的 scopekey发生变化的 keyold_value变化前的值新建 key 时为nullnew_value变化后的值删除时为null值得注意的是old_value与new_value的成对出现使得响应式函数可以实现差异计算——比如比较新旧值决定是否真正需要处理这正是下面条件化执行的用武之地。完整实战注册函数 绑定 state 触发器下面是一个完整的响应式链路先注册处理函数再把state触发器绑定到它最后通过写入状态来自然触发。第 1 步注册处理函数函数体与普通函数完全一致——这是模式的核心承诺。以订单状态变化为例import { registerWorker, Logger } from iii-sdk const iii registerWorker(process.env.III_URL ?? ws://localhost:49134) iii.registerFunction({ id: orders::on-status-change }, async (event) { const logger new Logger() logger.info(Order status changed, { key: event.key, new_value: event.new_value }) // ...react to the change here... return { handled: true } })Python 与 Rust 版本在 SDK 参考与状态工作器文档中均有对应写法sdk/packages/node/iii/README.md 给出了registerFunction/registerTrigger的通用 API 形态。第 2 步绑定 state 触发器iii.registerTrigger({ type: state, function_id: orders::on-status-change, config: { scope: orders, key: status }, })第 3 步写入状态让引擎自动触发响应式模式下不需要显式调用处理函数只需要写状态// 从任意函数或工作器写入状态引擎会自动触发绑定的处理函数 await iii.trigger({ function_id: state::set, payload: { scope: orders, key: status, value: { orderId: order-123, status: shipped }, }, })当任何函数通过state::set或state::update写入被监视的scope/key时引擎会自动调用已注册的处理函数并传入新值。从引擎源码看这一写入即触发的行为是内建保证在 engine/src/invocation/mod.rs 的调用追踪注释中明确写道内建的 state/stream 写入会在一个派生的*_triggersspan 中触发触发器并且会把写入方的 trace context 附加到扇出的触发处理上——也就是说响应式扇出在可观测性上与你调用链的发起方保持关联例如审批通过 → 状态写入 → 触发turn::on_approval整条链路可以在同一 trace 中追踪。条件化执行用 condition_function_id 过滤事件Reactive State Pattern 中一个高频诉求是变化了但不是我关心的那种变化别处理。condition_function_id就是为此设计的引擎会用状态事件调用该条件函数只有返回true时才继续调用处理函数。示例只有当用户 profile 的email字段真的改变时才发送验证邮件完整三语言代码见 docs/0-11-0/workers/iii-state.mdx 的 Conditional Trigger 小节// 条件函数只关心 email 字段的变化 const conditionFn iii.registerFunction( { id: 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, }, })这个例子同时展示了该模式的两个最佳实践用event_type区分事件种类只在state:updated时处理忽略 create/delete用old_valuevsnew_value做差异判断字段级比较避免值没变也触发副作用。构建真实服务从状态模型到实时推送该模式是 iii 官方教程Reactive CRUDdocs/0-17-0/tutorials/reactive-crud/overview.mdx的地基。其思路正是 Reactive State Pattern 的延伸模型与存储把数据模型建在 iii 状态存储上state::set/state::update写入CRUD 端点通过 HTTP 触发器暴露 create/read/update/delete 函数实时订阅把存储的变化流式推送给连接的客户端——不用写任何 pub/sub 代码。教程特别点明了 Reactive State Pattern 与流stream的一个关键区别状态不向 WebSocket 客户端主动推送更新而是触发工作器侧的服务器端处理docs/0-11-0/workers/iii-state.mdx 的架构说明。因此如果你的目标消费方是服务器端的派生逻辑/通知用 state 触发器如果目标是浏览器客户端实时收到推送则应结合 stream 机制。关于顺序保证与突发变化的边界说明原文档的 TODO 中明确列出尚未展开的三个主题完整 worked example、scope vs key 匹配、顺序保证ordering guarantees与突发变化bursts of changes的处理。作为补充这里给出当前仓库可确认的事实边界scope vs key 匹配已由StateTriggerConfig与 Worker Docs 完整覆盖见上文第三节顺序保证从引擎源码看状态写入触发是派生 span 中的扇出触发engine/src/invocation/mod.rs即触发处理与写入是异步扇出的仓库当前没有公开文档承诺严格的全局执行顺序——如果你的业务依赖顺序建议自行在事件数据中携带版本/序号或使用state::update的原子操作减少中间态突发变化仓库当前同样没有内置节流/合并throttle/debounce机制推荐的两种做法是用condition_function_id做字段级过滤以跳过无关事件以及在处理函数内以读取最新值state::get为准进行最终计算从而把多次中间态压缩为一次最终态处理。参考资料模式定义docs/0-17-0/patterns/reactive-state-pattern.mdx状态工作器完整参考适配器、函数、触发器、事件负载、错误码docs/0-11-0/workers/iii-state.mdx从零配置的响应式示例docs/0-10-0/how-to/react-to-state-changes.mdx引擎触发器配置与事件 Schema 定义engine/src/trigger_formats.rs触发器类型注册与 Schema 分发engine/src/trigger.rs状态写入触发的调用追踪语义engine/src/invocation/mod.rsSDK 注册 APIsdk/packages/node/iii/README.md相关实战教程docs/0-17-0/tutorials/reactive-crud/overview.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),仅供参考