跳到主要内容

触发器体系与内置 worker

30 秒导读: 在 iii 里,一个 function 只写「做什么」;「什么时候被叫」由一条独立的 trigger 声明决定。声明里有个 trigger_type(如 http/cron/state),引擎按这个字段把声明交给拥有该能力的内置 worker;worker 在真实事件发生时把 function 叫起来。本章讲这套声明式触发怎么运转,以及引擎自带的那批能力模块(worker)长什么样。

本章聚焦「触发能力如何被声明、注册、归属」。至于这些 worker 是怎么被发现、能否运行时热插拔、观测,见 活的系统:发现、运行时扩展与可观测性;外部进程 worker 如何握手接入,见 接入引擎:SDK 与 worker 握手。三原语与线上协议见 三原语与线上协议,引擎中枢的注册表与分发见 引擎中枢:注册表与消息分发


1. 这是什么:声明式触发(零基础也能懂)

一句话定义: trigger(触发器)是一条数据——它说「当某个事件发生时,把某个 function 叫起来」。function 本身完全不关心自己是被 HTTP 请求、定时器、还是队列消息叫醒的。

为什么这么设计: 把「事件来源」从「业务逻辑」里拆出来。同一个 sendWelcome handler,今天挂在 POST /signup 上,明天改挂到队列 topic user.created 上——只改那条 trigger 声明,handler 一行不动。

一条 trigger 长这样(它就是个可序列化的结构,trigger.rs:174-183Trigger):

{
"id": "signup-http", // 这条声明的唯一 id
"trigger_type": "http", // 关键:交给哪种触发能力
"function_id": "auth.signup", // 事件发生时叫哪个 function
"config": { // 这种触发能力专属的配置
"api_path": "/signup",
"http_method": "POST"
},
"worker_id": null, // 由哪个 worker 提供(内置的填 None)
"metadata": null
}

一句话直觉: 把 trigger 想成「插座上的接线」——trigger_type 是插座的规格(两孔/三孔),config 是这根线的具体接法,function_id 是线另一头的电器。引擎负责:确认这种规格的插座真的装好了(对应的 worker 在跑),然后把线接上去。

注意 Trigger 的相等性只看 id(trigger.rs:186-196PartialEq/Hash 只哈希 self.id)——同一个 id 就是同一条声明,哪怕 config 变了。这让「按 id 覆盖/去重」变得干净。


2. 顶层全景:三个角色

理解整章只需分清三个角色,它们各管一件事:

角色白话职责源码符号
trigger type(触发类型)一种触发能力的自描述:id、配置长啥样、事件 payload 长啥样、由谁落地TriggerType(trigger.rs:61)
registrator(落地器)收到一条 trigger 声明后,真正去后端挂上钩子(开端口/建定时任务/订阅 topic…)TriggerRegistrator(trigger.rs:163)
worker(能力模块)引擎自带的一支模块,提供若干 trigger type + 它们的 registrator,并管好后端连接Worker(workers/traits.rs:53)

它们怎么串起来(从左到右是一条 trigger 声明被「激活」的路径,方向统一):

function 作者写下 引擎的中央注册表 拥有该能力的内置 worker
┌────────────────┐ register ┌───────────────────┐ 查 trigger_type ┌──────────────────┐
│ Trigger 声明 │ ───────────▶ │ TriggerRegistry │ ───────────────▶ │ TriggerType │
│ type=http │ _trigger │ · trigger_types │ │ + registrator │
│ fn=auth.signup │ │ · triggers │ └────────┬─────────┘
└────────────────┘ └───────────────────┘ │ register_trigger

┌──────────────────────────────┐
│ worker(如 iii-http) │
│ └─ adapter(可插拔后端) │
│ 开端口 / 挂路由 / 订阅… │
└──────────────────────────────┘

怎么读这张图: 左边是「声明」,中间 TriggerRegistry 是撮合中枢——它按 trigger_type 找到右边那个已注册的 TriggerType,调用它携带的 registrator,由 registrator 通过 worker 的 adapter 把钩子挂到真实后端。注册表只做撮合,真正干活的是 worker 的 registrator。

先记住一个反直觉的点:worker 不是在启动时才把自己的 trigger type「安装」进注册表——两边可以任意先后到达,注册表负责补齐。下节详解。


3. 注册表:TriggerRegistry

这节讲中枢 TriggerRegistry(trigger.rs:198-360)。它只有两张并发字典:

字段存什么
trigger_types已注册的触发能力(worker 提供)trigger type id("http"…)
triggers已注册的触发声明(function 作者提供)trigger id("signup-http"…)

两者都是 DashMap(无锁并发字典),因为注册/注销随时可能从不同 worker 并发发生。

3.1 后到的能力会「回放」先到的声明(late binding)

register_trigger_type(trigger.rs:253-286)不只是插一条能力——它先扫一遍已有的 triggers,把所有等这种 type 的声明补挂上去:

// trigger.rs:265-283(节选)—— 新能力到达时,回放所有等它的旧声明
let matching_triggers: Vec<Trigger> = self.triggers.iter()
.filter(|pair| pair.value().trigger_type == trigger_type_id)
.map(|pair| pair.value().clone()).collect();
for trigger in matching_triggers {
let result = trigger_type.registrator.register_trigger(trigger.clone()).await;
// 单条失败只记日志,不阻断其余
}
self.trigger_types.insert(trigger_type.id.clone(), trigger_type);

为什么重要: 一条 type=http 的声明可以先于 iii-http worker 到达(比如声明来自配置,worker 稍后才启动)。声明先躺在 triggers 里,等 worker 一注册 http 能力,这些待定声明立刻被激活。测试 test_trigger_registry_register_type_auto_registers_existing_triggers(trigger.rs:588-616)锁的就是这条行为。

引擎侧的包装 Engine::register_trigger_type(engine/mod.rs:1844-1859)在此之上加了幂等:同名 type 已存在就 warn 并直接返回,不重复注册。

3.2 声明到达:查表、落地、或给出可操作的报错

register_trigger(trigger.rs:288-330)是热路径。它先按 trigger_type 查能力表,查不到时的报错分三档,这正是本章最实用的一处设计:

查 trigger_types[type] 命中?

┌──────────────┴───────────────┐
命中 未命中
│ │
registrator 是内置 type 吗?(查静态表)
.register_trigger ┌──────┴───────┐
│ 是 否
成功→存入 triggers UnknownBuiltin Unknown
失败→Error(不存) 「iii worker add X」 「去 workers.iii.dev 找」

对应 RegisterTriggerError(trigger.rs:44-59)三个变体:

  • UnknownBuiltin —— 这个 type 是引擎认识的内置能力,但对应 worker 没启用。报错直接给出修复命令:Run: iii worker add {worker}
  • Unknown —— 完全不认识的 type,提示去 https://workers.iii.dev/ 找提供它的 worker。
  • Other —— registrator 自己落地失败(如端口占用),原样透传。

注意热路径的事务性:只有 registrator 成功挂钩后,声明才被存进 triggers(trigger.rs:314-327)。落地失败就不留痕,测试 test_register_trigger_propagates_registrator_error(trigger.rs:697-717)验证了「失败时 triggers 里查不到这条」。

3.3 注销:单条与整 worker

  • unregister_trigger(trigger.rs:332-359):按 id 找到声明,调 registrator 的 unregister_trigger 摘钩,再从表里删。
  • unregister_worker(trigger.rs:212-251):一个 worker 掉线时,把它名下所有 triggers 和 trigger_types 一并清掉——按 worker_id == Some(id) 过滤。摘钩失败只记日志、继续清剩下的(测试 test_unregister_worker_continues_after_registrator_error,trigger.rs:748-770),保证一条坏 trigger 不会卡住整批清理。

4. TriggerType:一种触发能力的自描述

这节讲能力本身的结构 TriggerType(trigger.rs:61-161)。它是「这种触发怎么用」的完整说明书:

字段含义
idtype 名("http")
registrator落地器(装箱的 dyn TriggerRegistrator)
worker_id由哪个 worker 提供;内置在进程内的一律 None
trigger_request_format声明时 config 该长啥样(JSON Schema)
call_request_format事件触发时,function 收到的 payload 长啥样
call_response_format绑定的 handler 必须返回啥(只有少数 type 有约束)

4.1 三张 schema 按 id 自动填充

关键巧思在 TriggerType::new(trigger.rs:71-91):构造时就按 idtrigger_formats自动查出三张 schema——worker 注册能力时通常一行 schema 都不用写。分派逻辑是三个按 id 的 match:trigger_request_format_for(trigger.rs:115-131)、call_request_format_for(trigger.rs:133-147)、call_response_format_for(trigger.rs:153-160)。

需要覆盖时才用链式 builder:with_trigger_request_format::<T>() / with_call_request_format::<T>() / with_call_response_format::<T>()(trigger.rs:93-109),内部走 schemars::schema_for!(T) 生成 JSON Schema。

4.2 举例:HTTP 的「返回契约」

多数 type 对返回值不设约束(call_response_formatNone)。唯一的例外是 http:它要求 handler 返回一个响应信封 HttpCallResponse(trigger_formats.rs:70-82):

// trigger_formats.rs:70-82(节选)—— HTTP handler 的返回契约,三个字段全可选
pub struct HttpCallResponse {
pub status_code: Option<u16>, // 省略则默认 200
pub headers: Option<HttpResponseHeaders>, // map 或 "K: V" 数组两种写法都收
pub body: Option<Value>, // 省略则默认 {}
}

「三个字段全可选」这条被测试钉死:http_trigger_type_populates_call_response_format + assert_http_call_response_properties(trigger.rs:874-906)断言 required 数组里不许出现这三者。这样 handler 只 return { body } 也能工作,状态码自动补 200。


5. builtin_trigger_type_owner:静态归属表与「iii worker add X」

上一节说内置 trigger type 的 worker_id 都是 None——这带来一个问题:注册表无法用 Uuid 反查是哪个 worker 提供了它。iii 用一张编译期静态表兜底。

BUILTIN_TRIGGER_TYPES(trigger.rs:16-28)把每个内置 type id 映射到「拥有它的 worker 名」:

trigger type id拥有它的 worker干什么
httpiii-httpHTTP 端点触发
croniii-cron定时触发
subscribeiii-pubsub订阅 topic 广播
stateiii-stateKV 状态变更触发
durable:subscriberiii-queue持久队列消费
stream / stream:join / stream:leaveiii-stream流数据/加入/离开
log / traceiii-observability日志/链路事件触发
configurationconfiguration配置变更触发

builtin_trigger_type_owner(id)(trigger.rs:37-42)就是这张表的查询函数。它有两个用途(见 trigger.rs:30-36 的文档注释):

  1. 报错——§3.2 里 register_trigger 查不到能力时,用它判断「是内置但没启用」并给出 iii worker add {worker} 提示(trigger.rs:291-302)。
  2. 发现——engine_fn 把各 trigger type 归拢进「拥有它的 worker」的 workers::info 信封里。这属于发现机制,详见 05 章

表里的字符串就是报错文案里那个可复制的命令。用户看到 iii worker add iii-http 能直接照抄——把「缺哪个 worker」变成一条可执行修复。测试 test_trigger_registry_register_trigger_missing_builtin_worker(trigger.rs:545-557)锁死这段文案。至于 iii worker add 如何真的把 worker 拉起来,属于运行时扩展,见 05 章


6. trigger_formats:每种触发的 config 与 payload

trigger_formats.rs 是所有内置 type 的「说明书数据源」——每个 type 在这里定义配置结构(注册时填)和调用结构(触发时收)。所有结构体都 derive(JsonSchema),引擎据此自动生成 Schema(见 §4.1)。

一张表把它们对齐(逐格可回源码核对):

trigger typeconfig 结构(注册时)payload 结构(触发时)定义位置
httpHttpTriggerConfig(api_path/http_method/条件)HttpCallRequest(query/path/headers/body/method)trigger_formats.rs:24-64
cronCronTriggerConfig(6 段表达式 expression)CronCallRequest(job_id/scheduled_time…)trigger_formats.rs:98-116
durable:subscriberQueueTriggerConfig(topic/queue_config)动态(发布的消息体,无固定结构)trigger_formats.rs:120-130
subscribeSubscribeTriggerConfig(topic)动态(发布的事件体)trigger_formats.rs:134-142
stateStateTriggerConfig(scope/key 精确过滤)StateCallRequest(event_type/old_value/new_value)trigger_formats.rs:146-181
streamStreamTriggerConfig(stream_name/group_id/item_id)StreamCallRequest(event_type/event…)trigger_formats.rs:193-233
stream:join/leaveStreamJoinLeaveTriggerConfigStreamJoinLeaveCallRequest(peer 加入/离开)trigger_formats.rs:185-217
logLogTriggerConfig(level 阈值)LogCallRequest(OTel 日志记录字段)trigger_formats.rs:282-333
traceTraceTriggerConfig(service_name/status 过滤)TraceCallRequest(受影响的 trace_ids)trigger_formats.rs:337-358
configurationConfigurationTriggerConfig(configuration_id/event_types)ConfigurationCallRequest(old_value/new_value/schema)trigger_formats.rs:237-278

两个值得记住的细节:

  • 多数 config 都带 condition_function_id——注册前先跑一个条件 function,返回真才真正触发。这是内置的「守卫」机制。
  • trace 的 payload 是「合并节拍」而非「逐条 span」——TraceCallRequest 只带一批「有活动的 trace id」(trigger_formats.rs:349-358),提示 handler「有变化,自己去 engine::traces::* 重拉」,避免高频 span 把 handler 冲垮。

7. 内置 worker 概览:统一的三步套路

引擎自带一批 worker,每个 worker 提供上表里的一或多种触发能力。它们文件散在 engine/src/workers/ 下,但套路高度一致。

7.1 每个内置 worker 都做同样三件事

以最简的 pubsub 为例(workers/pubsub/pubsub.rs):

// ① initialize() 里注册自己的 trigger type,registrator 就是 worker 自己
// pubsub.rs:215-222
let trigger_type = TriggerType::new(
SUBSCRIBE_TRIGGER_TYPE, // "subscribe"
"Subscribe to a topic",
Box::new(self.clone()), // ← worker 自身实现 TriggerRegistrator
None, // 内置 → worker_id = None(见 §5)
);
let _ = self.engine.register_trigger_type(trigger_type).await;

// ② impl TriggerRegistrator for PubSubWorker(pubsub.rs:110)—— 落地逻辑
// ③ register_worker! 向引擎宣告这个 worker 的存在(pubsub.rs:580)

三步分别是:

  1. initialize()register_trigger_type(TriggerType::new(id, 描述, Box::new(self.clone()), None))——把能力挂进注册表,registrator 通常就是 worker 自身。
  2. impl TriggerRegistrator for XxxWorker——实现 register_trigger/unregister_trigger,通过 adapter 把钩子挂到后端。
  3. register_worker!(...)——在文件底部声明式地把 worker 登记进全局清单(下节讲宏)。

7.2 全景表

worker(register 名)提供的 trigger type落地做什么默认?关键文件
iii-httphttp开 HTTP server、挂路由;handler 返回 HttpCallResponse默认开rest_api/api_core.rs:132, 注册 :920
iii-croncron按 cron 表达式起定时任务,到点触发默认开cron/cron.rs:273, :491
iii-pubsubsubscribe订阅 topic,广播事件到订阅方默认开pubsub/pubsub.rs:215, :580
iii-statestate监听 KV 变更(created/updated/deleted)默认开state/state.rs:192, :763
iii-queuedurable:subscriber持久队列消费,带重试与死信默认开queue/queue.rs:1096, :1279
iii-streamstream,stream:join,stream:leave实时流订阅 + peer 加入/离开默认开stream/stream.rs:220-248, :1425
iii-observabilitylog,traceOTel 日志/链路/指标/告警;日志或 span 落地触发强制observability/mod.rs:3020-3038, :3349
configurationconfiguration存/取/监听「带类型的配置值」强制configuration/configuration.rs:80, :456
iii-http-functions(无 trigger type)把配置里的外部 HTTP 端点暴露成可调 function手动http_functions/mod.rs:177

两点注意:

  • iii-observabilityconfigurationmandatory(register_worker!mandatory 档,见下节)——它们是引擎运转的地基,不能关。
  • iii-http-functions 不提供任何 trigger type——它是反过来的:把外部 HTTP 端点包成 function 供别人调,initialize() 只注册了一个 service(http_functions/mod.rs:168-174),不碰触发注册表。它说明「worker」这个抽象比「触发能力提供者」更宽。

7.3 iii-stream 一个 worker、三种能力

多数 worker 一对一,但 iii-streaminitialize() 里连注册三次 TriggerType::new(stream/stream.rs:220-248):stream:joinstream:leavestream,三者共享同一个 StreamWorker 作为 registrator(stream/trigger.rs:68)。这也是为什么 §5 的静态表里三个 id 都指向同一个 iii-stream


8. Worker / ConfigurableWorker / adapter:一个 worker 的骨架

前面反复出现「worker」和「adapter」。这节把 workers/traits.rs 里的抽象讲清楚——它决定了内置 worker 如何做到「同一套逻辑、可换后端」。

8.1 Worker:生命周期契约

Worker trait(traits.rs:53-105)是每个 worker 的最小契约:

方法干什么默认
name()worker 名必填
create(engine, config)工厂:从配置造出 worker必填
initialize()注册 trigger type / service(见 §7.1)必填
start_background_tasks()起后台循环(定时器、消费循环…)空实现
destroy()优雅关停记日志
is_alive()报告后端是否还活着true

is_alive() 的默认 true 有讲究(traits.rs:83-94):内置进程内 worker 只要引擎活着就算活;但追踪进程外状态的 worker(如 detached VM)要覆盖它——reloader 靠它发现「VM 悄悄死了」并在下次 reload 时把该 worker 从 unchanged 提升为 changed 强制重启。这条与运行时可靠性相关,细节见 05 章

8.2 ConfigurableWorker:把「后端」抽成可插拔 adapter

大多数内置 worker 还实现 ConfigurableWorker(traits.rs:116-253)——它引入 adapter:同一个 worker 的逻辑不变,底层存储/传输可换。看关联类型就懂它的形状:

// traits.rs:117-121(节选)
type Config: DeserializeOwned + Default + Send; // 这个 worker 的配置结构
type Adapter: ?Sized; // 后端接口(如 dyn QueueAdapter)
type AdapterRegistration: ... + inventory::Collect;
const DEFAULT_ADAPTER_NAME: &'static str; // 不指定就用这个后端

配置里选后端靠一个统一的小结构 AdapterEntry(traits.rs:23-36):{ name, config? }——name 选哪个 adapter,config 是该 adapter 的专属参数(free-form,故意不约束 schema,好保持可插拔)。

各 worker 的可插拔后端(register_adapter! 登记 + DEFAULT_ADAPTER_NAME 兜底):

worker默认 adapter其它可选后端
iii-queuebuiltin(进程内)memory / redis / rabbitmq / bridge
iii-statekv(进程内 KV)redis / bridge
iii-streamkv_storeredis / bridge
iii-pubsublocalmemory / redis
iii-cronkvredis

例:iii-state 默认用进程内 kv(state/state.rs:209),生产上把配置改成 { "adapter": { "name": "redis" } } 就换成 Redis 后端,StateWorker 的触发逻辑一行不改。各 adapter 用 register_adapter!(<StateAdapterRegistration> name: "redis", make_adapter)(state/adapters/redis_adapter.rs:257)通过 inventory 在编译期自登记。

8.3 create_with_adapters:五步组装

create_with_adapters(traits.rs:206-252)是把配置变成活 worker 的通用装配线,五步清晰:

① 解析 config → Self::Config(缺省则 default)
② 选 adapter 名 → 配置里指定的,否则 DEFAULT_ADAPTER_NAME
③ 取 factory → 从 inventory 收集来的注册表里查;查不到 → 报错并列出可用项
④ 造 adapter → factory(engine, adapter_config).await
⑤ 组装 worker → Self::build(engine, config, adapter)

第 ③ 步的报错很友好:找不到 adapter 时把「可用 adapter 列表」一并抛出(traits.rs:228-238),测试 add_adapter_and_missing_adapter_error_are_reported(traits.rs:716-750)验证报错里同时含缺失名与可用名。整条链的 adapter 清单从哪来?build_registry()(traits.rs:133-142)遍历 inventory::iter::<Self::AdapterRegistration> 收集所有 register_adapter! 登记的项——这就是「编译期声明、运行期成表」。

8.4 register_worker!:声明式登记 worker

worker 自己也用同样的 inventory 套路登记。register_worker! 宏(workers/registry.rs:85-121)有三档:

写法语义
register_worker!(name, T, description = "...")存在但默认不启用,需手动 add
... enabled_by_default = true默认启用
... mandatory强制、默认启用、不可关(如 observability/configuration)

宏展开成一条 inventory::submit!WorkerRegistration(registry.rs:75-83,含 name/description/factory/is_default/mandatory)。description 会浮现在发现接口里——「这个 worker 是干嘛的」对人和 LLM 都可读。这批登记项如何被引擎收集成清单、并驱动 iii worker add,属于发现与运行时扩展,见 05 章


9. 巧妙之处(可借鉴)

  • 报错即修复指令。 RegisterTriggerError::UnknownBuiltin 不只说「缺 worker」,而是把 iii worker add {worker} 这条可复制命令塞进文案(trigger.rs:44-52)。静态表 BUILTIN_TRIGGER_TYPES 的第二列就是命令的一部分——归属信息与修复动作是同一份数据。

  • 能力与声明解耦、且可乱序到达。 register_trigger_type 回放待定 triggers(trigger.rs:265-283),让「声明先于 worker」也能最终一致。配置驱动的系统里,这消除了启动顺序的脆弱性。

  • schema 跟着 id 走,worker 零样板。 TriggerType::new 按 id 自动填三张 schema(trigger.rs:79-81),worker 注册能力时不用手写 schema;要定制才用 with_* builder。约定优先、可覆盖。

  • 一个 AdapterEntry 统一「换后端」。 所有 ConfigurableWorker 用同一个 {name, config?} 结构选后端(traits.rs:23-36),config 故意不设 schema 以保持后端可插拔;skip_serializing_if 避免 config: null 破坏 adapter 自己的 oneOf 校验——一个小 serde 细节保住了可插拔性。

  • 事务性注册。 只有 registrator 落地成功,声明才进表(trigger.rs:314-327);批量注销时单条失败不阻断其余(trigger.rs:238-241)。热路径与清理路径都不会因一条坏数据卡死。


10. 边界与局限

  • 内置 type 的归属是编译期硬编码。 BUILTIN_TRIGGER_TYPES(trigger.rs:16-28)是静态数组——新增内置能力必须改这张表并重编译。外部 worker 提供的 type 靠 worker_id(Uuid)归属,走另一条路(见 06 章),不进这张静态表。

  • Trigger 只按 id 判等。 两条 id 相同、config 不同的声明会被视为同一条并相互覆盖(trigger.rs:186-196)——id 唯一性由调用方负责,注册表不校验 config 冲突。

  • 能力缺失只在「注册声明时」才暴露。 一条 type=http 的声明若 iii-http 未启用,要到 register_trigger 那一刻才报 UnknownBuiltin;注册表不预先校验「所有声明的 type 是否都有 worker」。

  • 本章不覆盖运行时演化。 worker 如何被发现、iii worker add 如何真正拉起 worker、掉线如何重启、如何观测——都在 05 章;外部进程 worker 如何握手注册自己的 trigger type,在 06 章


11. 代码地图(导航索引)

主题文件关键符号
内置 type → worker 归属表engine/src/trigger.rsBUILTIN_TRIGGER_TYPES, builtin_trigger_type_owner
注册报错三档(含 add 提示)engine/src/trigger.rsRegisterTriggerError(UnknownBuiltin/Unknown/Other)
触发能力自描述 + schema 自填engine/src/trigger.rsTriggerType::new, *_format_for, with_call_response_format
落地器契约engine/src/trigger.rsTriggerRegistrator(register_trigger/unregister_trigger)
声明结构(id 判等)engine/src/trigger.rsTrigger, impl PartialEq/Hash for Trigger
中央注册表engine/src/trigger.rsTriggerRegistry(register_trigger_type/register_trigger/unregister_trigger/unregister_worker)
引擎侧幂等包装engine/src/engine/mod.rsEngine::register_trigger_type(:1844)
各 type 的 config/payload schemaengine/src/trigger_formats.rsHttpTriggerConfig, HttpCallResponse, StateCallRequest, TraceCallRequest
worker 生命周期契约engine/src/workers/traits.rsWorker(initialize/is_alive/destroy)
可插拔后端 + 装配线engine/src/workers/traits.rsConfigurableWorker, AdapterEntry, create_with_adapters, build_registry
worker/adapter 登记宏engine/src/workers/registry.rsregister_worker!, register_adapter!, WorkerRegistration, AdapterRegistration
iii-http 提供 httpengine/src/workers/rest_api/api_core.rsHttpWorker::initialize(:132), register_worker!(:920)
iii-queue 提供 durable:subscriberengine/src/workers/queue/queue.rsQueueWorker::initialize(:1091), impl TriggerRegistrator(:624)
iii-state 提供 stateengine/src/workers/state/state.rs + state/trigger.rsStateWorker, impl TriggerRegistrator for StateWorker
iii-stream 提供三种流 typeengine/src/workers/stream/stream.rs + stream/trigger.rsJOIN_TRIGGER_TYPE/LEAVE_TRIGGER_TYPE/STREAM_TRIGGER_TYPE
iii-observability 提供 log/traceengine/src/workers/observability/mod.rsLOG_TRIGGER_TYPE/TRACE_TRIGGER_TYPE, initialize(:3020)
configuration 提供 configurationengine/src/workers/configuration/configuration.rsConfigurationWorker(:80, :456)
iii-http-functions(无 type,注册 service)engine/src/workers/http_functions/mod.rsHttpFunctionsWorker::initialize(:168)