← [ BACK_TO_NOTES ]

事件驱动架构

解释系统如何通过已经发生的事实事件解耦生产者与消费者,并区分 Event、Command、Queue、Pub/Sub、Event Log 和 UI/Runtime 流式事件的不同语义。

#pattern #concept #evergreen #architecture

[!info] related notes

事件驱动架构

[!abstract] 一句话定义 事件驱动架构让组件通过“已经发生的事实”异步协作。Producer 发布 Event,Broker / Event Bus 传递或保存,Consumer 独立响应;它降低时间和部署耦合,但引入最终一致性、重复、顺序、Schema 和跨系统排障成本。

Command、Event 与 Event Log

Command
→ “请执行某动作”
→ 可能被拒绝

Event
→ “某事实已经发生”
→ 使用过去式,原则上不可撤回

Event Log
→ 按顺序保留事件历史
→ 支持 Replay 和独立消费进度

例如:

CreateOrder       → Command
OrderCreated      → Event
order-events topic→ Event Log

核心组件

Producer
→ Event Contract
→ Broker / Channel / Log
→ Consumer
→ Local Business Effect

事件契约至少应明确:Event ID、Type、Version、Occurred At、Producer、Aggregate / Entity ID、Payload 和必要的 Trace / Tenant 信息。

为什么能降低耦合

同步调用要求双方同时在线,并让调用方知道下游地址和响应。事件模式让 Producer 只发布事实:

Order Service emits OrderCreated
├─ Inventory reserves stock
├─ Notification sends message
└─ Analytics updates metrics

新增 Consumer 不必修改 Producer 主流程。

但它只是降低耦合,不是消除依赖:大家仍依赖 Event Schema、语义和可用性。

事件驱动的五类代价

1. Eventual Consistency

不同 Consumer 在不同时间完成,本地状态会暂时不一致。产品要明确“处理中”状态和可接受延迟。

2. Duplicate Delivery

ACK、Offset 或网络故障会导致重复投递。Consumer 必须幂等,见 幂等消费者。

3. Ordering

Broker 顺序边界、并发消费和 Retry 路径都可能改变业务效果顺序。常用 Entity Key + Version / State Machine 防止旧事件覆盖新状态。

4. Schema Evolution

Producer 和 Consumer 不能总同步发布。优先向后兼容新增字段、明确版本和弃用窗口,避免随意改变字段语义。

5. Observability

一个请求可能变成跨分钟、跨多个 Consumer 的事件链。必须传播 Correlation / Trace ID,并观察 Lag、Retry、DLQ 和最终业务结果。

Event Notification 与 Event-Carried State

Event Notification

OrderCreated { order_id }

Consumer 再调用 Producer 查询详情。Payload 小,但增加同步依赖和查询压力。

Event-Carried State Transfer

OrderCreated { order_id, user_id, amount, items, ... }

Consumer 可独立处理,但事件更大、敏感数据更多,Schema 演进和重复事实治理更复杂。

选择取决于自治、数据敏感度、更新频率和一致性要求。

DB 与 Event 的可靠双写

错误做法:

write DB
→ publish event

两步之间崩溃会出现 DB 已提交但 Event 丢失。

常见解决:

Business Transaction
→ write business row + outbox row atomically
→ publisher / CDC reads outbox
→ publish event
→ mark sent / retry

这仍需幂等 Producer 和 Consumer。

在 Agent Runtime 中的应用

Agent Runtime 可把执行过程表达为事件:

run_started
tool_call_requested
tool_call_completed
approval_required
run_resumed
run_completed

Event 适合前端同步、Trace、Replay 和审计;Runtime 仍需要权威 State / Checkpoint,不能只靠客户端收到的事件恢复。

EDA 与 SSE / WebSocket

  • EDA 是系统组件的协作架构;
  • SSE / WebSocket 是向客户端传输事件的协议。

后端可以内部用同步调用,外部用 SSE;也可以内部用 Kafka,客户端仍通过普通 HTTP。两者不是同义词。

什么时候值得用

适合:多个独立下游、长任务、削峰、跨服务集成、审计/Replay、状态投影。

不适合:简单强一致 CRUD、极短同步链路、没有 Schema/幂等/观测能力的小系统。

设计检查表

  • Event 是已发生事实,不是模糊命令。
  • Event ID、Version、Entity Key、Occurred At 明确。
  • Producer / Consumer 都可重试且幂等。
  • 顺序边界和过期事件处理明确。
  • DB + Broker 双写使用 Outbox / CDC 或等价方案。
  • 有 Lag、Retry、DLQ、Trace 和业务完成指标。
  • 能重建派生视图,且知道 Source of Truth 在哪里。

学习检查

不看正文回答:

  1. Event 与 Command 的核心语义差异是什么?
  2. EDA 降低了哪些耦合,又引入了哪些一致性和可观测性成本?
  3. at-least-once delivery 为什么要求 consumer 幂等?
  4. ordering 的边界为什么通常需要落到 partition/entity key,而不是追求全局总顺序?
  5. Event Notification 与 Event-Carried State Transfer 分别牺牲/减少了什么耦合?
  6. 为什么 DB transaction + broker publish 会产生 dual-write 问题?Outbox/CDC 怎样收敛它?
  7. SSE / WebSocket 为什么是 transport,而不是 EDA 本身?
  8. 给一个“订单创建后触发库存、邮件、分析”场景,画出 producer、event contract、broker/log、consumer 与 failure/retry 边界。
创建于 2025/1/1 更新于 2026/9/30
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本