3. 事件驱动架构基础:事件总线、事件源、事件消费者、事件路由模式
在构建自动化审批流程时,传统轮询或定时任务无法满足实时性与解耦需求。事件驱动架构(EDA)通过事件作为核心通信单元,让基础设施变更的每个状态变化(如“PR 创建”、“审批通过”、“Terraform 执行完成”)都能被异步捕获并触发后续动作。本章聚焦四个核心概念:事件总线、事件源、事件消费者与事件路由模式,为后续 Runbook 联动奠定架构基础。
3.1 核心概念与职责
事件驱动架构由四个基本角色组成,下表总结了各自的职责与在审批流程中的典型实例:
| 角色 | 职责 | 审批流程实例 |
|---|---|---|
| 事件源 | 产生事件并发布到总线,不关心谁消费 | GitHub Webhook(PR opened)、Jenkins(构建完成) |
| 事件总线 | 接收、过滤、路由事件到对应消费者 | AWS EventBridge、Kafka Topic、Azure Event Grid |
| 事件消费者 | 订阅特定事件类型,执行业务逻辑 | 审批通知 Lambda、Terraform 执行 Runbook |
| 事件路由模式 | 定义事件从总线到消费者的分发策略 | 基于事件类型路由、内容过滤路由、扇出路由 |
关键原则:事件源与消费者完全解耦。事件源不需要知道谁在处理事件,消费者也不需要知道事件从何而来。这种松耦合使得审批流程中的任意环节(如增加新的审批人、替换 IaC 扫描工具)都可以独立变更而不影响其他组件。
3.2 事件总线:中枢神经
事件总线是事件驱动架构的核心中间件。它接收来自多个事件源的事件,并根据预定义的规则将事件分发给一个或多个消费者。在 IaC 审批场景中,事件总线承担以下关键功能:
- 事件接收与标准化:将不同来源(GitHub、GitLab、Jenkins)的事件转换为统一格式。
- 过滤与路由:只将“PR 已批准”或“安全扫描失败”等关键事件转发给对应的 Runbook。
- 缓冲与重试:当消费者暂时不可用时,事件总线保留事件并自动重试。
- 审计追踪:记录所有事件流,满足合规与回溯需求。
下图展示了事件总线在审批流程中的中心位置:
3.3 事件源:变更的触发器
事件源是产生事件的实体。在 IaC 审批流程中,常见的事件源包括:
- 版本控制系统:GitHub/GitLab Webhook 在 PR 创建、更新、合并时触发事件。
- CI/CD 管道:Jenkins/GitHub Actions 在构建、测试、部署阶段完成后发布事件。
- 安全扫描工具:Trivy/Checkov 扫描完成后输出结果事件。
- 审批平台:用户点击“批准”或“拒绝”后生成审批结果事件。
- 定时器:用于超时自动拒绝或定期检查审批状态。
最佳实践:每个事件源应遵循 CloudEvents 规范,包含
id、source、specversion、type、subject、time、data 等标准字段。这确保不同工具产生的事件能被总线统一处理。
3.4 事件消费者:Runbook 的执行者
事件消费者是订阅并处理特定事件的组件。在审批流程中,每个消费者通常对应一个 Runbook 或 Lambda 函数。消费者设计需遵循以下原则:
- 幂等性:同一事件被多次处理时,结果应保持一致。例如,重复的“批准”事件不应导致多次部署。
- 无状态:消费者不保存状态,所有上下文信息从事件数据中获取。
- 快速失败:处理失败时立即抛出异常,让事件总线触发重试或死信队列。
- 可观测性:每个消费者应输出结构化日志,包含事件 ID、处理耗时、结果状态。
以下是一个事件消费者(Node.js Lambda)处理审批事件的代码示例:
// 事件消费者:审批结果处理 Runbook
exports.handler = async (event) => {
console.log(`收到事件: ${event['detail-type']}`, JSON.stringify(event));
const { prId, repoName, action, approvedBy } = event.detail;
// 幂等性检查:查询当前 PR 是否已被处理
const alreadyProcessed = await checkIfProcessed(prId);
if (alreadyProcessed) {
console.log(`PR ${prId} 已被处理,跳过`);
return { statusCode: 200, body: 'Skipped' };
}
if (action === 'approved') {
// 触发 Terraform 执行 Runbook
await triggerTerraformRun(prId, repoName);
console.log(`已触发 Terraform 执行: PR ${prId}`);
} else if (action === 'rejected') {
// 发送拒绝通知
await sendRejectionNotification(prId, approvedBy);
console.log(`已发送拒绝通知: PR ${prId}`);
}
// 记录处理状态
await markAsProcessed(prId);
return { statusCode: 200, body: 'Processed' };
};
3.5 事件路由模式
事件路由模式决定了事件如何从总线到达正确的消费者。在审批流程中,三种模式最为常用:
| 路由模式 | 描述 | 审批场景示例 |
|---|---|---|
| 基于类型路由 | 根据事件类型字段分发 | pr.opened → 审批通知;pr.merged → 部署执行 |
| 基于内容过滤 | 根据事件数据中的字段值过滤 | 只路由 branch == "production" 的事件到高级审批 |
| 扇出路由 | 同一事件同时发送给多个消费者 | 审批通过事件同时发送给:Terraform 执行器 + 审计日志 + Slack 通知 |
注意:扇出路由虽然灵活,但需注意消费者之间的依赖关系。如果 Terraform 执行器需要等待审计日志写入完成,则应使用编排模式而非纯扇出。在审批流程中,建议将“执行”与“通知”解耦,避免通知延迟阻塞部署。
3.6 事件驱动架构在审批流程中的优势
将事件驱动架构应用于 IaC 变更审批,带来以下核心收益:
- 实时响应:事件产生后毫秒级触发后续动作,无需轮询。
- 弹性扩展:审批高峰期可独立扩展消费者实例,不影响事件源。
- 故障隔离:单个消费者故障不会影响其他流程,事件总线自动重试。
- 审计完备:所有事件流被总线记录,形成完整的变更审计链。
- 灵活编排:通过修改路由规则即可调整审批流程,无需重新部署。
下图展示了事件驱动架构在完整审批流程中的端到端流转: