1 开头想讲个故事
2019 年双 11,阿里巴巴创下了每秒 54.4 万笔交易的峰值记录。
这个数字意味着什么?假设你手速足够快,每秒能完成一次交易,也要不吃不喝不睡觉地连续工作 15年 才能达到这个量级。
他们是怎么做到的?
答案绝对不是"买更多服务器"那么简单。传统架构下,订单进来后需要依次调用:库存服务 -> 支付服务 -> 物流服务 -> 通知服务...任何一个环节卡主,整个链路就崩溃。
而大厂用的是另一套玩法
当你下单成功后,系统知识轻描淡写地"发布"了一个【订单已创建】的时间,然后就告诉你"提交成功"了。后续的库存检查、仓库备货、物流通知、短信推送...全由各个服务异步完成,互不干扰。
这就是**事件驱动架构(Event-Driven Architecture,简称 EDA)**的威力。
2 EDA 到底是什么?
2.1 重新定义【事件】
先跑掉教科书上哪些晦涩难懂的定义。用最直白的话说:
事件 = 系统中发生的任何事情的事件记录
你下单了,是一个事件。
库存减扣了,是一个事件。
用户登录了,是一个事件。
温度传感器读数变化了,也是一个事件。
TypeScript
/**
* 订单创建事件 - 就是一个"事实记录"
* @interface OrderCreatedEvent
* @description 记录"订单已经创建"这个事实
*/
interface OrderCreatedEvent {
eventId: string; // 事件的唯一身份证
eventType: 'OrderCreated'; // 事件类型
orderId: string; // 哪个订单
userId: string; // 哪个用户
totalAmount: number; // 多少钱
occurredAt: string; // 什么时候发生的
}
2.2 事件 vs 命令:80% 的人都搞混了
这是 EDA 最核心的概念,90% 的面试者都答不清楚。
命令:
**一句话定义 -**帮我做一件事
发送方式- 点对点,我只告诉特定的人
可以撤回吗?- 可以,我还没做呢!
举例 - 创建订单
事件:
一句话定义- 这件事已经发生了
发送方式 - 广播,谁想听都可以过来听
可以撤回吗? - 不行,已经发生了
举例- 订单已创建
TypeScript
/**
* 命令:我要你做事(可能做也可能不做)
*/
interface CreateOrderCommand {
type: 'CreateOrderCommand';
payload: { userId: string; items: CartItem[] };
// 接收者需要回复:成功还是失败
}
TypeScript
/**
* 事件:向大家宣告一件事(不可撤销的历史事实)
*/
interface OrderCreatedEvent {
type: 'OrderCreated';
payload: { orderId: string; userId: string; totalAmount: number };
// 谁爱听谁听,跟我无关
}
2.3 EDA 的三大核心角色
发布者(Publisher):只负责"广播"发生了什么,不关心谁会处理
事件总线(Event Bus):负责传递、十九话、保证送达
消费者(Consumer):订阅自己关心的事情,独自处理

3 传统架构 vs 事件驱动
3.1 你一定见过的【同步调用地狱】
传统同步调用 - 一个请求要经过整条链路
TypeScript
class OrderService {
async createOrder(userId: string, items: Item[]): Promise<Order> {
// 步骤1:创建订单(耗时 10ms)
const order = await this.orderRepo.create({ userId, items });
// 步骤2:扣减库存(等待...如果库存服务慢呢?)
await this.inventoryService.reserveStock(items); // 🔸 阻塞
// 步骤3:处理支付(等待...如果支付超时呢?)
await this.paymentService.process(order); // 🔸 阻塞
// 步骤4:通知仓库(等待...)
await this.warehouseService.notify(order); // 🔸 阻塞
// 步骤5:发送短信(等待...)
await this.smsService.send(order.userId); // 🔸 阻塞
return order; // 用户等了 500ms+,体验极差
}
}
这种架构的问题:
- 一个服务挂 -> 整条链路挂
- 用户要等所有步骤完成才能收到响应
- 添加新功能必须修改原有代码
- 任何一个环节慢,整条响应就慢
3.2 事件驱动:优雅的异步解耦
TypeScript
class OrderService {
constructor(private eventBus: EventBus) {}
/**
* 下单操作 - 立即返回,不需要等待后续处理
*/
async createOrder(userId: string, items: Item[]): Promise<Order> {
const order = await this.orderRepo.create({ userId, items });
// 只需要发布一个事件,然后...就没有然后了
await this.eventBus.publish('order.created', {
orderId: order.id,
userId,
items,
totalAmount: order.totalAmount
});
return order; // 用户立即收到响应,耗时 < 50ms
}
}
/**
* 库存服务 - 自己监听事件,自己处理
*/
class InventoryConsumer {
constructor(private eventBus: EventBus, private inventory: InventoryService) {
this.eventBus.subscribe('order.created', this.handle);
}
async handle(event: OrderCreatedEvent) {
await this.inventory.reserveStock(event.items);
// 处理完成,发个事件通知别人
await this.eventBus.publish('stock.reserved', { orderId: event.orderId });
}
}
/**
* 通知服务 - 完全独立,爱谁谁
*/
class NotificationConsumer {
constructor(private eventBus: EventBus, private notifier: Notifier) {
this.eventBus.subscribe('order.created', this.handle);
}
async handle(event: OrderCreatedEvent) {
await this.notifier.sendSMS(event.userId, '订单创建成功');
}
}
/**
* 支付服务 - 也独立订阅
*/
class PaymentConsumer {
constructor(private eventBus: EventBus, private payment: PaymentService) {
// 支付服务可能需要等库存确认后再处理
this.eventBus.subscribe('stock.reserved', this.handle);
}
async handle(event: StockReservedEvent) {
await this.payment.process(event.orderId);
}
}
3.3 对比总结
|------|-------------|------------|
| 维度 | 传统同步架构 | 事件驱动架构 |
| 响应时间 | 所有步骤耗时总和 | 只算第一步耗时 |
| 服务依赖 | 强耦合,一个挂,全都挂 | 松耦合,互不影响 |
| 扩展性 | 难,一个慢扩展哪个 | 简单,哪个慢扩展哪个 |
| 新增功能 | 修改源代码 | 新增消费者即可 |
| 故障影响 | 链路崩溃 | 单点故障局部影响 |
| 用户体验 | 等待时间长 | 响应速度快 |
4 为什么大厂都在用 EDA?四大核心优势
4.1 松耦合(Decoupling)
这是 EDA 最大的价值,没有之一
发布者和消费者之间完全不依赖彼此的存在
TypeScript
/**
* 订单服务:只管发布事件,不关心谁在听
*/
class OrderService {
async publish(order: Order) {
await this.eventBus.publish('order.created', order.toEvent());
// 将来如果要加"数据分析服务"?
// 不好意思,不关订单服务的事,你自己订阅去
}
}
/**
* 某天产品说:我们要给用户发优惠券
* 工程师:好,加一个优惠券消费者就行,订单服务代码不用动一根汗毛
*/
class CouponConsumer {
constructor(eventBus: EventBus) {
eventBus.subscribe('order.created', async (event) => {
// 判断是否满足优惠券条件
if (event.totalAmount > 100) {
await this.couponService.grantCoupon(event.userId, 'NEW_USER_100');
}
});
}
}
现实比喻
传统架构:老板站在你身后盯着你干活
事件驱动:老板发个任务单就不管了,自己安排时间完成。
4.2 可扩展性(Scalability)
每个消费者可以独立扩展,想加多少加多少
TypeScript
/**
* 消费者组 - 支持水平扩展
* @description 流量高峰期,多开几个消费者实例就完事了
*/
class ConsumerGroup<T> {
private consumers: Consumer<T>[] = [];
/**
* 根据负载动态扩展
*/
scaleOut(count: number): void {
for (let i = 0; i < count; i++) {
this.consumers.push(new Consumer(this.eventBus, this.handler));
}
console.log(`已扩展到 ${this.consumers.length} 个消费者实例`);
}
/**
* 流量低了缩回来
*/
scaleIn(count: number): void {
this.consumers.splice(0, count);
console.log(`已缩减到 ${this.consumers.length} 个消费者实例`);
}
}
4.3 容错与韧性(Resilience)
消息持久化 + 重试机制 = 永不丢失
TypeScript
/**
* 可靠的事件处理 - 消息不丢失
* @class ReliableEventBus
*/
class ReliableEventBus {
private queue: MessageQueue;
/**
* 发布时自动持久化
*/
async publish(topic: string, payload: object): Promise<void> {
const message = {
id: uuid(),
topic,
payload,
timestamp: new Date().toISOString(),
retryCount: 0
};
// 写磁盘/数据库,确保不丢
await this.queue.persist(message);
await this.queue.route(topic, message);
}
/**
* 消费失败自动重试
*/
async subscribe(topic: string, handler: Handler): Promise<void> {
await this.queue.listen(topic, async (message) => {
try {
await handler(message.payload);
await message.ack(); // 确认处理成功
} catch (error) {
// 重试机制
if (message.retryCount < 3) {
message.retryCount++;
await this.queue.retry(message, 1000 * message.retryCount); // 延迟重试
} else {
// 进入死信队列,人工处理
await this.queue.deadLetter(message, error);
}
}
});
}
}
4.4 实时响应(Real-time)
事件驱动天然支持实时处理,毫秒级响应
TypeScript
/**
* 股票价格实时监控 - 事件驱动的典型应用
*/
class StockPriceMonitor {
constructor(private eventBus: EventBus) {
this.eventBus.subscribe('stock.price.updated', this.handle);
}
async handle(event: PriceUpdateEvent) {
// 1. 实时检查告警(毫秒级)
if (Math.abs(event.changePercent) > 10) {
await this.sendAlert(event);
}
// 2. 实时更新缓存
await this.cache.set(event.symbol, event.price);
// 3. 实时推送客户端
await this.wsBroadcast(event.symbol, event);
}
}
5 EDA 三大核心模式
5.1 发布 / 订阅(Pub / Sub)
最基础,也是最常用的模式

TypeScript
/**
* 简单的 Pub/Sub 实现
*/
class PubSub {
private subscriptions = new Map<string, Handler[]>();
subscribe(topic: string, handler: Handler): void {
if (!this.subscriptions.has(topic)) {
this.subscriptions.set(topic, []);
}
this.subscriptions.get(topic)!.push(handler);
}
async publish(topic: string, data: any): Promise<void> {
const handlers = this.subscriptions.get(topic) || [];
await Promise.all(handlers.map(h => h(data)));
}
}
5.2 事件溯源(Event Sourcing)
不存当前状态,存放所有历史事件
TypeScript
/**
* 银行账户 - 事件溯源示例
* @class BankAccount
* @description 不存储余额,只存储所有交易记录
*/
class BankAccount {
private events: AccountEvent[] = [];
/**
* 存款 - 其实是记录一个"存款事件"
*/
deposit(amount: number): void {
this.events.push({
type: 'DEPOSIT',
amount,
timestamp: new Date().toISOString()
});
}
/**
* 取款 - 记录一个"取款事件"
*/
withdraw(amount: number): void {
if (this.getBalance() < amount) {
throw new Error('余额不足');
}
this.events.push({
type: 'WITHDRAWAL',
amount,
timestamp: new Date().toISOString()
});
}
/**
* 当前余额 = 重放所有事件
*/
getBalance(): number {
return this.events.reduce((balance, event) => {
return event.type === 'DEPOSIT'
? balance + event.amount
: balance - event.amount;
}, 0);
}
/**
* 完整的历史记录 - 审计追踪了解一下?
*/
getHistory(): AccountEvent[] {
return [...this.events];
}
}
事件溯源的超级优势:
- 完整的审计日志(每一笔钱怎么来的,清清楚楚)
- 可以回到任意时间点的状态(时间旅行调试)
- 可以重放时间来修复数据错误
5.3 CQRS(命令查询职责分离)
读和写,用不同的模型
CQRS模式
写模型:负责创建、更新
读模式:负责查询,针对不同场景优化
TypeScript
// -------------------- 写端 --------------------
class OrderCommandHandler {
async handle(cmd: CreateOrderCommand): Promise<void> {
const order = new Order(cmd.userId, cmd.items);
await this.orderRepo.save(order); // 存订单
await this.eventBus.publish('order.created', order.toEvent()); // 通知读端
}
}
// -------------------- 读端 --------------------
class OrderQueryHandler {
// 场景1:查订单详情(归一化视图)
async getOrderDetail(id: string): Promise<OrderDetail> {
return this.detailView.get(id);
}
// 场景2:查用户的所有订单(反规范化视图)
async getUserOrders(userId: string): Promise<OrderSummary[]> {
return this.userOrderView.getByUserId(userId);
}
// 场景3:运营后台统计(聚合视图)
async getStatistics(): Promise<OrderStats> {
return this.statsView.get();
}
}
// -------------------- 视图同步 --------------------
class ViewSyncHandler {
constructor(private eventBus: EventBus) {
this.eventBus.subscribe('order.created', this.syncDetailView);
this.eventBus.subscribe('order.created', this.syncUserOrderView);
this.eventBus.subscribe('order.created', this.syncStatsView);
}
// 更新各种读视图,互不影响
}
6 大厂怎么使用 EDA
6.1 电商订单全流程

6.2 物联网数据处理
TypeScript
/**
* 传感器数据实时处理流水线
*/
class IoTPipeline {
constructor(private eventBus: EventBus) {
// 温度传感器数据处理
this.eventBus.subscribe('sensor.temperature', this.handleTemperature);
// 湿度传感器数据处理
this.eventBus.subscribe('sensor.humidity', this.handleHumidity);
// 异常告警处理
this.eventBus.subscribe('sensor.anomaly', this.handleAnomaly);
}
async handleTemperature(event: TemperatureEvent) {
// 1. 存时序数据库
await this.tsdb.insert(event);
// 2. 超温告警
if (event.value > 80) {
await this.eventBus.publish('sensor.anomaly', {
sensorId: event.sensorId,
type: 'HIGH_TEMPERATURE',
value: event.value,
threshold: 80
});
}
// 3. 更新实时仪表盘
await this.dashboard.update(event.sensorId, event.value);
}
}
7 EDA 四大挑战及解决方案
7.1 最终一致性
**问题:**事件驱动是异步的,如果保证数据最终一致性?
**解决方案:**Saga 模式
Saga 模式 能够把分布式事务拆分成多个本地事务
TypeScript
class OrderSaga {
async execute(cmd: CreateOrderCommand): Promise<void> {
try {
// 步骤1:创建订单
const order = await this.orderService.create(cmd);
// 步骤2:扣库存(失败?回滚订单)
await this.inventoryService.reserve(cmd.items);
// 步骤3:扣款(失败?回滚库存)
await this.paymentService.charge(order);
// 步骤4:完成
await this.eventBus.publish('order.completed', order);
} catch (error) {
// 补偿事务:哪里出错就回滚哪几步
await this.compensate(cmd);
}
}
}
7.2 事件顺序
**问题:**时间可能乱序到达怎么办?
**解决方式:**分区 + 序列号
方案1,同一个对象的时间走同一个分区
方案2:消费者端按顺序处理
TypeScript
/**
* 保证事件顺序
*/
class OrderedConsumer {
// 方案1:同一对象的事件走同一分区
async consume(event: Event): Promise<void> {
const partitionKey = event.orderId; // 同一个订单的事件进同一个分区
await this.queue.consume('orders', partitionKey, this.handler);
}
// 方案2:消费者端排序
private eventBuffer = new Map<string, Event[]>();
async handle(event: Event): Promise<void> {
const events = this.eventBuffer.get(event.orderId) || [];
events.push(event);
events.sort((a, b) => a.sequence - b.sequence); // 按序号排序
// 按顺序处理
for (const e of events) {
await this.process(e);
}
}
}
7.3 幂等性
**问题:**消费者可能被调用多次,如何保证不重复消费?
**解决方案:**去重机制
TypeScript
class IdempotentConsumer {
private processed = new Set<string>();
async handle(event: Event): Promise<void> {
// 已经处理过?直接跳过
if (this.processed.has(event.id)) {
console.log(`事件 ${event.id} 已处理,跳过`);
return;
}
// 处理业务
await this.businessLogic(event);
// 记录已处理
this.processed.add(event.id);
// 生产环境应该存数据库,用唯一键约束
// await this.eventLog.save({ eventId: event.id, processedAt: new Date() });
}
}
7.4 调试困难
**问题:**事件驱动系统如何追踪问题?
**解决方案:**分布式追踪
TypeScript
/**
* 事件链路追踪
*/
class TracedEventBus {
async publish(topic: string, payload: object): Promise<void> {
const tracing = {
traceId: uuid(), // 全链路ID
spanId: uuid(), // 当前Span
parentId: getCurrentSpanId(),
timestamp: Date.now()
};
await this.bus.publish(topic, { ...payload, _trace: tracing });
}
subscribe(topic: string, handler: Handler): void {
this.bus.subscribe(topic, async (event) => {
const span = this.tracer.startSpan(handler.name, {
traceId: event._trace.traceId,
parentId: event._trace.spanId
});
try {
await handler(event);
span.setTag('status', 'ok');
} catch (e) {
span.setTag('status', 'error');
span.log({ error: e.message });
} finally {
span.end();
}
});
}
}
8 总结:什么时候该用 EDA?
EDA 适用场景:
- 高并发系统,需要处理海量请求
- 微服务架构,服务间需要解耦
- 实时处理,需要毫秒级响应
- 审计追踪,需要完整的事件历史
- 业务流程复杂,多系统多步骤协作
慎用 EDA:
- 简单单体应用,没必要增加复杂度
- 强一致性要求,金融转账等场景
- 团队不熟悉,学习曲线需要了解一下
9 最后的建议
事件驱动架构 ≠ 不用学任何东西
它带来了灵活性 ,但也带来了**复杂性。**在采用之前,请确保:
- 你的团队能驾驭,需要理解异步解耦、消息队列、分布式事务
- 你的场景合适,不是所有系统都需要 EDA
- 你准备好了监控,事件驱动系统的问题更难排查
但一旦用好,EDA 就是你系统最坚强的后盾。