其他
在 VTEX IO 服务中设计异步事件与后台任务时…
当 VTEX IO 应用需要通过事件、worker 或后台处理执行异步工作时,用这套做法保证幂等、可重试且失败可追踪。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 38/100 ×0.15
适用场景
在 VTEX IO 服务中设计异步事件与特定
解决思路
- 当工作昂贵、易重试或不需要在请求-响应周期内完成时,使用 events 或 workers。
- VTEX 使用内部 event broadcaster 将平台和应用事件投递到你的服务,同一 broadcaster 也能把你应用发布的事件路由到其他 handler;双向都按 at-least-once 语义处理,事件可能被重试或重放,因此 handler 必须幂等且能安全应对重复。
- 保持事件 handler 幂等:同一事件可能被投递多次,handler 必须能安全容忍重放。
- 把幂等和处理状态持久化到合适的存储中,例如用 VBase 存 keyed 标记、用 Master Data 存结构化记录,以便 handler 在重试间检测重复、已完成工作和失败。
- 在 `service.json` 中显式声明 events 和 workers,使其接入 IO runtime,并保持其输入契约稳定、明确,不要依赖 HTTP 路由假设。
- 需要通知其他应用或扇出工作时,通过合适的 VTEX IO client 或事件机制发布事件,而不是为了模拟异步投递而临时创建 HTTP 回调。
- 要通过 VTEX IO event bus 发布事件,应用通常需要在 `manifest.json` 中声明 `colossus-fire-event` policy;仅当应用确实也消费那些受保护资源时才添加其他 policy。
- 当 handler 超出一个小而清晰的工作单元时,将事件摄入与业务编排分离。
- 把重试视为预期行为而非异常行为,设计 handler 使重复执行是安全的。
- 让后台 handler 明确其副作用,例如写入、外部调用或状态转换。
- 对于面向批处理的 handler,以小的、明确的单元处理条目,并记录每个条目的状态,使单个失败元素不会掩盖批次其余部分的进度。
- 事件 handler 必须容忍重复执行而不产生不一致的副作用:如果 handler 执行写入或外部副作用却没有检查工作是否已完成,应停止并先添加幂等保护。
- 正确做法示例:先通过 `ctx.clients.statusStore.hasProcessed(orderId)` 检查是否已处理,已处理则直接 return,否则调用 `ctx.clients.partnerApi.sendOrder(orderId)` 并 `markProcessed(orderId)`。
- 错误做法示例:直接 `await ctx.clients.partnerApi.sendOrder(ctx.body.orderId)` 而不做幂等检查。
- Workers 和事件 handler 不得依赖仅 HTTP 才有的假设,例如路由参数、即时用户交互或绑定请求的可变状态:如果事件 handler 期望请求头、路由参数或路由特定状态形状,应停止并重新设计输入契约,使 handler 接收显式的异步数据。
- 正确做法示例:从 `ctx.body` 解构 `importId` 和 `account`,再调用 `ctx.clients.importApi.process(importId, account)`。
- 错误做法示例:使用 `ctx.vtex.route.params.id` 和 `ctx.request.header.account`。
- 异步 handler 必须通过状态、日志或持久标记让部分失败可见,而不是静默吞掉:如果 handler 捕获错误却不记录失败状态、不记录足够上下文或不在适当时重新抛出,应停止并让失败处理显式化。
- 正确做法示例:try 中调用 `ctx.clients.partnerApi.syncCatalog(ctx.body.catalogId)` 后 `markSuccess`,catch 中 `markFailure` 并 `throw error`。
- 错误做法示例:catch 中直接 `return` 吞掉错误。
- 推荐文件布局:`node/events/index.ts`、`node/events/catalog.ts`、`node/events/orders.ts`、`node/workers/sync.ts`。
- 最小异步 handler 模式:从 `ctx.body` 取 `skuId`,用 `ctx.clients.syncState.isProcessed(skuId)` 检查,已处理则 return,否则 `ctx.clients.catalogSync.syncSku(skuId)` 后 `markProcessed(skuId)`。
- 事件发布模式示例:从 `ctx` 取 `clients.events` 和 `body.payload`、`senderAppId`、`clientAppId`,遍历 payload 逐行调用 `events.sendEvent(clientAppId, 'my-app.event-name', { data: row, senderAppId })`,最后 `await next()`。
- 事件发布的最小 manifest policy:在 `policies` 中声明 `{ "name": "colossus-fire-event" }`。
- 用路由确认或触发工作,用 events 或 workers 执行缓慢、可重复且能感知失败的处理。
- 有意识地使用存储管理异步状态:VBase 用于按外部标识符键控的简单幂等标记,Master Data 用于带状态和时间戳的结构化处理记录。
- 把异步 payload 当作独立契约,不要复用仅属于 HTTP 请求的路由假设。
- 对于扇出或跨应用通知,发布包含 ID 和最小元数据的小而明确的事件,让下游 handler 在需要时从源头获取完整详情,而不是嵌入大 payload 或依赖自定义回调 URL。
- 开发时使用 Broadcaster 应用在 Admin 中的 `Notify Target Workspace` 设置将事件路由到特定 workspace,而不是发明临时的公共路由或仅测试用的投递流程;handler 仍应无论哪个 workspace 接收事件都正确运行。
适用边界 · 注意事项
- 不要用于定义 HTTP 路由契约。
- 不要用于设计 GraphQL schema 或 resolver。
- 不要用于决定应用级 policy 或底层 client 构造。