事件总线
事件总线让空间内的应用把数据变更和业务动作变成事件,并让任务订阅这些事件,从而在事情发生时自动运行。任务不再只能定时或手动运行,而是可以因事件而运行——一笔交易完成、一位会员被更新、一篇内容被发布——并在运行时读取该事件的数据。
这让你的空间变得事件驱动:一个事件可以扇出到任意数量的任务,跨越一个或多个已安装的应用。
概念
| 术语 | 含义 |
|---|---|
| 事件(Event) | 一件已发生的、有名字的事,例如 transaction.completed,携带一个 JSON 载荷(payload)(如 member_id、amount)。 |
| 已注册事件(Registered event) | 已安装应用声明它会发出的事件,连同其载荷必须符合的 schema。应用安装时自动注册。 |
| 订阅(Subscription) | 已注册事件与任务之间的绑定。事件触发时,被订阅的任务随之触发。订阅可带可选的过滤器。 |
| 信封(Envelope) | 一次发射的持久化记录——载荷、状态,以及命中了哪些订阅。它是事件日志与重放的基础。 |
访问入口
打开控制台,点击侧边栏的 事件总线(Event Bus)(/event-bus)。页面包含三个页签:
- 目录(Catalog) —— 已安装应用注册的全部事件,按应用分组,并展示每个事件的载荷 schema。
- 订阅(Subscriptions) —— 事件与任务之间的绑定;在此创建、启用/停用、设置过滤器或删除。
- 日志(Log) —— 已发射事件的审计流,含状态,并可重放单条事件。
仪表盘上的 事件洞察(Event Insights) 小组件会汇总近期活动(总量、成功率、热门事件)。
工作原理
1. 发射 Emit 应用发出事件(例如写入一笔交易时)。
2. 校验 Validate 载荷依据事件已注册的 schema 进行校验。
3. 记录 Record 存储一条信封(审计 + 幂等 + 重放)。
4. 派发 Dispatch 匹配该事件的所有启用订阅。
5. 触发 Trigger 每个被订阅的任务随即运行,并附带事件数据。
每个被订阅的任务都走常规的任务执行路径,因此你已经熟悉的任务运行机制(执行历史、日志、并发限制)原样适用。
事件从何而来
你无需手动创建已注册事件——它们来自已安装的应用:
- 在数据写入时自动产生。 应用可以声明某次数据写入会发出一个事件。例如记录一笔交易会发出
transaction.completed,并以新行的字段作为载荷。无需任何额外操作,安装应用即让该事件可用。 - 由应用显式发出。 应用也可以直接发出事件(用于非数据类动作),通过平台的发射 API。
应用安装时,它声明的事件会出现在 目录 中;它自带的默认订阅(应用自己的任务响应自己的事件)也会为你创建。卸载或升级应用会保留你自己创建的订阅。
把任务订阅到事件
在 订阅 页签:
- 点击 新建订阅。
- 选择 事件(来自目录)与要触发的 任务。
- 可选地添加 过滤器,使任务仅在载荷匹配时运行。
- 保存。订阅立即生效。
你可以临时停用订阅(切换 启用 开关)而无需删除。
EVENT 类型任务会被触发任务仅在 启用(active) 状态下才会被事件触发。运行时使用任务所有者的身份,与定时任务一致。
过滤器
过滤器是对事件载荷的简单匹配。过滤器中的每个字段都必须等于载荷中的对应值;当过滤值为数组时,表示「载荷值是数组成员之一」即匹配。
// 仅当会员等级为 VIC 时运行
{ "tier": "VIC" }
// 任一门店即运行
{ "store_id": ["S001", "S002"] }
若不设置过滤器,任务会在该事件每次发生时都运行。
在任务中使用事件数据
当任务由事件触发时,事件会作为 trigger 附加到这次运行上。在工作流的自定义代码节点中,你可以从执行上下文读取它:
async execute(inputs, context) {
const trigger = context.execution?.inputs?.trigger;
const event = trigger?.event; // 例如 "transaction.completed"
const memberId = trigger?.payload?.member_id;
const amount = trigger?.payload?.amount;
const source = trigger?.source; // "datasource" | "app" | "system"
}
trigger 对象包含:
| 字段 | 说明 |
|---|---|
event | 事件名 |
payload | 事件数据 |
source | 来源——数据写入(datasource)、应用(app)或系统(system) |
id | 信封 id(也显示在 日志 中) |
emittedAt | 事件发射时间 |
spaceId、actorUserId、causationId、depth | 来源与防环元数据 |
手动或定时触发的任务没有 trigger,因此现有任务不受影响。
事件日志与重放
日志 页签列出每一次发射的事件(信封),含:
- 状态 ——
DISPATCHED(已匹配并触发)、PENDING(待派发)、SKIPPED(例如被级联深度防护拦截)或FAILED。 - 事件名、载荷,以及 命中的订阅数。
你可以按状态或事件名过滤,并 重放(Replay) 单条信封以再次派发——这在修复任务后针对历史事件重新运行时很有用。重放会重新触发匹配的订阅;请将任务设计为幂等,使重放与重试都安全。
可靠性
事件总线在设计上不丢事件、也不重复触发:
- 持久且校验。 每次发射在派发前都会被记录,载荷会依据已注册 schema 校验——非法载荷被拒绝。
- 幂等。 使用相同幂等键的重复发射、以及被重投的消息,不会让任务触发两次。
- 故障不丢失。 在消息系统不可用期间发射的事件会被存储,并在其恢复后自动重新派发。
- 防环。 若某事件触发的任务又发出同一事件,深度防护会阻止失控循环(此类事件被标记为
SKIPPED)。 - 空间隔离。 任务只能订阅本空间的事件。
从你自己的应用发射事件
如果你在开发 GeniApp,可以显式发射事件。先在应用的事件清单中声明事件(名称 + 载荷 schema),使其在安装时被注册,然后发射:
POST /api/events/emit
Authorization: Bearer <应用 system api key>
Content-Type: application/json
{
"appIdentifier": "retail_crm",
"event": "transaction.completed",
"payload": { "member_id": "...", "amount": 199.0, "store_id": "S001" },
"idempotencyKey": "order-12345"
}
平台会校验应用已安装、事件已注册、载荷符合 schema,然后记录信封并派发。请提供 idempotencyKey(例如订单号),使重试不会发出重复事件。
JavaScript SDK 提供 emitManagedEvent(...) 作为该端点的轻量封装——详见开发者文档。