/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
1. 引言:为什么需要事件驱动架构
在单体应用时代,模块之间通过函数调用直接通信,简单直接。但当系统拆分为几十个微服务后,同步调用链会迅速演变成一张难以维护的网:一个下游服务抖动,整条链路超时;新增一个业务方,就要改动上游代码;大促流量峰值到来时,只能靠堆机器硬扛。
事件驱动架构(Event-Driven Architecture,EDA)正是为应对这些问题而生。它把「谁发生了什么」与「谁需要对此做出反应」解耦,让系统从「命令式调用」转向「事件通知 + 订阅消费」的协作模式。
本文将从核心概念出发,依次讨论适用场景、主流消息中间件选型、事件溯源与 CQRS 模式实践,最后落到事件驱动系统中最容易被忽视的一环——可观测性。
下面是本文的整体知识脉络,便于先建立全局认知:
#mermaid-svg-RC5L3YSkyi9PTXJG{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RC5L3YSkyi9PTXJG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RC5L3YSkyi9PTXJG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RC5L3YSkyi9PTXJG .error-icon{fill:#552222;}#mermaid-svg-RC5L3YSkyi9PTXJG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RC5L3YSkyi9PTXJG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RC5L3YSkyi9PTXJG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RC5L3YSkyi9PTXJG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RC5L3YSkyi9PTXJG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RC5L3YSkyi9PTXJG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RC5L3YSkyi9PTXJG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RC5L3YSkyi9PTXJG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RC5L3YSkyi9PTXJG .marker.cross{stroke:#333333;}#mermaid-svg-RC5L3YSkyi9PTXJG svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RC5L3YSkyi9PTXJG p{margin:0;}#mermaid-svg-RC5L3YSkyi9PTXJG .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-RC5L3YSkyi9PTXJG .cluster-label text{fill:#333;}#mermaid-svg-RC5L3YSkyi9PTXJG .cluster-label span{color:#333;}#mermaid-svg-RC5L3YSkyi9PTXJG .cluster-label span p{background-color:transparent;}#mermaid-svg-RC5L3YSkyi9PTXJG .label text,#mermaid-svg-RC5L3YSkyi9PTXJG span{fill:#333;color:#333;}#mermaid-svg-RC5L3YSkyi9PTXJG .node rect,#mermaid-svg-RC5L3YSkyi9PTXJG .node circle,#mermaid-svg-RC5L3YSkyi9PTXJG .node ellipse,#mermaid-svg-RC5L3YSkyi9PTXJG .node polygon,#mermaid-svg-RC5L3YSkyi9PTXJG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RC5L3YSkyi9PTXJG .rough-node .label text,#mermaid-svg-RC5L3YSkyi9PTXJG .node .label text,#mermaid-svg-RC5L3YSkyi9PTXJG .image-shape .label,#mermaid-svg-RC5L3YSkyi9PTXJG .icon-shape .label{text-anchor:middle;}#mermaid-svg-RC5L3YSkyi9PTXJG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RC5L3YSkyi9PTXJG .rough-node .label,#mermaid-svg-RC5L3YSkyi9PTXJG .node .label,#mermaid-svg-RC5L3YSkyi9PTXJG .image-shape .label,#mermaid-svg-RC5L3YSkyi9PTXJG .icon-shape .label{text-align:center;}#mermaid-svg-RC5L3YSkyi9PTXJG .node.clickable{cursor:pointer;}#mermaid-svg-RC5L3YSkyi9PTXJG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RC5L3YSkyi9PTXJG .arrowheadPath{fill:#333333;}#mermaid-svg-RC5L3YSkyi9PTXJG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RC5L3YSkyi9PTXJG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RC5L3YSkyi9PTXJG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RC5L3YSkyi9PTXJG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RC5L3YSkyi9PTXJG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RC5L3YSkyi9PTXJG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RC5L3YSkyi9PTXJG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RC5L3YSkyi9PTXJG .cluster text{fill:#333;}#mermaid-svg-RC5L3YSkyi9PTXJG .cluster span{color:#333;}#mermaid-svg-RC5L3YSkyi9PTXJG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RC5L3YSkyi9PTXJG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RC5L3YSkyi9PTXJG rect.text{fill:none;stroke-width:0;}#mermaid-svg-RC5L3YSkyi9PTXJG .icon-shape,#mermaid-svg-RC5L3YSkyi9PTXJG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RC5L3YSkyi9PTXJG .icon-shape p,#mermaid-svg-RC5L3YSkyi9PTXJG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RC5L3YSkyi9PTXJG .icon-shape .label rect,#mermaid-svg-RC5L3YSkyi9PTXJG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RC5L3YSkyi9PTXJG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RC5L3YSkyi9PTXJG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RC5L3YSkyi9PTXJG :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
事件驱动架构EDA
核心概念
适用场景
消息队列选型
事件溯源与CQRS
可观测性
事件命令查询
三种典型拓扑
解耦的三个层次
Kafka
RabbitMQ
Pulsar
事件存储与重放
读写模型分离
日志指标追踪
消费积压与死信
2. 事件驱动架构的核心概念
2.1 事件、命令与查询
理解 EDA 的第一步是区分三类消息语义:
| 事件(Event) | 已经发生的事实 | OrderPaid | 不可变、过去时、可多方订阅 |
| 命令(Command) | 希望对方执行的动作 | PayOrder | 点对点、可失败、通常只有一个处理者 |
| 查询(Query) | 请求读取数据 | GetOrderDetail | 同步返回、无副作用 |
事件的关键特征是不可变与一对多:发布者不关心谁消费、消费几次,只负责声明「这件事发生了」。
2.2 三种典型拓扑
- 事件通知(Event Notification):只传递「发生了什么」,消费者需要时再回查详情。耦合最低,但可能产生额外查询。
- 事件携带状态转移(Event-Carried State Transfer):事件中带上完整数据快照,消费者本地维护副本,避免回查。代价是事件体积变大、数据一致性窗口变长。
- 事件溯源(Event Sourcing):把事件本身作为唯一事实来源,状态由事件重放推导。这是最重的一种形态,后文单独展开。
三种拓扑在耦合度与数据完整性上的取舍可以直观对比:
#mermaid-svg-4RkWbb0HYdk6G7le{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4RkWbb0HYdk6G7le .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4RkWbb0HYdk6G7le .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4RkWbb0HYdk6G7le .error-icon{fill:#552222;}#mermaid-svg-4RkWbb0HYdk6G7le .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4RkWbb0HYdk6G7le .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4RkWbb0HYdk6G7le .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4RkWbb0HYdk6G7le .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4RkWbb0HYdk6G7le .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4RkWbb0HYdk6G7le .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4RkWbb0HYdk6G7le .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4RkWbb0HYdk6G7le .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4RkWbb0HYdk6G7le .marker.cross{stroke:#333333;}#mermaid-svg-4RkWbb0HYdk6G7le svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4RkWbb0HYdk6G7le p{margin:0;}#mermaid-svg-4RkWbb0HYdk6G7le .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-4RkWbb0HYdk6G7le .cluster-label text{fill:#333;}#mermaid-svg-4RkWbb0HYdk6G7le .cluster-label span{color:#333;}#mermaid-svg-4RkWbb0HYdk6G7le .cluster-label span p{background-color:transparent;}#mermaid-svg-4RkWbb0HYdk6G7le .label text,#mermaid-svg-4RkWbb0HYdk6G7le span{fill:#333;color:#333;}#mermaid-svg-4RkWbb0HYdk6G7le .node rect,#mermaid-svg-4RkWbb0HYdk6G7le .node circle,#mermaid-svg-4RkWbb0HYdk6G7le .node ellipse,#mermaid-svg-4RkWbb0HYdk6G7le .node polygon,#mermaid-svg-4RkWbb0HYdk6G7le .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4RkWbb0HYdk6G7le .rough-node .label text,#mermaid-svg-4RkWbb0HYdk6G7le .node .label text,#mermaid-svg-4RkWbb0HYdk6G7le .image-shape .label,#mermaid-svg-4RkWbb0HYdk6G7le .icon-shape .label{text-anchor:middle;}#mermaid-svg-4RkWbb0HYdk6G7le .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4RkWbb0HYdk6G7le .rough-node .label,#mermaid-svg-4RkWbb0HYdk6G7le .node .label,#mermaid-svg-4RkWbb0HYdk6G7le .image-shape .label,#mermaid-svg-4RkWbb0HYdk6G7le .icon-shape .label{text-align:center;}#mermaid-svg-4RkWbb0HYdk6G7le .node.clickable{cursor:pointer;}#mermaid-svg-4RkWbb0HYdk6G7le .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4RkWbb0HYdk6G7le .arrowheadPath{fill:#333333;}#mermaid-svg-4RkWbb0HYdk6G7le .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4RkWbb0HYdk6G7le .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4RkWbb0HYdk6G7le .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4RkWbb0HYdk6G7le .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4RkWbb0HYdk6G7le .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4RkWbb0HYdk6G7le .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4RkWbb0HYdk6G7le .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4RkWbb0HYdk6G7le .cluster text{fill:#333;}#mermaid-svg-4RkWbb0HYdk6G7le .cluster span{color:#333;}#mermaid-svg-4RkWbb0HYdk6G7le div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4RkWbb0HYdk6G7le .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4RkWbb0HYdk6G7le rect.text{fill:none;stroke-width:0;}#mermaid-svg-4RkWbb0HYdk6G7le .icon-shape,#mermaid-svg-4RkWbb0HYdk6G7le .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4RkWbb0HYdk6G7le .icon-shape p,#mermaid-svg-4RkWbb0HYdk6G7le .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4RkWbb0HYdk6G7le .icon-shape .label rect,#mermaid-svg-4RkWbb0HYdk6G7le .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4RkWbb0HYdk6G7le .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4RkWbb0HYdk6G7le .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4RkWbb0HYdk6G7le :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
事件溯源
生产者
事件即事实来源
重放推导状态
事件携带状态转移
生产者
事件含完整快照
消费者本地副本
事件通知
生产者
仅事件类型
消费者回查详情
2.3 解耦的三个层次
事件驱动带来的解耦可以拆成三层:
#mermaid-svg-3wsGO6UaQ9sJBhro{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3wsGO6UaQ9sJBhro .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3wsGO6UaQ9sJBhro .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3wsGO6UaQ9sJBhro .error-icon{fill:#552222;}#mermaid-svg-3wsGO6UaQ9sJBhro .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3wsGO6UaQ9sJBhro .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3wsGO6UaQ9sJBhro .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3wsGO6UaQ9sJBhro .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3wsGO6UaQ9sJBhro .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3wsGO6UaQ9sJBhro .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3wsGO6UaQ9sJBhro .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3wsGO6UaQ9sJBhro .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3wsGO6UaQ9sJBhro .marker.cross{stroke:#333333;}#mermaid-svg-3wsGO6UaQ9sJBhro svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3wsGO6UaQ9sJBhro p{margin:0;}#mermaid-svg-3wsGO6UaQ9sJBhro .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-3wsGO6UaQ9sJBhro .cluster-label text{fill:#333;}#mermaid-svg-3wsGO6UaQ9sJBhro .cluster-label span{color:#333;}#mermaid-svg-3wsGO6UaQ9sJBhro .cluster-label span p{background-color:transparent;}#mermaid-svg-3wsGO6UaQ9sJBhro .label text,#mermaid-svg-3wsGO6UaQ9sJBhro span{fill:#333;color:#333;}#mermaid-svg-3wsGO6UaQ9sJBhro .node rect,#mermaid-svg-3wsGO6UaQ9sJBhro .node circle,#mermaid-svg-3wsGO6UaQ9sJBhro .node ellipse,#mermaid-svg-3wsGO6UaQ9sJBhro .node polygon,#mermaid-svg-3wsGO6UaQ9sJBhro .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3wsGO6UaQ9sJBhro .rough-node .label text,#mermaid-svg-3wsGO6UaQ9sJBhro .node .label text,#mermaid-svg-3wsGO6UaQ9sJBhro .image-shape .label,#mermaid-svg-3wsGO6UaQ9sJBhro .icon-shape .label{text-anchor:middle;}#mermaid-svg-3wsGO6UaQ9sJBhro .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3wsGO6UaQ9sJBhro .rough-node .label,#mermaid-svg-3wsGO6UaQ9sJBhro .node .label,#mermaid-svg-3wsGO6UaQ9sJBhro .image-shape .label,#mermaid-svg-3wsGO6UaQ9sJBhro .icon-shape .label{text-align:center;}#mermaid-svg-3wsGO6UaQ9sJBhro .node.clickable{cursor:pointer;}#mermaid-svg-3wsGO6UaQ9sJBhro .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3wsGO6UaQ9sJBhro .arrowheadPath{fill:#333333;}#mermaid-svg-3wsGO6UaQ9sJBhro .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3wsGO6UaQ9sJBhro .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3wsGO6UaQ9sJBhro .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3wsGO6UaQ9sJBhro .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3wsGO6UaQ9sJBhro .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3wsGO6UaQ9sJBhro .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3wsGO6UaQ9sJBhro .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3wsGO6UaQ9sJBhro .cluster text{fill:#333;}#mermaid-svg-3wsGO6UaQ9sJBhro .cluster span{color:#333;}#mermaid-svg-3wsGO6UaQ9sJBhro div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3wsGO6UaQ9sJBhro .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3wsGO6UaQ9sJBhro rect.text{fill:none;stroke-width:0;}#mermaid-svg-3wsGO6UaQ9sJBhro .icon-shape,#mermaid-svg-3wsGO6UaQ9sJBhro .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3wsGO6UaQ9sJBhro .icon-shape p,#mermaid-svg-3wsGO6UaQ9sJBhro .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3wsGO6UaQ9sJBhro .icon-shape .label rect,#mermaid-svg-3wsGO6UaQ9sJBhro .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3wsGO6UaQ9sJBhro .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3wsGO6UaQ9sJBhro .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3wsGO6UaQ9sJBhro :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
订单服务生产者
消息中间件事件总线
库存服务
积分服务
通知服务
数据仓库
3. 适用场景与不适用场景
3.1 适合引入 EDA 的场景
- 跨团队、跨域的业务协作:订单、库存、履约、结算分属不同团队,通过事件契约协作比 RPC 接口更稳定。
- 异步长流程:下单后发短信、发优惠券、更新报表,这些动作不需要阻塞主流程。
- 流量削峰:秒杀场景下把写请求投递到队列,后端按自身能力匀速消费。
- 多下游数据分发:一份业务变更需要同步到搜索、缓存、数仓、风控等多个系统。
- 审计与回放需求:金融、交易类系统需要完整记录状态变化过程。
3.2 不适合的场景
- 强一致、低延迟的同步查询:用户下单后立刻要看到订单详情,这类读请求走同步接口更合适。
- 简单 CRUD 且调用链很短:引入消息中间件反而增加运维成本和排障难度。
- 需要严格顺序且吞吐不高的场景:用数据库事务或单线程处理可能更简单可靠。
经验法则:当「调用方不关心结果」或「有多个不相关的下游」时,才考虑事件驱动;否则优先保持同步。
可以用一张决策图快速判断是否该引入事件驱动:
#mermaid-svg-AUl4BzHDeE3OZVOw{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AUl4BzHDeE3OZVOw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AUl4BzHDeE3OZVOw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AUl4BzHDeE3OZVOw .error-icon{fill:#552222;}#mermaid-svg-AUl4BzHDeE3OZVOw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AUl4BzHDeE3OZVOw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AUl4BzHDeE3OZVOw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AUl4BzHDeE3OZVOw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AUl4BzHDeE3OZVOw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AUl4BzHDeE3OZVOw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AUl4BzHDeE3OZVOw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AUl4BzHDeE3OZVOw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AUl4BzHDeE3OZVOw .marker.cross{stroke:#333333;}#mermaid-svg-AUl4BzHDeE3OZVOw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AUl4BzHDeE3OZVOw p{margin:0;}#mermaid-svg-AUl4BzHDeE3OZVOw .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-AUl4BzHDeE3OZVOw .cluster-label text{fill:#333;}#mermaid-svg-AUl4BzHDeE3OZVOw .cluster-label span{color:#333;}#mermaid-svg-AUl4BzHDeE3OZVOw .cluster-label span p{background-color:transparent;}#mermaid-svg-AUl4BzHDeE3OZVOw .label text,#mermaid-svg-AUl4BzHDeE3OZVOw span{fill:#333;color:#333;}#mermaid-svg-AUl4BzHDeE3OZVOw .node rect,#mermaid-svg-AUl4BzHDeE3OZVOw .node circle,#mermaid-svg-AUl4BzHDeE3OZVOw .node ellipse,#mermaid-svg-AUl4BzHDeE3OZVOw .node polygon,#mermaid-svg-AUl4BzHDeE3OZVOw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AUl4BzHDeE3OZVOw .rough-node .label text,#mermaid-svg-AUl4BzHDeE3OZVOw .node .label text,#mermaid-svg-AUl4BzHDeE3OZVOw .image-shape .label,#mermaid-svg-AUl4BzHDeE3OZVOw .icon-shape .label{text-anchor:middle;}#mermaid-svg-AUl4BzHDeE3OZVOw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AUl4BzHDeE3OZVOw .rough-node .label,#mermaid-svg-AUl4BzHDeE3OZVOw .node .label,#mermaid-svg-AUl4BzHDeE3OZVOw .image-shape .label,#mermaid-svg-AUl4BzHDeE3OZVOw .icon-shape .label{text-align:center;}#mermaid-svg-AUl4BzHDeE3OZVOw .node.clickable{cursor:pointer;}#mermaid-svg-AUl4BzHDeE3OZVOw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AUl4BzHDeE3OZVOw .arrowheadPath{fill:#333333;}#mermaid-svg-AUl4BzHDeE3OZVOw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AUl4BzHDeE3OZVOw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AUl4BzHDeE3OZVOw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AUl4BzHDeE3OZVOw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AUl4BzHDeE3OZVOw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AUl4BzHDeE3OZVOw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AUl4BzHDeE3OZVOw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AUl4BzHDeE3OZVOw .cluster text{fill:#333;}#mermaid-svg-AUl4BzHDeE3OZVOw .cluster span{color:#333;}#mermaid-svg-AUl4BzHDeE3OZVOw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AUl4BzHDeE3OZVOw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AUl4BzHDeE3OZVOw rect.text{fill:none;stroke-width:0;}#mermaid-svg-AUl4BzHDeE3OZVOw .icon-shape,#mermaid-svg-AUl4BzHDeE3OZVOw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AUl4BzHDeE3OZVOw .icon-shape p,#mermaid-svg-AUl4BzHDeE3OZVOw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AUl4BzHDeE3OZVOw .icon-shape .label rect,#mermaid-svg-AUl4BzHDeE3OZVOw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AUl4BzHDeE3OZVOw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AUl4BzHDeE3OZVOw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AUl4BzHDeE3OZVOw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
关心且需强一致
不关心结果
是
否
是
否
有一个业务动作
调用方关心结果吗
保持同步调用
是否存在多个不相关下游
引入事件驱动
是否需要削峰或审计回放
4. 消息队列选型:Kafka、RabbitMQ、Pulsar 对比
选型是 EDA 落地时最现实的问题。三者定位差异明显,不能简单用「谁更好」来回答。
4.1 核心架构差异
- Kafka:基于分区日志(Partitioned Log),消息持久化在磁盘上按顺序追加,消费者通过 offset 自主拉取。吞吐极高,天然支持消息回放。
- RabbitMQ:基于 AMQP 协议的 Broker 模型,消息被路由到 Exchange 再分发到 Queue,消费后通常删除。路由能力灵活,延迟低。
- Pulsar:采用计算存储分离架构,Broker 无状态、存储交给 BookKeeper。同时支持队列语义与流语义,原生支持多租户和跨地域复制。
4.2 对比表
| 模型 | 分区日志 | 队列 + 交换机 | 分区日志 + 分层存储 |
| 吞吐量 | 极高(十万级+) | 中等(万级) | 高(十万级) |
| 延迟 | 毫秒级 | 亚毫秒~毫秒级 | 毫秒级 |
| 消息回放 | 原生支持 | 不支持(需插件) | 原生支持 |
| 路由灵活性 | 一般 | 强(多种 Exchange) | 较强 |
| 顺序保证 | 分区内有序 | 单队列有序 | 分区内有序 |
| 多租户 | 弱 | 弱 | 原生支持 |
| 运维复杂度 | 中 | 低 | 高 |
| 典型场景 | 日志、埋点、流处理 | 业务解耦、任务队列 | 多租户平台、跨机房 |
4.3 选型建议
- 团队规模小、业务以任务分发为主 → RabbitMQ,上手快、管理界面友好。
- 需要高吞吐、数据回放、与 Flink/Spark 生态打通 → Kafka。
- 有多租户隔离、跨地域容灾、存算分离诉求 → Pulsar,但要评估运维投入。
选型时可以按下面的路径逐层收敛:
#mermaid-svg-nNDahxXQfEhqgCX7{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nNDahxXQfEhqgCX7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nNDahxXQfEhqgCX7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nNDahxXQfEhqgCX7 .error-icon{fill:#552222;}#mermaid-svg-nNDahxXQfEhqgCX7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nNDahxXQfEhqgCX7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nNDahxXQfEhqgCX7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nNDahxXQfEhqgCX7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nNDahxXQfEhqgCX7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nNDahxXQfEhqgCX7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nNDahxXQfEhqgCX7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nNDahxXQfEhqgCX7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nNDahxXQfEhqgCX7 .marker.cross{stroke:#333333;}#mermaid-svg-nNDahxXQfEhqgCX7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nNDahxXQfEhqgCX7 p{margin:0;}#mermaid-svg-nNDahxXQfEhqgCX7 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-nNDahxXQfEhqgCX7 .cluster-label text{fill:#333;}#mermaid-svg-nNDahxXQfEhqgCX7 .cluster-label span{color:#333;}#mermaid-svg-nNDahxXQfEhqgCX7 .cluster-label span p{background-color:transparent;}#mermaid-svg-nNDahxXQfEhqgCX7 .label text,#mermaid-svg-nNDahxXQfEhqgCX7 span{fill:#333;color:#333;}#mermaid-svg-nNDahxXQfEhqgCX7 .node rect,#mermaid-svg-nNDahxXQfEhqgCX7 .node circle,#mermaid-svg-nNDahxXQfEhqgCX7 .node ellipse,#mermaid-svg-nNDahxXQfEhqgCX7 .node polygon,#mermaid-svg-nNDahxXQfEhqgCX7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nNDahxXQfEhqgCX7 .rough-node .label text,#mermaid-svg-nNDahxXQfEhqgCX7 .node .label text,#mermaid-svg-nNDahxXQfEhqgCX7 .image-shape .label,#mermaid-svg-nNDahxXQfEhqgCX7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-nNDahxXQfEhqgCX7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nNDahxXQfEhqgCX7 .rough-node .label,#mermaid-svg-nNDahxXQfEhqgCX7 .node .label,#mermaid-svg-nNDahxXQfEhqgCX7 .image-shape .label,#mermaid-svg-nNDahxXQfEhqgCX7 .icon-shape .label{text-align:center;}#mermaid-svg-nNDahxXQfEhqgCX7 .node.clickable{cursor:pointer;}#mermaid-svg-nNDahxXQfEhqgCX7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nNDahxXQfEhqgCX7 .arrowheadPath{fill:#333333;}#mermaid-svg-nNDahxXQfEhqgCX7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nNDahxXQfEhqgCX7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nNDahxXQfEhqgCX7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nNDahxXQfEhqgCX7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nNDahxXQfEhqgCX7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nNDahxXQfEhqgCX7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nNDahxXQfEhqgCX7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nNDahxXQfEhqgCX7 .cluster text{fill:#333;}#mermaid-svg-nNDahxXQfEhqgCX7 .cluster span{color:#333;}#mermaid-svg-nNDahxXQfEhqgCX7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nNDahxXQfEhqgCX7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nNDahxXQfEhqgCX7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-nNDahxXQfEhqgCX7 .icon-shape,#mermaid-svg-nNDahxXQfEhqgCX7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nNDahxXQfEhqgCX7 .icon-shape p,#mermaid-svg-nNDahxXQfEhqgCX7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nNDahxXQfEhqgCX7 .icon-shape .label rect,#mermaid-svg-nNDahxXQfEhqgCX7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nNDahxXQfEhqgCX7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nNDahxXQfEhqgCX7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nNDahxXQfEhqgCX7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
是
否
否
是
否
开始选型
需要高吞吐与消息回放
需要多租户或跨机房容灾
Pulsar
Kafka
以任务分发与灵活路由为主
RabbitMQ
评估业务量后重新判断
// Kafka 生产者示例:发送订单支付事件
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("acks", "all");
props.put("enable.idempotence", "true");
try (KafkaProducer<String, String> producer = new KafkaProducer<>(props)) {
ProducerRecord<String, String> record =
new ProducerRecord<>("order-events", "order-1001",
"{\\"type\\":\\"OrderPaid\\",\\"orderId\\":\\"1001\\",\\"amount\\":199.00}");
producer.send(record, (metadata, ex) -> {
if (ex != null) {
log.error("发送订单事件失败", ex);
} else {
log.info("已发送到分区 {} offset {}", metadata.partition(), metadata.offset());
}
});
}
5. 事件溯源与 CQRS 模式实践
5.1 事件溯源(Event Sourcing)
传统做法只保存「当前状态」,事件溯源则保存「导致状态变化的所有事件」。账户余额不是一行数字,而是一串 Deposited、Withdrawn 事件的重放结果。
优势:
- 完整的审计轨迹,任何时刻的状态都可追溯。
- 可以基于历史事件重建新视图、修复 Bug 后重放。
- 天然契合事件驱动,写模型只追加不修改。
代价:
- 查询需要重放或依赖投影,读模型必须单独建设。
- 事件版本演进(Schema 变更)需要谨慎设计。
- 快照机制必不可少,否则重放成本随时间线性增长。
5.2 CQRS(命令查询职责分离)
CQRS 把写模型与读模型分离:命令侧处理业务规则并产生事件,读侧订阅事件构建面向查询的投影(Projection)。
#mermaid-svg-h54BqIWzUzTVZJgK{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-h54BqIWzUzTVZJgK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-h54BqIWzUzTVZJgK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-h54BqIWzUzTVZJgK .error-icon{fill:#552222;}#mermaid-svg-h54BqIWzUzTVZJgK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h54BqIWzUzTVZJgK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-h54BqIWzUzTVZJgK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h54BqIWzUzTVZJgK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h54BqIWzUzTVZJgK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-h54BqIWzUzTVZJgK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h54BqIWzUzTVZJgK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h54BqIWzUzTVZJgK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h54BqIWzUzTVZJgK .marker.cross{stroke:#333333;}#mermaid-svg-h54BqIWzUzTVZJgK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h54BqIWzUzTVZJgK p{margin:0;}#mermaid-svg-h54BqIWzUzTVZJgK .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-h54BqIWzUzTVZJgK .cluster-label text{fill:#333;}#mermaid-svg-h54BqIWzUzTVZJgK .cluster-label span{color:#333;}#mermaid-svg-h54BqIWzUzTVZJgK .cluster-label span p{background-color:transparent;}#mermaid-svg-h54BqIWzUzTVZJgK .label text,#mermaid-svg-h54BqIWzUzTVZJgK span{fill:#333;color:#333;}#mermaid-svg-h54BqIWzUzTVZJgK .node rect,#mermaid-svg-h54BqIWzUzTVZJgK .node circle,#mermaid-svg-h54BqIWzUzTVZJgK .node ellipse,#mermaid-svg-h54BqIWzUzTVZJgK .node polygon,#mermaid-svg-h54BqIWzUzTVZJgK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h54BqIWzUzTVZJgK .rough-node .label text,#mermaid-svg-h54BqIWzUzTVZJgK .node .label text,#mermaid-svg-h54BqIWzUzTVZJgK .image-shape .label,#mermaid-svg-h54BqIWzUzTVZJgK .icon-shape .label{text-anchor:middle;}#mermaid-svg-h54BqIWzUzTVZJgK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-h54BqIWzUzTVZJgK .rough-node .label,#mermaid-svg-h54BqIWzUzTVZJgK .node .label,#mermaid-svg-h54BqIWzUzTVZJgK .image-shape .label,#mermaid-svg-h54BqIWzUzTVZJgK .icon-shape .label{text-align:center;}#mermaid-svg-h54BqIWzUzTVZJgK .node.clickable{cursor:pointer;}#mermaid-svg-h54BqIWzUzTVZJgK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-h54BqIWzUzTVZJgK .arrowheadPath{fill:#333333;}#mermaid-svg-h54BqIWzUzTVZJgK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-h54BqIWzUzTVZJgK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-h54BqIWzUzTVZJgK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h54BqIWzUzTVZJgK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-h54BqIWzUzTVZJgK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h54BqIWzUzTVZJgK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-h54BqIWzUzTVZJgK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-h54BqIWzUzTVZJgK .cluster text{fill:#333;}#mermaid-svg-h54BqIWzUzTVZJgK .cluster span{color:#333;}#mermaid-svg-h54BqIWzUzTVZJgK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-h54BqIWzUzTVZJgK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-h54BqIWzUzTVZJgK rect.text{fill:none;stroke-width:0;}#mermaid-svg-h54BqIWzUzTVZJgK .icon-shape,#mermaid-svg-h54BqIWzUzTVZJgK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h54BqIWzUzTVZJgK .icon-shape p,#mermaid-svg-h54BqIWzUzTVZJgK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-h54BqIWzUzTVZJgK .icon-shape .label rect,#mermaid-svg-h54BqIWzUzTVZJgK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h54BqIWzUzTVZJgK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-h54BqIWzUzTVZJgK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-h54BqIWzUzTVZJgK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
客户端命令
写模型(聚合根)
事件存储 Event Store
投影器 Projector
读库:订单列表
读库:统计报表
客户端查询
5.3 落地要点
// 简化的聚合根:基于事件重建状态
public class Account {
private String id;
private BigDecimal balance = BigDecimal.ZERO;
private final List<DomainEvent> changes = new ArrayList<>();
public static Account replay(List<DomainEvent> history) {
Account account = new Account();
history.forEach(account::apply);
return account;
}
public void withdraw(BigDecimal amount) {
if (balance.compareTo(amount) < 0) {
throw new IllegalStateException("余额不足");
}
apply(new MoneyWithdrawn(id, amount));
}
private void apply(DomainEvent event) {
if (event instanceof MoneyWithdrawn e) {
balance = balance.subtract(e.amount());
} else if (event instanceof MoneyDeposited e) {
balance = balance.add(e.amount());
}
changes.add(event);
}
}
6. 事件驱动系统中的可观测性挑战
异步链路一旦出问题,排障难度远高于同步调用:请求没有统一的 TraceId 贯穿、消息可能重复或乱序、消费积压往往在业务方投诉后才被发现。
6.1 三大支柱在 EDA 中的落地
- 日志(Logging):在事件头中携带 traceId、eventId、correlationId,生产与消费两侧都打印结构化日志。
- 指标(Metrics):重点监控生产速率、消费速率、消费延迟(Lag)、重试次数、死信队列长度。
- 链路追踪(Tracing):把消息发送与消费作为 Span,通过消息头传递上下文,形成跨服务的完整调用链。
6.2 关键监控指标
| Consumer Lag | 消费位点与最新位点的差值 | 持续增长超过阈值即告警 |
| 消费失败率 | 失败消息数 / 总消费数 | 超过 1% 关注,超过 5% 告警 |
| 端到端延迟 | 事件产生到被处理完成的时间 | 按业务 SLA 设定 |
| 死信队列长度 | 多次重试仍失败的消息数 | 大于 0 即需人工介入 |
| 重复消费率 | 重复消息占比 | 突增说明幂等或位点提交异常 |
6.3 实践建议
一次事件从生产到消费的完整可观测链路如下:
可观测平台消费者服务消息中间件生产者服务可观测平台消费者服务消息中间件生产者服务#mermaid-svg-nYLwABnKvDCnMdHE{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nYLwABnKvDCnMdHE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nYLwABnKvDCnMdHE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nYLwABnKvDCnMdHE .error-icon{fill:#552222;}#mermaid-svg-nYLwABnKvDCnMdHE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nYLwABnKvDCnMdHE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nYLwABnKvDCnMdHE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nYLwABnKvDCnMdHE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nYLwABnKvDCnMdHE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nYLwABnKvDCnMdHE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nYLwABnKvDCnMdHE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nYLwABnKvDCnMdHE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nYLwABnKvDCnMdHE .marker.cross{stroke:#333333;}#mermaid-svg-nYLwABnKvDCnMdHE svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nYLwABnKvDCnMdHE p{margin:0;}#mermaid-svg-nYLwABnKvDCnMdHE .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-nYLwABnKvDCnMdHE text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-nYLwABnKvDCnMdHE .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-nYLwABnKvDCnMdHE .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-nYLwABnKvDCnMdHE .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-nYLwABnKvDCnMdHE .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-nYLwABnKvDCnMdHE #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-nYLwABnKvDCnMdHE .sequenceNumber{fill:white;}#mermaid-svg-nYLwABnKvDCnMdHE #sequencenumber{fill:#333;}#mermaid-svg-nYLwABnKvDCnMdHE #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-nYLwABnKvDCnMdHE .messageText{fill:#333;stroke:none;}#mermaid-svg-nYLwABnKvDCnMdHE .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-nYLwABnKvDCnMdHE .labelText,#mermaid-svg-nYLwABnKvDCnMdHE .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-nYLwABnKvDCnMdHE .loopText,#mermaid-svg-nYLwABnKvDCnMdHE .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-nYLwABnKvDCnMdHE .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-nYLwABnKvDCnMdHE .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-nYLwABnKvDCnMdHE .noteText,#mermaid-svg-nYLwABnKvDCnMdHE .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-nYLwABnKvDCnMdHE .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-nYLwABnKvDCnMdHE .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-nYLwABnKvDCnMdHE .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-nYLwABnKvDCnMdHE .actorPopupMenu{position:absolute;}#mermaid-svg-nYLwABnKvDCnMdHE .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-nYLwABnKvDCnMdHE .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-nYLwABnKvDCnMdHE .actor-man circle,#mermaid-svg-nYLwABnKvDCnMdHE line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-nYLwABnKvDCnMdHE :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}生成 traceId / eventId发送事件(携带事件信封)上报生产指标与日志投递事件从消息头恢复追踪上下文上报消费耗时、结果、重试次数提交消费位点聚合 Lag、失败率并触发告警
7. 总结
事件驱动架构不是银弹,而是一种用「最终一致」换取「解耦与弹性」的架构取舍:
- 核心概念上,先分清事件、命令与查询,再选择合适的拓扑形态。
- 适用场景上,只在调用方不关心结果、或存在多个不相关下游时引入。
- 中间件选型上,Kafka 重吞吐与回放,RabbitMQ 重路由与易用,Pulsar 重多租户与存算分离。
- 事件溯源与 CQRS 适合核心域,务必配套快照、幂等与版本演进策略。
- 可观测性是 EDA 的生命线,事件信封、统一埋点与事件目录缺一不可。
落地时建议从一条非核心链路开始试点,跑通「事件契约 → 消费幂等 → 监控告警」的闭环,再逐步推广到核心业务。


