3. 事件驱动架构基础:Event、Event Bus、Event Sourcing 与 CQRS 简介
在分布式系统与微服务架构中,服务间的通信方式决定了系统的耦合度、扩展性与容错能力。事件驱动架构(Event-Driven Architecture, EDA)通过将状态变更封装为不可变的事件,实现了服务间的异步、松耦合通信。本章将系统梳理 EDA 的四个核心概念:Event、Event Bus、Event Sourcing 与 CQRS,为后续跨服务熔断传播与隔离设计奠定理论基础。
3.1 核心概念:Event(事件)
事件是 EDA 中最基本的通信单元,代表“已经发生的事情”。在微服务中,事件通常由某个服务在完成业务操作后发布,其他服务通过订阅来感知并做出响应。
| 特性 | 说明 | 示例 |
|---|---|---|
| 不可变性 | 事件一旦生成,内容不可修改 | OrderCreated 事件生成后,订单 ID 与金额固定 |
| 自描述性 | 事件包含足够上下文,消费者无需查询源服务 | 事件体中携带 orderId、userId、totalAmount |
| 时间戳 | 记录事件发生的精确时间 | occurredAt: 2025-03-21T10:30:00Z |
| 幂等键 | 唯一标识,用于消费者去重 | eventId: uuid-v4 |
设计原则:事件应使用过去时态命名(如
PaymentProcessed、InventoryReserved),避免使用命令式(如 ProcessPayment)。事件是事实记录,而非请求指令。
3.2 事件通道:Event Bus(事件总线)
Event Bus 是事件在服务间传递的中介层,负责事件的接收、路由、过滤与投递。它解耦了事件发布者与订阅者,使双方无需感知彼此的网络地址与生命周期。
3.2.1 核心功能
- 发布/订阅(Pub/Sub):支持一对多的消息分发模式。
- 路由规则:基于事件类型、标签或内容进行精确或模糊匹配。
- 持久化与重试:确保事件在消费者故障时不丢失,支持死信队列。
- 顺序保证:在分区(Partition)级别维持事件顺序。
3.2.2 常见实现对比
| 实现 | 模型 | 顺序保证 | 持久化 | 适用场景 |
|---|---|---|---|---|
| Apache Kafka | 分区日志 | 分区内严格有序 | 磁盘持久化 | 高吞吐、事件溯源、日志聚合 |
| RabbitMQ | 队列/交换器 | 单队列有序 | 可选持久化 | 任务分发、RPC 转异步 |
| NATS | 轻量 Pub/Sub | 无严格顺序 | 内存/文件 | 低延迟、云原生环境 |
| 云服务(EventBridge) | 事件总线 | 依赖规则 | 托管持久化 | Serverless、跨账户事件 |
3.3 事件溯源(Event Sourcing)
传统持久化模式只保存实体的当前状态,而事件溯源将每个状态变更都记录为不可变事件。实体的当前状态由事件序列通过重放(Replay)计算得出。
3.3.1 核心优势
- 完整审计轨迹:所有变更均可追溯,满足合规要求。
- 时间旅行:可重建任意历史时刻的实体状态。
- 事件驱动原生:事件存储天然支持异步发布。
- 消除对象-关系阻抗:无需复杂的 ORM 映射。
3.3.2 典型事件存储结构
-- 事件表结构示例(PostgreSQL)
CREATE TABLE event_store (
event_id UUID PRIMARY KEY,
aggregate_type VARCHAR(100) NOT NULL, -- 聚合类型,如 "Order"
aggregate_id VARCHAR(64) NOT NULL, -- 聚合 ID
event_type VARCHAR(100) NOT NULL, -- 事件类型,如 "OrderCreated"
event_data JSONB NOT NULL, -- 事件载荷
version INT NOT NULL, -- 聚合版本号(乐观锁)
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (aggregate_type, aggregate_id, version)
);
注意:事件溯源不适用于所有场景。对于频繁更新且只关心最新状态的实体(如用户最后登录时间),传统 CRUD 更高效。事件溯源适合需要审计、复杂事件处理或跨服务状态同步的场景。
3.4 命令查询职责分离(CQRS)
CQRS 将系统的读写模型分离为独立的命令模型(写)与查询模型(读)。命令模型处理业务逻辑并产生事件,查询模型针对展示需求优化数据读取。
3.4.1 分离带来的好处
| 维度 | 命令模型(写) | 查询模型(读) |
|---|---|---|
| 数据形态 | 事件流 / 规范化表 | 物化视图 / 反规范化表 |
| 一致性 | 强一致性(聚合内) | 最终一致性 |
| 扩展方式 | 按聚合类型分区 | 按查询维度独立扩展 |
| 缓存策略 | 不缓存(事件不可变) | 可大量使用缓存 |
| 典型技术 | Event Store、Kafka | Elasticsearch、Redis、Read DB |
3.4.2 与 Event Sourcing 的协同
Event Sourcing 与 CQRS 经常组合使用:命令模型将事件写入 Event Store,查询模型通过订阅事件流构建并更新自己的读视图。这种组合在微服务中被称为 ES/CQRS 模式。
3.5 三者关系与适用场景
Event、Event Bus、Event Sourcing 与 CQRS 并非必须同时使用。下表总结了各组件在微服务熔断与隔离场景中的角色定位:
| 组件 | 熔断场景中的角色 | 隔离价值 |
|---|---|---|
| Event | 熔断状态变更通知(如 CircuitBreakerOpened) |
事件不可变,下游可独立判断 |
| Event Bus | 熔断事件跨服务传播通道 | 解耦发布者与订阅者,避免级联故障 |
| Event Sourcing | 记录熔断状态变更历史 | 支持熔断恢复后的状态重建 |
| CQRS | 熔断规则配置(写)与状态查询(读)分离 | 读模型可独立缓存熔断状态,不阻塞写操作 |