事件驱动内核
什么是Event-Driven内核
Event-Driven内核(EDC)是体系结构的"脊柱",其中业务事实被捕获并作为不可变事件传播,其余功能(阅读,集成,分析,缓存,符号化)建立在这些事件流之上。内核指定事件合同,交付规则以及顺序/相容性不变性,从而提供较弱的连通性和可扩展性。
关键思想:首先写出事实(内核),然后将其独立丰富并投影到所需的模型中。这会降低连通性并增加对部分故障的抵抗力。
EDC的目标和属性
事实的真实性:每个事件都是"发生了什么"的不可改变的记录。
连通性弱:生产者不认识消费者;系统扩展-增加订户。
扩大规模:按政党/拓扑水平增长,独立消费者。
可观察性和审计:端到端ID,可重复性,可重复性和可重复性。
受控演变:方案版本,兼容性,可执行性。
体系结构组件
1.总线/活动经纪人:Kafka/NATS/Pulsar/SNS+SQS-频道,派对,回避。
2.电路注册表:JSON Schema/Avro/Protobuf,用于兼容性和进化。
3.Outbox/CDC路径:原子事实固定+发布没有"双重记录"。
4.投影/读取(CQRS):用于快速查询的实例化视图。
5.传奇/编排:通过事件/命令协调长寿过程。
6.丰富:单独的".enriched"/"derived"拓扑,对关键路径没有影响。
7.天文可用性:跟踪、逻辑、事件和滞后指标。
事件模型
事件类型
域事件: 业务事实('payment.authorized`, `kyc.approved`).
Integration Events:面向外部系统(稳定,变化缓慢)。
更改数据捕获(CDC):技术记录更改(用于迁移/集成)。
审计/电信:演员行动,安全,SLA。
强制属性(内核)
json
{
"event_id": "uuid",
"event_type": "payment. authorized. v1",
"occurred_at": "2025-10-31T11:34:52Z",
"producer": "payments-service",
"subject": { "type": "payment", "id": "pay_123" },
"payload": { "amount": 1000, "currency": "EUR", "method": "card" },
"schema_version": 1,
"trace_id": "abc123",
"partition_key": "pay_123"
}
建议:'event_id'是全局唯一的,'partition_key'设置实体的顺序,'trace_id'提供相关性。
交付语义和幂等
On-Least-once(许多经纪人默认):消费者必须保持平均水平。
最多:只有次要遥测才能接受。
Exactly-once:通过交易/等价密钥/湖泊在线程和存储级别实现(更贵,需要充分的理由)。
消费者的相容性模板
"event_id"/'(event_id,consumer_id)的dedup表与TTL ≥拓扑。
Upsert代替insert;在"sequence"/"occurred_at"上转换投影。
交易中的操作:标记"看到"+状态变化。
顺序和分组
保证党内秩序。
选择"partition_key"以便将单个聚合实体的所有事件分成一个部分("user_id","payment_id")。
避免"热键":如果要分配负载,则使用盐/小键哈希。
模式和演变
Additive-first:新的可选字段,禁止在没有主要版本的情况下更改类型/语义。
兼容性:方案注册表中的BACKWARD/FORWARD;CI阻止不兼容的更改。
命名: "域。action.v{major}` (`payment.authorized.v1`).
迁移:并行发布"v1"和"v2"对,提供双辐射(通过outbox进行双写入),过渡后拍摄"v1"。
Outbox и CDC
Outbox(推荐用于事务性服务)
1.在一个DB事务中:我们将域记录和事件存储在outbox中。
2.背景公关人员阅读outbox,发布到经纪人,标记"发送"。
3.保证:跌倒时没有"损失"事实,没有同步。
CDC (Change Data Capture)
适用于现有系统/迁移;来源-DB复制日志。
需要过滤/重新编码到域事件(不要向外广播"原始"表)。
CQRS和投影
命令更改状态(通常是同步的),事件生成投影(异步)。
投影是针对查询(搜索,列表,报告)设计的,并由订户更新。
时间同步是规范:显示稳定的UX("数据将在几秒钟内更新")。
传奇: 流程协调
编排:一个协调员发送命令并等待事件。
编舞:参与者对彼此的事件做出反应(更简单,但需要合同纪律)。
规则:明确的补偿和超时,可重复的步骤,相同的处理程序。
可观察性
Trace/Span:在生成事件时,通过标题来推动"trace_id"/"span_id"。
指标:消费者脱落,发布/消费速度,死信率,重复数据消除比例。
DLQ/parking lot:不合时宜的消息-成一个单独的标有alert的标题;确保重新整理。
安全性和合规性
数据分类:内核仅包含所需的最小PII/findans(反金字塔模型),细节包含在富集中。
关键属性的签名/散列,完整性控制。
按主题/控制器(IAM/ACL)对权限进行飞行加密,并进行恢复。
回避政策和遗忘权:为每个拓扑明确定义。
性能和可持续性
Backpressure:消费者有竞争限制,经纪人有配额/限制。
Batch处理和压缩:分组记录以降低开销。
带有抖动器和DLQ的回程代替无休止的尝试。
反弹韧性:在交易/外部存储离场,用快门加速冷启动。
典型事件模板
支付核心
`payment.initiated.v1` → `payment.authorized.v1` → `payment.captured.v1` → `payment.settled.v1`
免责声明: '付款。declined.v1`, `payment.refunded.v1`
派对: "payment_id"
SLA:核心差≤ 2 s p95;消费者的偶然性是强制性的。
CUS/验证
`kyc.started.v1` → `kyc.document.received.v1` → `kyc.approved.v1`/`kyc.rejected.v1`
PII-最小;文档详细信息-在'kyc中。enriched.v1'限制访问。
审计/安全
`audit.recorded.v1'具有"actor","subject","action","occurred_at","trace_id"属性。
持续撤回/归档;增强的完整性(WORM存储)。
反模式
Fat Event:不需要负担过重的薪水,PII泄漏。
通过事件隐藏的RPC: 等待同步响应"在这里和现在。"
原始CDC向外:与DB方案的紧密连接。
消费者没有偶然性:配音会导致双重副作用。
一个"全部"的常见拓扑:利益冲突,有问题的秩序,复杂的演变。
逐步实施EDC
1.域映射:突出显示关键聚合和生命周期。
2.事件目录:标题,含义,不变量,必填字段。
3.图和注册表:选择格式,启用兼容性规则。
4.Outbox/CDC:为每个制作人定义发布事实的机制。
5.分期:选择密钥并评估热键/重写。
6.相似性:消除重复模式+消费者事务性。
7.投影:定义实例化模型和更新SLA。
8.天文可用性:跟踪、泻湖、DLQ、Alertes。
9.安全/PII:数据分类、加密、ACL。
10.进化论海德:版本政治,消除裂痕,双写作以进行迁移。
生产支票清单
- 每个事件都有'event_id'、'trace_id'、'occurred_at'、'partition_key'。
- 注册表中的方案,包括兼容性检查。
- 实现并测试了消费者的水平。
- 已将DLQ/parking lot和Alerta配置为发布/消费错误。
- 投影以可接受的时间从逻辑(replay)中重建。
- 限制使用PII;内核中的最低工资。
- 对滞后/交货的SLA进行了测量,并在行车记录仪上可见。
- 有计划迁移事件版本和删除记录窗口。
FAQ
EDC与"简单轮胎"有何不同?
核心不仅是经纪人,而且是事件契约,顺序/相等性规则,进化过程和可观察性。
只能在CDC上构建吗?
CDC适用于集成/迁移,但是域事件更清楚地表达了意义,并且更稳定地经历了DB的变化。
如何保持一致?
接受事件一致性并设计其下方的UX/过程(刷新,撤销,补偿指标)。
什么时候需要exactly-once?
很少:当加倍是严格不可接受的,无法补偿。更常见的是,at-least-once+等效性就足够了。
底线
Event-Driven内核将业务事实流转变为系统的可靠基础。明确的事件合同,交付纪律和可观察性提供了可扩展性,稳定性和进化速度-没有脆弱的同步链接和变化中的"风暴"回归。