事件驱动架构从概念到落地——让系统像神经反射一样响应变化

🔔 本文 8000+ 字深度原创,含 10+ 张架构图和 iPaaS 事件中心、IoT、电商三大实战案例拆解。创作不易,如果对你有帮助,请点赞 👍 收藏 ⭐ 关注 🔥 三连支持,你的认可是我持续输出的最大动力!


写在前面

做了几年分布式系统之后,我发现一个很有意思的现象:

大部分系统的"慢"和"脆",不是因为单个服务写得差,而是因为服务之间"连"得太紧。

你肯定见过这些场景:

  • 用户下了一个订单,系统同步调了库存服务、支付服务、通知服务、积分服务------一个超時全链路挂掉
  • 老板说"加一个新的下游系统",你打开代码发现要改上游三四个服务的代码
  • 系统吞吐量上不去,因为所有操作都串行执行,一个慢查询拖垮全链路
  • Webhook 回调超时了,但业务逻辑还在同步执行,连接池被撑爆

这些问题的根源都是同一个------同步调用的紧耦合架构

事件驱动架构(Event-Driven Architecture,EDA)就是为了解决这类问题而生的。它的核心思想很简单:系统中的组件不直接调用彼此,而是通过"事件"来通信------我做了什么(事件),谁关心谁来听。

本文从事件驱动的本质出发,拆解核心概念、架构拓扑、消息可靠投递、事件溯源、在 iPaaS 平台事件中心的实战落地,以及我在真实项目中踩过的坑。不讲教科书定义,只讲"能落地"的东西。


一、事件驱动架构到底在解决什么问题?

1.1 同步调用的"三重困境"

在传统的 RPC/HTTP 同步调用架构中,系统面临三重困境:
#mermaid-svg-jNQdkODuinvZ4p2X{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-jNQdkODuinvZ4p2X .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jNQdkODuinvZ4p2X .error-icon{fill:#552222;}#mermaid-svg-jNQdkODuinvZ4p2X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jNQdkODuinvZ4p2X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jNQdkODuinvZ4p2X .marker.cross{stroke:#333333;}#mermaid-svg-jNQdkODuinvZ4p2X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jNQdkODuinvZ4p2X p{margin:0;}#mermaid-svg-jNQdkODuinvZ4p2X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster-label text{fill:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster-label span{color:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster-label span p{background-color:transparent;}#mermaid-svg-jNQdkODuinvZ4p2X .label text,#mermaid-svg-jNQdkODuinvZ4p2X span{fill:#333;color:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .node rect,#mermaid-svg-jNQdkODuinvZ4p2X .node circle,#mermaid-svg-jNQdkODuinvZ4p2X .node ellipse,#mermaid-svg-jNQdkODuinvZ4p2X .node polygon,#mermaid-svg-jNQdkODuinvZ4p2X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jNQdkODuinvZ4p2X .rough-node .label text,#mermaid-svg-jNQdkODuinvZ4p2X .node .label text,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape .label,#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape .label{text-anchor:middle;}#mermaid-svg-jNQdkODuinvZ4p2X .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jNQdkODuinvZ4p2X .rough-node .label,#mermaid-svg-jNQdkODuinvZ4p2X .node .label,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape .label,#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape .label{text-align:center;}#mermaid-svg-jNQdkODuinvZ4p2X .node.clickable{cursor:pointer;}#mermaid-svg-jNQdkODuinvZ4p2X .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jNQdkODuinvZ4p2X .arrowheadPath{fill:#333333;}#mermaid-svg-jNQdkODuinvZ4p2X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jNQdkODuinvZ4p2X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jNQdkODuinvZ4p2X .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jNQdkODuinvZ4p2X .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jNQdkODuinvZ4p2X .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jNQdkODuinvZ4p2X .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jNQdkODuinvZ4p2X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster text{fill:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster span{color:#333;}#mermaid-svg-jNQdkODuinvZ4p2X 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-jNQdkODuinvZ4p2X .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jNQdkODuinvZ4p2X rect.text{fill:none;stroke-width:0;}#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape p,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape .label rect,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jNQdkODuinvZ4p2X .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jNQdkODuinvZ4p2X .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jNQdkODuinvZ4p2X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 困境三:吞吐瓶颈
串行执行
串行执行
串行执行
API入口
处理1
处理2
处理3
困境二:扩展耦合
硬编码调用
硬编码调用
硬编码调用
订单服务
积分服务
风控服务
数据服务
困境一:级联故障
同步调用
同步调用
超时!
订单服务
库存服务
通知服务
全链路阻塞

困境 表现 后果
级联故障 下游超时,上游连接堆积 一个慢服务拖垮全系统
扩展耦合 加一个下游要改上游代码 系统无法独立演进
吞吐瓶颈 串行同步执行 整体延迟 = 各环节延迟之和

1.2 事件驱动的核心主张

事件驱动架构用一句话概括:系统中的事件是核心,组件通过异步产生和响应事件来协作,解耦生产者和消费者。
#mermaid-svg-RURz89mARnW2Wv71{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-RURz89mARnW2Wv71 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RURz89mARnW2Wv71 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RURz89mARnW2Wv71 .error-icon{fill:#552222;}#mermaid-svg-RURz89mARnW2Wv71 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RURz89mARnW2Wv71 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RURz89mARnW2Wv71 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RURz89mARnW2Wv71 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RURz89mARnW2Wv71 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RURz89mARnW2Wv71 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RURz89mARnW2Wv71 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RURz89mARnW2Wv71 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RURz89mARnW2Wv71 .marker.cross{stroke:#333333;}#mermaid-svg-RURz89mARnW2Wv71 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RURz89mARnW2Wv71 p{margin:0;}#mermaid-svg-RURz89mARnW2Wv71 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RURz89mARnW2Wv71 .cluster-label text{fill:#333;}#mermaid-svg-RURz89mARnW2Wv71 .cluster-label span{color:#333;}#mermaid-svg-RURz89mARnW2Wv71 .cluster-label span p{background-color:transparent;}#mermaid-svg-RURz89mARnW2Wv71 .label text,#mermaid-svg-RURz89mARnW2Wv71 span{fill:#333;color:#333;}#mermaid-svg-RURz89mARnW2Wv71 .node rect,#mermaid-svg-RURz89mARnW2Wv71 .node circle,#mermaid-svg-RURz89mARnW2Wv71 .node ellipse,#mermaid-svg-RURz89mARnW2Wv71 .node polygon,#mermaid-svg-RURz89mARnW2Wv71 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RURz89mARnW2Wv71 .rough-node .label text,#mermaid-svg-RURz89mARnW2Wv71 .node .label text,#mermaid-svg-RURz89mARnW2Wv71 .image-shape .label,#mermaid-svg-RURz89mARnW2Wv71 .icon-shape .label{text-anchor:middle;}#mermaid-svg-RURz89mARnW2Wv71 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RURz89mARnW2Wv71 .rough-node .label,#mermaid-svg-RURz89mARnW2Wv71 .node .label,#mermaid-svg-RURz89mARnW2Wv71 .image-shape .label,#mermaid-svg-RURz89mARnW2Wv71 .icon-shape .label{text-align:center;}#mermaid-svg-RURz89mARnW2Wv71 .node.clickable{cursor:pointer;}#mermaid-svg-RURz89mARnW2Wv71 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RURz89mARnW2Wv71 .arrowheadPath{fill:#333333;}#mermaid-svg-RURz89mARnW2Wv71 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RURz89mARnW2Wv71 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RURz89mARnW2Wv71 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RURz89mARnW2Wv71 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RURz89mARnW2Wv71 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RURz89mARnW2Wv71 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RURz89mARnW2Wv71 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RURz89mARnW2Wv71 .cluster text{fill:#333;}#mermaid-svg-RURz89mARnW2Wv71 .cluster span{color:#333;}#mermaid-svg-RURz89mARnW2Wv71 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-RURz89mARnW2Wv71 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RURz89mARnW2Wv71 rect.text{fill:none;stroke-width:0;}#mermaid-svg-RURz89mARnW2Wv71 .icon-shape,#mermaid-svg-RURz89mARnW2Wv71 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RURz89mARnW2Wv71 .icon-shape p,#mermaid-svg-RURz89mARnW2Wv71 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RURz89mARnW2Wv71 .icon-shape .label rect,#mermaid-svg-RURz89mARnW2Wv71 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RURz89mARnW2Wv71 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RURz89mARnW2Wv71 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RURz89mARnW2Wv71 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 发布事件
发布事件
订阅分发
订阅分发
订阅分发
生产者A
事件总线

Event Broker
生产者B
消费者1
消费者2
消费者3

三个核心主张:

  1. 解耦(Decoupling):生产者不知道消费者的存在,消费者不知道事件从哪来。加一个新的消费者?注册一个订阅就行,生产者的代码一行不改。

  2. 异步(Asynchrony):生产者发出事件就返回,不等消费者处理完。整体延迟从"串行累加"变成"并行处理"。

  3. 弹性(Resilience):消费者挂了?事件在消息队列里等着,恢复后继续消费。不会因为一个节点故障导致全链路崩溃。

1.3 事件驱动适合什么系统?

系统类型 是否适合 EDA 原因
CRUD 管理后台 ❌ 不适合 请求-响应模型,同步调用更直观
报表查询系统 ❌ 不适合 用户需要即时结果,异步无意义
IoT 物联网平台 ✅ 非常适合 海量设备异步上报,高吞吐实时处理
实时推荐系统 ✅ 非常适合 用户行为事件流 → 实时特征计算 → 推荐更新
iPaaS 集成平台 ✅ 非常适合 Webhook/CDC/定时触发 → 异步流程编排
电商交易系统 ✅ 部分适合 下单后的库存、通知、积分适合事件驱动
金融交易系统 ⚠️ 谨慎使用 高吞吐场景适合,但强一致性场景需额外设计

判断标准:你的系统是否存在"一个操作触发多个下游"、"需要高吞吐异步处理"、"下游数量经常变化"这三个特征中的至少一个。


二、事件驱动架构的核心概念

2.1 事件(Event)

事件是"已经发生的事实"------不可变的、轻量级的、带有时间戳的状态变更记录。
#mermaid-svg-40snaFmQ2a0nh5tM{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-40snaFmQ2a0nh5tM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-40snaFmQ2a0nh5tM .error-icon{fill:#552222;}#mermaid-svg-40snaFmQ2a0nh5tM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-40snaFmQ2a0nh5tM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-40snaFmQ2a0nh5tM .marker.cross{stroke:#333333;}#mermaid-svg-40snaFmQ2a0nh5tM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-40snaFmQ2a0nh5tM p{margin:0;}#mermaid-svg-40snaFmQ2a0nh5tM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster-label text{fill:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster-label span{color:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster-label span p{background-color:transparent;}#mermaid-svg-40snaFmQ2a0nh5tM .label text,#mermaid-svg-40snaFmQ2a0nh5tM span{fill:#333;color:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .node rect,#mermaid-svg-40snaFmQ2a0nh5tM .node circle,#mermaid-svg-40snaFmQ2a0nh5tM .node ellipse,#mermaid-svg-40snaFmQ2a0nh5tM .node polygon,#mermaid-svg-40snaFmQ2a0nh5tM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-40snaFmQ2a0nh5tM .rough-node .label text,#mermaid-svg-40snaFmQ2a0nh5tM .node .label text,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape .label,#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape .label{text-anchor:middle;}#mermaid-svg-40snaFmQ2a0nh5tM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-40snaFmQ2a0nh5tM .rough-node .label,#mermaid-svg-40snaFmQ2a0nh5tM .node .label,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape .label,#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape .label{text-align:center;}#mermaid-svg-40snaFmQ2a0nh5tM .node.clickable{cursor:pointer;}#mermaid-svg-40snaFmQ2a0nh5tM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-40snaFmQ2a0nh5tM .arrowheadPath{fill:#333333;}#mermaid-svg-40snaFmQ2a0nh5tM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-40snaFmQ2a0nh5tM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-40snaFmQ2a0nh5tM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-40snaFmQ2a0nh5tM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-40snaFmQ2a0nh5tM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-40snaFmQ2a0nh5tM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-40snaFmQ2a0nh5tM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster text{fill:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster span{color:#333;}#mermaid-svg-40snaFmQ2a0nh5tM 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-40snaFmQ2a0nh5tM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-40snaFmQ2a0nh5tM rect.text{fill:none;stroke-width:0;}#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape p,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape .label rect,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-40snaFmQ2a0nh5tM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-40snaFmQ2a0nh5tM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-40snaFmQ2a0nh5tM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 事件的本质
事件名(过去式)

OrderCreated
事件载荷(Payload)

订单ID/金额/商品列表
元数据(Metadata)

时间戳/来源/追踪ID

事件和命令(Command)的区别:

维度 事件(Event) 命令(Command)
语义 "已经发生了什么" "请去做什么"
命名 过去式:OrderCreated 祈使句:CreateOrder
目标 无特定目标,广播给所有订阅者 有明确目标,发给指定处理器
耦合度 低(生产者不知道谁在消费) 高(调用方知道被调用方)
失败处理 消费者各自重试 调用方重试或回滚
java 复制代码
// 事件:已经发生的事实
public record OrderCreatedEvent(
    String orderId,
    String userId,
    BigDecimal totalAmount,
    List<OrderItem> items,
    Instant createdAt          // 时间戳
) {}

// 命令:要求执行的操作
public record CreateOrderCommand(
    String userId,
    List<OrderItem> items
) {}

2.2 事件通道(Event Channel)

事件从生产者到消费者的传输管道。根据传输方式不同,分为三种:
#mermaid-svg-TknrVCHhrLAf2QZD{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-TknrVCHhrLAf2QZD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TknrVCHhrLAf2QZD .error-icon{fill:#552222;}#mermaid-svg-TknrVCHhrLAf2QZD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TknrVCHhrLAf2QZD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TknrVCHhrLAf2QZD .marker.cross{stroke:#333333;}#mermaid-svg-TknrVCHhrLAf2QZD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TknrVCHhrLAf2QZD p{margin:0;}#mermaid-svg-TknrVCHhrLAf2QZD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster-label text{fill:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster-label span{color:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster-label span p{background-color:transparent;}#mermaid-svg-TknrVCHhrLAf2QZD .label text,#mermaid-svg-TknrVCHhrLAf2QZD span{fill:#333;color:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .node rect,#mermaid-svg-TknrVCHhrLAf2QZD .node circle,#mermaid-svg-TknrVCHhrLAf2QZD .node ellipse,#mermaid-svg-TknrVCHhrLAf2QZD .node polygon,#mermaid-svg-TknrVCHhrLAf2QZD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TknrVCHhrLAf2QZD .rough-node .label text,#mermaid-svg-TknrVCHhrLAf2QZD .node .label text,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape .label,#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape .label{text-anchor:middle;}#mermaid-svg-TknrVCHhrLAf2QZD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TknrVCHhrLAf2QZD .rough-node .label,#mermaid-svg-TknrVCHhrLAf2QZD .node .label,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape .label,#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape .label{text-align:center;}#mermaid-svg-TknrVCHhrLAf2QZD .node.clickable{cursor:pointer;}#mermaid-svg-TknrVCHhrLAf2QZD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TknrVCHhrLAf2QZD .arrowheadPath{fill:#333333;}#mermaid-svg-TknrVCHhrLAf2QZD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TknrVCHhrLAf2QZD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TknrVCHhrLAf2QZD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TknrVCHhrLAf2QZD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TknrVCHhrLAf2QZD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TknrVCHhrLAf2QZD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TknrVCHhrLAf2QZD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster text{fill:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster span{color:#333;}#mermaid-svg-TknrVCHhrLAf2QZD 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-TknrVCHhrLAf2QZD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TknrVCHhrLAf2QZD rect.text{fill:none;stroke-width:0;}#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape p,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape .label rect,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TknrVCHhrLAf2QZD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TknrVCHhrLAf2QZD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TknrVCHhrLAf2QZD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 请求-响应通道
请求事件
响应事件
请求者
请求队列
响应者
响应队列
发布-订阅通道
生产者
Topic
消费者A
消费者B
消费者C
点对点通道
生产者
队列
唯一消费者

通道类型 典型实现 适用场景
点对点 RabbitMQ Queue、RocketMQ 普通消息 任务分发,一个事件只需一个消费者处理
发布-订阅 Kafka Topic、RocketMQ Topic 事件广播,一个事件需要多个消费者各自处理
请求-响应 NATS Request-Reply 需要异步获取结果的场景

2.3 事件代理(Event Broker)

事件代理是事件驱动架构的"中枢神经"------所有事件都经过它来路由和分发。
#mermaid-svg-8qcp7oeZDGL4pONH{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-8qcp7oeZDGL4pONH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8qcp7oeZDGL4pONH .error-icon{fill:#552222;}#mermaid-svg-8qcp7oeZDGL4pONH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8qcp7oeZDGL4pONH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8qcp7oeZDGL4pONH .marker.cross{stroke:#333333;}#mermaid-svg-8qcp7oeZDGL4pONH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8qcp7oeZDGL4pONH p{margin:0;}#mermaid-svg-8qcp7oeZDGL4pONH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster-label text{fill:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster-label span{color:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster-label span p{background-color:transparent;}#mermaid-svg-8qcp7oeZDGL4pONH .label text,#mermaid-svg-8qcp7oeZDGL4pONH span{fill:#333;color:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .node rect,#mermaid-svg-8qcp7oeZDGL4pONH .node circle,#mermaid-svg-8qcp7oeZDGL4pONH .node ellipse,#mermaid-svg-8qcp7oeZDGL4pONH .node polygon,#mermaid-svg-8qcp7oeZDGL4pONH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8qcp7oeZDGL4pONH .rough-node .label text,#mermaid-svg-8qcp7oeZDGL4pONH .node .label text,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape .label,#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape .label{text-anchor:middle;}#mermaid-svg-8qcp7oeZDGL4pONH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8qcp7oeZDGL4pONH .rough-node .label,#mermaid-svg-8qcp7oeZDGL4pONH .node .label,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape .label,#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape .label{text-align:center;}#mermaid-svg-8qcp7oeZDGL4pONH .node.clickable{cursor:pointer;}#mermaid-svg-8qcp7oeZDGL4pONH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8qcp7oeZDGL4pONH .arrowheadPath{fill:#333333;}#mermaid-svg-8qcp7oeZDGL4pONH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8qcp7oeZDGL4pONH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8qcp7oeZDGL4pONH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8qcp7oeZDGL4pONH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8qcp7oeZDGL4pONH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8qcp7oeZDGL4pONH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8qcp7oeZDGL4pONH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster text{fill:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster span{color:#333;}#mermaid-svg-8qcp7oeZDGL4pONH 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-8qcp7oeZDGL4pONH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8qcp7oeZDGL4pONH rect.text{fill:none;stroke-width:0;}#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape p,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape .label rect,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8qcp7oeZDGL4pONH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8qcp7oeZDGL4pONH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8qcp7oeZDGL4pONH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 事件代理
事件接收

协议适配/鉴权
事件存储

持久化/回放
事件路由

Topic/Tag过滤
事件投递

推送/拉取
生产者1
生产者2
消费者1
消费者2
消费者3

主流消息中间件的选型对比:

中间件 吞吐量 延迟 消息模型 适用场景
RocketMQ 十万级/秒 毫秒级 发布-订阅+点对点 电商/金融/企业集成
Kafka 百万级/秒 毫秒级 发布-订阅(Log) 日志/大数据/IoT
RabbitMQ 万级/秒 微秒级 灵活路由 企业内部集成/任务队列
Pulsar 百万级/秒 毫秒级 多租户+分层存储 超大规模多租户场景

我们在 iPaaS 平台选择了 RocketMQ------它的事务消息、顺序消息、延时消息等企业级特性,非常契合集成平台的场景。

2.4 事件处理的三种模式

#mermaid-svg-HhTUkAXIssch2uaE{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-HhTUkAXIssch2uaE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HhTUkAXIssch2uaE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HhTUkAXIssch2uaE .error-icon{fill:#552222;}#mermaid-svg-HhTUkAXIssch2uaE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HhTUkAXIssch2uaE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HhTUkAXIssch2uaE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HhTUkAXIssch2uaE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HhTUkAXIssch2uaE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HhTUkAXIssch2uaE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HhTUkAXIssch2uaE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HhTUkAXIssch2uaE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HhTUkAXIssch2uaE .marker.cross{stroke:#333333;}#mermaid-svg-HhTUkAXIssch2uaE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HhTUkAXIssch2uaE p{margin:0;}#mermaid-svg-HhTUkAXIssch2uaE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HhTUkAXIssch2uaE .cluster-label text{fill:#333;}#mermaid-svg-HhTUkAXIssch2uaE .cluster-label span{color:#333;}#mermaid-svg-HhTUkAXIssch2uaE .cluster-label span p{background-color:transparent;}#mermaid-svg-HhTUkAXIssch2uaE .label text,#mermaid-svg-HhTUkAXIssch2uaE span{fill:#333;color:#333;}#mermaid-svg-HhTUkAXIssch2uaE .node rect,#mermaid-svg-HhTUkAXIssch2uaE .node circle,#mermaid-svg-HhTUkAXIssch2uaE .node ellipse,#mermaid-svg-HhTUkAXIssch2uaE .node polygon,#mermaid-svg-HhTUkAXIssch2uaE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HhTUkAXIssch2uaE .rough-node .label text,#mermaid-svg-HhTUkAXIssch2uaE .node .label text,#mermaid-svg-HhTUkAXIssch2uaE .image-shape .label,#mermaid-svg-HhTUkAXIssch2uaE .icon-shape .label{text-anchor:middle;}#mermaid-svg-HhTUkAXIssch2uaE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HhTUkAXIssch2uaE .rough-node .label,#mermaid-svg-HhTUkAXIssch2uaE .node .label,#mermaid-svg-HhTUkAXIssch2uaE .image-shape .label,#mermaid-svg-HhTUkAXIssch2uaE .icon-shape .label{text-align:center;}#mermaid-svg-HhTUkAXIssch2uaE .node.clickable{cursor:pointer;}#mermaid-svg-HhTUkAXIssch2uaE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HhTUkAXIssch2uaE .arrowheadPath{fill:#333333;}#mermaid-svg-HhTUkAXIssch2uaE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HhTUkAXIssch2uaE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HhTUkAXIssch2uaE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HhTUkAXIssch2uaE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HhTUkAXIssch2uaE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HhTUkAXIssch2uaE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HhTUkAXIssch2uaE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HhTUkAXIssch2uaE .cluster text{fill:#333;}#mermaid-svg-HhTUkAXIssch2uaE .cluster span{color:#333;}#mermaid-svg-HhTUkAXIssch2uaE 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-HhTUkAXIssch2uaE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HhTUkAXIssch2uaE rect.text{fill:none;stroke-width:0;}#mermaid-svg-HhTUkAXIssch2uaE .icon-shape,#mermaid-svg-HhTUkAXIssch2uaE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HhTUkAXIssch2uaE .icon-shape p,#mermaid-svg-HhTUkAXIssch2uaE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HhTUkAXIssch2uaE .icon-shape .label rect,#mermaid-svg-HhTUkAXIssch2uaE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HhTUkAXIssch2uaE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HhTUkAXIssch2uaE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HhTUkAXIssch2uaE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Saga 编排
成功
失败
订单事件
步骤1: 扣库存
步骤2: 扣款
补偿: 恢复库存
复杂事件处理(CEP)
事件流
CEP引擎

模式匹配/时间窗口
触发动作
简单事件处理
事件
单处理器

模式 说明 适用场景
简单事件处理 一个事件触发一个处理器 大多数业务场景
复杂事件处理(CEP) 多个事件组合满足模式后触发 风控/告警/IoT 异常检测
Saga 编排 长事务拆成多个事件驱动的步骤 跨服务的分布式事务

三、事件驱动架构的四种拓扑

3.1 中介者拓扑(Mediator Topology)

#mermaid-svg-CenZqxHc5n47vvBb{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-CenZqxHc5n47vvBb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CenZqxHc5n47vvBb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CenZqxHc5n47vvBb .error-icon{fill:#552222;}#mermaid-svg-CenZqxHc5n47vvBb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CenZqxHc5n47vvBb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CenZqxHc5n47vvBb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CenZqxHc5n47vvBb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CenZqxHc5n47vvBb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CenZqxHc5n47vvBb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CenZqxHc5n47vvBb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CenZqxHc5n47vvBb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CenZqxHc5n47vvBb .marker.cross{stroke:#333333;}#mermaid-svg-CenZqxHc5n47vvBb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CenZqxHc5n47vvBb p{margin:0;}#mermaid-svg-CenZqxHc5n47vvBb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CenZqxHc5n47vvBb .cluster-label text{fill:#333;}#mermaid-svg-CenZqxHc5n47vvBb .cluster-label span{color:#333;}#mermaid-svg-CenZqxHc5n47vvBb .cluster-label span p{background-color:transparent;}#mermaid-svg-CenZqxHc5n47vvBb .label text,#mermaid-svg-CenZqxHc5n47vvBb span{fill:#333;color:#333;}#mermaid-svg-CenZqxHc5n47vvBb .node rect,#mermaid-svg-CenZqxHc5n47vvBb .node circle,#mermaid-svg-CenZqxHc5n47vvBb .node ellipse,#mermaid-svg-CenZqxHc5n47vvBb .node polygon,#mermaid-svg-CenZqxHc5n47vvBb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CenZqxHc5n47vvBb .rough-node .label text,#mermaid-svg-CenZqxHc5n47vvBb .node .label text,#mermaid-svg-CenZqxHc5n47vvBb .image-shape .label,#mermaid-svg-CenZqxHc5n47vvBb .icon-shape .label{text-anchor:middle;}#mermaid-svg-CenZqxHc5n47vvBb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CenZqxHc5n47vvBb .rough-node .label,#mermaid-svg-CenZqxHc5n47vvBb .node .label,#mermaid-svg-CenZqxHc5n47vvBb .image-shape .label,#mermaid-svg-CenZqxHc5n47vvBb .icon-shape .label{text-align:center;}#mermaid-svg-CenZqxHc5n47vvBb .node.clickable{cursor:pointer;}#mermaid-svg-CenZqxHc5n47vvBb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CenZqxHc5n47vvBb .arrowheadPath{fill:#333333;}#mermaid-svg-CenZqxHc5n47vvBb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CenZqxHc5n47vvBb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CenZqxHc5n47vvBb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CenZqxHc5n47vvBb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CenZqxHc5n47vvBb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CenZqxHc5n47vvBb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CenZqxHc5n47vvBb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CenZqxHc5n47vvBb .cluster text{fill:#333;}#mermaid-svg-CenZqxHc5n47vvBb .cluster span{color:#333;}#mermaid-svg-CenZqxHc5n47vvBb 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-CenZqxHc5n47vvBb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CenZqxHc5n47vvBb rect.text{fill:none;stroke-width:0;}#mermaid-svg-CenZqxHc5n47vvBb .icon-shape,#mermaid-svg-CenZqxHc5n47vvBb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CenZqxHc5n47vvBb .icon-shape p,#mermaid-svg-CenZqxHc5n47vvBb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CenZqxHc5n47vvBb .icon-shape .label rect,#mermaid-svg-CenZqxHc5n47vvBb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CenZqxHc5n47vvBb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CenZqxHc5n47vvBb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CenZqxHc5n47vvBb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 中介者拓扑
步骤1
事件A完成
步骤2
事件B完成
步骤3
初始事件
事件中介者

编排步骤
事件处理器A
事件处理器B
事件处理器C

特点:有一个"指挥官"决定事件处理的顺序。类似流程引擎------事件到达后,由中介者编排处理步骤。

适用场景:业务流程有明确的步骤顺序,如订单处理(校验→扣库存→支付→发货)。

3.2 代理者拓扑(Broker Topology)

#mermaid-svg-HBdIEfMzQjYjSlpx{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-HBdIEfMzQjYjSlpx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HBdIEfMzQjYjSlpx .error-icon{fill:#552222;}#mermaid-svg-HBdIEfMzQjYjSlpx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HBdIEfMzQjYjSlpx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HBdIEfMzQjYjSlpx .marker.cross{stroke:#333333;}#mermaid-svg-HBdIEfMzQjYjSlpx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HBdIEfMzQjYjSlpx p{margin:0;}#mermaid-svg-HBdIEfMzQjYjSlpx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster-label text{fill:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster-label span{color:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster-label span p{background-color:transparent;}#mermaid-svg-HBdIEfMzQjYjSlpx .label text,#mermaid-svg-HBdIEfMzQjYjSlpx span{fill:#333;color:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .node rect,#mermaid-svg-HBdIEfMzQjYjSlpx .node circle,#mermaid-svg-HBdIEfMzQjYjSlpx .node ellipse,#mermaid-svg-HBdIEfMzQjYjSlpx .node polygon,#mermaid-svg-HBdIEfMzQjYjSlpx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HBdIEfMzQjYjSlpx .rough-node .label text,#mermaid-svg-HBdIEfMzQjYjSlpx .node .label text,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape .label,#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape .label{text-anchor:middle;}#mermaid-svg-HBdIEfMzQjYjSlpx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HBdIEfMzQjYjSlpx .rough-node .label,#mermaid-svg-HBdIEfMzQjYjSlpx .node .label,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape .label,#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape .label{text-align:center;}#mermaid-svg-HBdIEfMzQjYjSlpx .node.clickable{cursor:pointer;}#mermaid-svg-HBdIEfMzQjYjSlpx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HBdIEfMzQjYjSlpx .arrowheadPath{fill:#333333;}#mermaid-svg-HBdIEfMzQjYjSlpx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HBdIEfMzQjYjSlpx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HBdIEfMzQjYjSlpx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HBdIEfMzQjYjSlpx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HBdIEfMzQjYjSlpx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HBdIEfMzQjYjSlpx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster text{fill:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster span{color:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx 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-HBdIEfMzQjYjSlpx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx rect.text{fill:none;stroke-width:0;}#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape p,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape .label rect,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HBdIEfMzQjYjSlpx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HBdIEfMzQjYjSlpx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HBdIEfMzQjYjSlpx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 代理者拓扑
产生事件B
产生事件C
事件A
处理器1
处理器2
处理器3

特点:没有中心编排者,每个处理器消费事件后产生新事件,下一个处理器自动接力。像"接力赛"------每一棒跑完把接力棒传下去。

适用场景:处理链路比较固定、步骤间有自然的数据流转关系。

3.3 广播拓扑(Broadcast Topology)

#mermaid-svg-AqDYtNo2DK1Brlyn{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-AqDYtNo2DK1Brlyn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AqDYtNo2DK1Brlyn .error-icon{fill:#552222;}#mermaid-svg-AqDYtNo2DK1Brlyn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AqDYtNo2DK1Brlyn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AqDYtNo2DK1Brlyn .marker.cross{stroke:#333333;}#mermaid-svg-AqDYtNo2DK1Brlyn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AqDYtNo2DK1Brlyn p{margin:0;}#mermaid-svg-AqDYtNo2DK1Brlyn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster-label text{fill:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster-label span{color:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster-label span p{background-color:transparent;}#mermaid-svg-AqDYtNo2DK1Brlyn .label text,#mermaid-svg-AqDYtNo2DK1Brlyn span{fill:#333;color:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .node rect,#mermaid-svg-AqDYtNo2DK1Brlyn .node circle,#mermaid-svg-AqDYtNo2DK1Brlyn .node ellipse,#mermaid-svg-AqDYtNo2DK1Brlyn .node polygon,#mermaid-svg-AqDYtNo2DK1Brlyn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AqDYtNo2DK1Brlyn .rough-node .label text,#mermaid-svg-AqDYtNo2DK1Brlyn .node .label text,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape .label,#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape .label{text-anchor:middle;}#mermaid-svg-AqDYtNo2DK1Brlyn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AqDYtNo2DK1Brlyn .rough-node .label,#mermaid-svg-AqDYtNo2DK1Brlyn .node .label,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape .label,#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape .label{text-align:center;}#mermaid-svg-AqDYtNo2DK1Brlyn .node.clickable{cursor:pointer;}#mermaid-svg-AqDYtNo2DK1Brlyn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AqDYtNo2DK1Brlyn .arrowheadPath{fill:#333333;}#mermaid-svg-AqDYtNo2DK1Brlyn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AqDYtNo2DK1Brlyn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AqDYtNo2DK1Brlyn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AqDYtNo2DK1Brlyn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AqDYtNo2DK1Brlyn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AqDYtNo2DK1Brlyn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster text{fill:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster span{color:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn 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-AqDYtNo2DK1Brlyn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn rect.text{fill:none;stroke-width:0;}#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape p,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape .label rect,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AqDYtNo2DK1Brlyn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AqDYtNo2DK1Brlyn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AqDYtNo2DK1Brlyn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 广播拓扑
事件源
事件总线
处理器1

发通知
处理器2

更新缓存
处理器3

写日志
处理器4

推数据仓库

特点:一个事件同时触发多个独立的处理器,彼此之间无依赖关系。

适用场景:一个业务事件需要多个系统各自独立响应。比如"订单已支付"需要通知、日志、缓存、数仓各自处理。

3.4 中介者-代理者混合拓扑

实际系统大多是混合拓扑------关键流程用中介者编排,非关键旁路用广播分发。
#mermaid-svg-wZDvXTmrOXJg1Axk{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-wZDvXTmrOXJg1Axk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wZDvXTmrOXJg1Axk .error-icon{fill:#552222;}#mermaid-svg-wZDvXTmrOXJg1Axk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wZDvXTmrOXJg1Axk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wZDvXTmrOXJg1Axk .marker.cross{stroke:#333333;}#mermaid-svg-wZDvXTmrOXJg1Axk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wZDvXTmrOXJg1Axk p{margin:0;}#mermaid-svg-wZDvXTmrOXJg1Axk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster-label text{fill:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster-label span{color:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster-label span p{background-color:transparent;}#mermaid-svg-wZDvXTmrOXJg1Axk .label text,#mermaid-svg-wZDvXTmrOXJg1Axk span{fill:#333;color:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .node rect,#mermaid-svg-wZDvXTmrOXJg1Axk .node circle,#mermaid-svg-wZDvXTmrOXJg1Axk .node ellipse,#mermaid-svg-wZDvXTmrOXJg1Axk .node polygon,#mermaid-svg-wZDvXTmrOXJg1Axk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wZDvXTmrOXJg1Axk .rough-node .label text,#mermaid-svg-wZDvXTmrOXJg1Axk .node .label text,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape .label,#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape .label{text-anchor:middle;}#mermaid-svg-wZDvXTmrOXJg1Axk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wZDvXTmrOXJg1Axk .rough-node .label,#mermaid-svg-wZDvXTmrOXJg1Axk .node .label,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape .label,#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape .label{text-align:center;}#mermaid-svg-wZDvXTmrOXJg1Axk .node.clickable{cursor:pointer;}#mermaid-svg-wZDvXTmrOXJg1Axk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wZDvXTmrOXJg1Axk .arrowheadPath{fill:#333333;}#mermaid-svg-wZDvXTmrOXJg1Axk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wZDvXTmrOXJg1Axk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wZDvXTmrOXJg1Axk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wZDvXTmrOXJg1Axk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wZDvXTmrOXJg1Axk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wZDvXTmrOXJg1Axk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster text{fill:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster span{color:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk 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-wZDvXTmrOXJg1Axk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk rect.text{fill:none;stroke-width:0;}#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape p,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape .label rect,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wZDvXTmrOXJg1Axk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wZDvXTmrOXJg1Axk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wZDvXTmrOXJg1Axk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 步骤1
库存已扣
步骤2
扣款成功
订单已完成
订单创建事件
订单中介者
扣库存
扣款
订单完成事件
广播总线
通知服务
日志服务
积分服务
数据仓库


四、iPaaS 事件中心实战

理论讲够了,来看我们 iPaaS 平台的真实事件驱动架构。

4.1 事件中心在 iPaaS 中的定位

iPaaS 是一个集成平台,核心使命是"让不同的系统通过事件联动起来"。事件中心是整个平台的"神经中枢":
#mermaid-svg-cM5E1rWmm4sVsVZq{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-cM5E1rWmm4sVsVZq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cM5E1rWmm4sVsVZq .error-icon{fill:#552222;}#mermaid-svg-cM5E1rWmm4sVsVZq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cM5E1rWmm4sVsVZq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cM5E1rWmm4sVsVZq .marker.cross{stroke:#333333;}#mermaid-svg-cM5E1rWmm4sVsVZq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cM5E1rWmm4sVsVZq p{margin:0;}#mermaid-svg-cM5E1rWmm4sVsVZq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster-label text{fill:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster-label span{color:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster-label span p{background-color:transparent;}#mermaid-svg-cM5E1rWmm4sVsVZq .label text,#mermaid-svg-cM5E1rWmm4sVsVZq span{fill:#333;color:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .node rect,#mermaid-svg-cM5E1rWmm4sVsVZq .node circle,#mermaid-svg-cM5E1rWmm4sVsVZq .node ellipse,#mermaid-svg-cM5E1rWmm4sVsVZq .node polygon,#mermaid-svg-cM5E1rWmm4sVsVZq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cM5E1rWmm4sVsVZq .rough-node .label text,#mermaid-svg-cM5E1rWmm4sVsVZq .node .label text,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape .label,#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape .label{text-anchor:middle;}#mermaid-svg-cM5E1rWmm4sVsVZq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cM5E1rWmm4sVsVZq .rough-node .label,#mermaid-svg-cM5E1rWmm4sVsVZq .node .label,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape .label,#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape .label{text-align:center;}#mermaid-svg-cM5E1rWmm4sVsVZq .node.clickable{cursor:pointer;}#mermaid-svg-cM5E1rWmm4sVsVZq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cM5E1rWmm4sVsVZq .arrowheadPath{fill:#333333;}#mermaid-svg-cM5E1rWmm4sVsVZq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cM5E1rWmm4sVsVZq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cM5E1rWmm4sVsVZq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cM5E1rWmm4sVsVZq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cM5E1rWmm4sVsVZq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cM5E1rWmm4sVsVZq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster text{fill:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster span{color:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq 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-cM5E1rWmm4sVsVZq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq rect.text{fill:none;stroke-width:0;}#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape p,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape .label rect,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cM5E1rWmm4sVsVZq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cM5E1rWmm4sVsVZq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cM5E1rWmm4sVsVZq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 基础设施
流程引擎
事件中心
事件来源(600+ 第三方应用)
Webhook 推送

钉钉/企微/飞书/...
定时轮询

HTTP/数据库/API
数据变更捕获

MySQL/PG/MongoDB
定时触发

Cron 表达式
事件接收层

BaseEventController
消息生产者

PushEventProducer
消息消费者

PushEventListener
触发器系统

Push/Polling/CDC
流程编排执行
连接器调用
RocketMQ

push_event Topic
Redis

去重/限流

4.2 事件中心的架构设计

事件中心的核心职责:接收第三方系统推送的事件 → 标准化封装 → 异步投递到 RocketMQ → 引擎消费并触发流程。

4.2.1 事件接收层

每个第三方应用有独立的 EventController,继承 BaseEventController,负责接收 Webhook 回调:

java 复制代码
// 事件接收基类------统一的事件入口
@Component
public class BaseEventController {
    @Resource
    PushEventProducer pushEventProducer;

    /**
     * 构建标准化事件消息体
     */
    protected PushEventMessage buildBody(
            String processor,
            ConnectorKey connectorKey,
            AssetKey assetKey,
            @Nullable Object event,
            String body) {
        PushEventMessage msg = new PushEventMessage();
        msg.setProcessor(processor);
        msg.setConnectorKey(connectorKey);
        msg.setAssetKey(assetKey);
        msg.setEvent(event);
        msg.setMessage(body);
        return msg;
    }

    /**
     * 异步投递事件到 MQ
     */
    protected void sendMessage(PushEventMessage pushEventMessage) {
        String message = JSON.toJSONString(pushEventMessage);
        pushEventProducer.sendMessage(message);
    }

    /**
     * 快速响应第三方------不等流程执行完
     */
    protected Object buildResponse() {
        Map<String, Object> response = new HashMap<>();
        response.put("code", 0);
        response.put("msg", "success");
        return response;
    }
}

以小红书事件控制器为例:

java 复制代码
@RestController
@RequestMapping("/event/xiaohongshu")
public class XiaohongshuEventController extends BaseEventController {

    @PostMapping("/pushEvent")
    public Object pushEvent(
            @RequestHeader("app-key") String appKey,
            @RequestHeader("sign") String sign,
            @RequestBody String body) {

        // 1. 解析第三方推送的事件数据
        List<XiaohongshuPushing> messages = JSON.parseArray(body, ...);

        // 2. 逐条封装为标准化事件消息
        messages.forEach(message -> {
            PushEventMessage msg = this.buildBody(
                PROCESS_NAME,
                new ConnectorKey(CONNECTOR_ID, connectorVersion),
                AssetKey.builder()
                    .type(AssetOwnerTypeEnum.ASSET_UK.getCode())
                    .assetId(message.getSellerId())
                    .build(),
                message.getMsgTag(),
                (String) message.getData()
            );
            // 3. 异步投递到 MQ------不等流程执行
            this.sendMessage(msg);
        });

        // 4. 立即响应第三方------毫秒级返回
        return buildResponse();
    }
}

关键设计决策

  1. 快速 ACK:收到 Webhook 后立即返回成功,不等流程执行完。如果同步执行流程,第三方超时会导致连接堆积
  2. 标准化封装 :600+ 个第三方应用的事件格式千差万别,但在事件中心统一封装成 PushEventMessage,下游引擎不需要关心来源差异
  3. 继承体系BaseEventController 封装公共逻辑,各应用的 Controller 只关注自身的事件解析
4.2.2 消息投递层
java 复制代码
@Component
public class PushEventProducer {
    @Resource
    private Producer producer;

    @Value("${rocketmq.push.event.topic:push_event}")
    private String topic;

    /**
     * 发送普通事件消息
     */
    public void sendMessage(String msgBody) {
        final Message message = provider.newMessageBuilder()
            .setTopic(topic)
            .setKeys(PUSH_EVENT_TAG)
            .setTag(PUSH_EVENT_TAG)
            .setBody(msgBody.getBytes(StandardCharsets.UTF_8))
            .build();
        producer.send(message);
    }

    /**
     * 发送顺序消息------保证同一事件源的顺序性
     */
    public void sendOrderlyMessage(String msgBody, String orderTag) {
        // 按 orderTag 分区,保证同一分区内消息有序
        ...
    }
}
4.2.3 消息消费层
java 复制代码
@Component
public class PushEventListener extends BaseConsumer implements MessageListener {
    @Override
    public ConsumeResult consume(MessageView messageView) {
        // 从 MQ 消费事件消息,触发流程引擎执行
        String body = ByteBufferUtils.byteBufferToString(messageView.getBody());
        return doConsume(body);
    }
}

4.3 引擎侧的触发器体系

事件中心投递消息后,引擎侧通过触发器体系响应事件:
#mermaid-svg-B3BdQ2PWBn9NuLc5{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-B3BdQ2PWBn9NuLc5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .error-icon{fill:#552222;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .marker.cross{stroke:#333333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 p{margin:0;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster-label text{fill:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster-label span{color:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster-label span p{background-color:transparent;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .label text,#mermaid-svg-B3BdQ2PWBn9NuLc5 span{fill:#333;color:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .node rect,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node circle,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node ellipse,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node polygon,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .rough-node .label text,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node .label text,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape .label,#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .rough-node .label,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node .label,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape .label,#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape .label{text-align:center;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .node.clickable{cursor:pointer;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .arrowheadPath{fill:#333333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B3BdQ2PWBn9NuLc5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster text{fill:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster span{color:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 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-B3BdQ2PWBn9NuLc5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape p,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape .label rect,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B3BdQ2PWBn9NuLc5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-B3BdQ2PWBn9NuLc5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 流程引擎执行
四种触发器类型
推送触发器 PushEventTrigger

接收 Webhook/MQ 事件
轮询触发器 HttpPollingTrigger

Cron 定时拉取
CDC触发器 DataBaseCdcTrigger

数据库变更捕获
定时触发器 TimerTrigger

Cron 定时执行
TriggerExchange

事件上下文
FlowInstance

流程实例
ExecuteMachine

执行机

四种触发模式对比

触发器 事件来源 实时性 适用场景
推送触发器 第三方 Webhook → MQ 实时 钉钉消息回调、电商订单推送
轮询触发器 Cron 定时拉取 API 准实时(秒级) 不支持 Webhook 的系统
CDC 触发器 数据库 Binlog 准实时(秒级) 数据库变更监听
定时触发器 Cron 定时执行 定时(分钟级) 定时报表、数据同步

4.4 一个事件的完整生命周期

以"钉钉机器人消息触发流程"为例:
SAP系统 流程引擎 RocketMQ 事件中心 钉钉服务器 SAP系统 流程引擎 RocketMQ 事件中心 钉钉服务器 #mermaid-svg-tN0G8UoFBJ8DNTOB{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-tN0G8UoFBJ8DNTOB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tN0G8UoFBJ8DNTOB .error-icon{fill:#552222;}#mermaid-svg-tN0G8UoFBJ8DNTOB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tN0G8UoFBJ8DNTOB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tN0G8UoFBJ8DNTOB .marker.cross{stroke:#333333;}#mermaid-svg-tN0G8UoFBJ8DNTOB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tN0G8UoFBJ8DNTOB p{margin:0;}#mermaid-svg-tN0G8UoFBJ8DNTOB .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tN0G8UoFBJ8DNTOB text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tN0G8UoFBJ8DNTOB .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB .sequenceNumber{fill:white;}#mermaid-svg-tN0G8UoFBJ8DNTOB #sequencenumber{fill:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB .messageText{fill:#333;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tN0G8UoFBJ8DNTOB .labelText,#mermaid-svg-tN0G8UoFBJ8DNTOB .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .loopText,#mermaid-svg-tN0G8UoFBJ8DNTOB .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .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-tN0G8UoFBJ8DNTOB .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-tN0G8UoFBJ8DNTOB .noteText,#mermaid-svg-tN0G8UoFBJ8DNTOB .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tN0G8UoFBJ8DNTOB .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tN0G8UoFBJ8DNTOB .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tN0G8UoFBJ8DNTOB .actorPopupMenu{position:absolute;}#mermaid-svg-tN0G8UoFBJ8DNTOB .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-tN0G8UoFBJ8DNTOB .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tN0G8UoFBJ8DNTOB .actor-man circle,#mermaid-svg-tN0G8UoFBJ8DNTOB line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-tN0G8UoFBJ8DNTOB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 事件在队列中等待消费 POST /event/dingding/webhook 验签 + 解析消息 sendMessage(PushEventMessage) 200 OK(< 50ms) consume(MessageView) 匹配触发器 → 创建流程实例 加载流程定义 → 逐节点执行 HTTP 调用 SAP 接口 返回业务数据 写入执行日志

关键设计要点

  1. 毫秒级响应第三方:事件中心只做"接收 → 封装 → 投递"三步,不做任何业务处理。钉钉服务器不会因为流程执行慢而超时
  2. 削峰填谷:高并发 Webhook 涌入时,事件堆积在 RocketMQ 中,引擎按自己的消费能力逐步处理
  3. 故障隔离:引擎挂了?事件在 MQ 中安全存储,恢复后继续消费,零数据丢失

五、事件驱动架构中最常踩的八个坑

5.1 坑一:事件丢失------消息发了但没人收到

症状:用户反馈"流程没有被触发",查日志发现事件确实发了,但消费者没收到。

为什么丢

  • 生产者发送失败但没重试
  • MQ 宕机时未持久化的消息丢失
  • 消费者处理失败但标记了消费成功

修复方案------三道防线
#mermaid-svg-3FRja9MXT2YFDFoQ{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-3FRja9MXT2YFDFoQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3FRja9MXT2YFDFoQ .error-icon{fill:#552222;}#mermaid-svg-3FRja9MXT2YFDFoQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3FRja9MXT2YFDFoQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3FRja9MXT2YFDFoQ .marker.cross{stroke:#333333;}#mermaid-svg-3FRja9MXT2YFDFoQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3FRja9MXT2YFDFoQ p{margin:0;}#mermaid-svg-3FRja9MXT2YFDFoQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster-label text{fill:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster-label span{color:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster-label span p{background-color:transparent;}#mermaid-svg-3FRja9MXT2YFDFoQ .label text,#mermaid-svg-3FRja9MXT2YFDFoQ span{fill:#333;color:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .node rect,#mermaid-svg-3FRja9MXT2YFDFoQ .node circle,#mermaid-svg-3FRja9MXT2YFDFoQ .node ellipse,#mermaid-svg-3FRja9MXT2YFDFoQ .node polygon,#mermaid-svg-3FRja9MXT2YFDFoQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3FRja9MXT2YFDFoQ .rough-node .label text,#mermaid-svg-3FRja9MXT2YFDFoQ .node .label text,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape .label,#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-3FRja9MXT2YFDFoQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3FRja9MXT2YFDFoQ .rough-node .label,#mermaid-svg-3FRja9MXT2YFDFoQ .node .label,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape .label,#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape .label{text-align:center;}#mermaid-svg-3FRja9MXT2YFDFoQ .node.clickable{cursor:pointer;}#mermaid-svg-3FRja9MXT2YFDFoQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3FRja9MXT2YFDFoQ .arrowheadPath{fill:#333333;}#mermaid-svg-3FRja9MXT2YFDFoQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3FRja9MXT2YFDFoQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3FRja9MXT2YFDFoQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3FRja9MXT2YFDFoQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3FRja9MXT2YFDFoQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3FRja9MXT2YFDFoQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster text{fill:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster span{color:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ 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-3FRja9MXT2YFDFoQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape p,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape .label rect,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3FRja9MXT2YFDFoQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3FRja9MXT2YFDFoQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3FRja9MXT2YFDFoQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 防线三:消费端确认
投递
处理成功
处理失败
MQ
消费者
ACK
NACK → 重试
防线二:Broker持久化
同步刷盘
主从复制
MQ
磁盘
从节点
防线一:生产端确认
发送
ACK
失败重试
生产者
MQ

5.2 坑二:重复消费------一个事件被处理了三次

症状:用户收到三条一样的通知短信,数据库里出现重复记录。

为什么重复

  • 消费者处理成功后 ACK 失败,MQ 重新投递
  • 生产者超时重试,同一条消息发了两次
  • 消费者重启,未 ACK 的消息被重新投递

修复方案------幂等性设计

java 复制代码
/**
 * 幂等消费:同一个事件只处理一次
 */
public ConsumeResult consume(MessageView messageView) {
    String eventId = extractEventId(messageView);
    
    // 1. 幂等检查:用 Redis 记录已处理的事件ID
    if (redisTemplate.opsForValue().setIfAbsent(
            "consumed:" + eventId, "1", 24, TimeUnit.HOURS)) {
        // 2. 第一次处理:执行业务逻辑
        return doProcess(messageView);
    } else {
        // 3. 重复消息:跳过
        log.warn("Duplicate event detected: {}", eventId);
        return ConsumeResult.SUCCESS;
    }
}

幂等设计的核心原则:消费者必须假设"同一条消息可能被投递多次",并确保多次处理的结果和一次处理完全一致。

5.3 坑三:事件顺序错乱------先消费了"取消"再消费"创建"

症状:订单先收到"取消"事件,再收到"创建"事件------状态机乱了。

为什么乱序

  • MQ 的多分区并行消费
  • 消费者多线程处理
  • 网络延迟导致后发的消息先到

修复方案

方案 说明 适用场景
分区有序 同一实体的事件发到同一分区 大部分场景(推荐)
版本号 事件带版本号,低版本丢弃 数据同步场景
时间戳排序 消费者按时间戳重排序 容忍一定延迟的批处理
java 复制代码
// 分区有序:同一 assetId 的事件进入同一分区
public void sendOrderlyMessage(String msgBody, String orderTag) {
    final Message message = provider.newMessageBuilder()
        .setTopic(orderTopic)
        .setKeys(orderTag)          // 用 orderTag 做分区键
        .setTag(PUSH_ORDER_EVENT_TAG)
        .setBody(msgBody.getBytes(StandardCharsets.UTF_8))
        .build();
    producer.send(message);          // 同一 orderTag 保证顺序
}

5.4 坑四:消费者雪崩------一个慢消费者拖垮所有人

症状:某个消费者的处理逻辑特别慢(比如调外部 API 超时),导致消息堆积,其他正常消费者也受影响。

修复方案

  1. 独立消费组:不同类型的消费者使用独立的消费组,互不影响
  2. 消费超时:设置单条消息的最大处理时间
  3. 死信队列:多次重试失败的消息进入死信队列,不阻塞正常消费

#mermaid-svg-dDCSwpClY5jViGXV{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-dDCSwpClY5jViGXV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dDCSwpClY5jViGXV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dDCSwpClY5jViGXV .error-icon{fill:#552222;}#mermaid-svg-dDCSwpClY5jViGXV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dDCSwpClY5jViGXV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dDCSwpClY5jViGXV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dDCSwpClY5jViGXV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dDCSwpClY5jViGXV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dDCSwpClY5jViGXV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dDCSwpClY5jViGXV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dDCSwpClY5jViGXV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dDCSwpClY5jViGXV .marker.cross{stroke:#333333;}#mermaid-svg-dDCSwpClY5jViGXV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dDCSwpClY5jViGXV p{margin:0;}#mermaid-svg-dDCSwpClY5jViGXV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dDCSwpClY5jViGXV .cluster-label text{fill:#333;}#mermaid-svg-dDCSwpClY5jViGXV .cluster-label span{color:#333;}#mermaid-svg-dDCSwpClY5jViGXV .cluster-label span p{background-color:transparent;}#mermaid-svg-dDCSwpClY5jViGXV .label text,#mermaid-svg-dDCSwpClY5jViGXV span{fill:#333;color:#333;}#mermaid-svg-dDCSwpClY5jViGXV .node rect,#mermaid-svg-dDCSwpClY5jViGXV .node circle,#mermaid-svg-dDCSwpClY5jViGXV .node ellipse,#mermaid-svg-dDCSwpClY5jViGXV .node polygon,#mermaid-svg-dDCSwpClY5jViGXV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dDCSwpClY5jViGXV .rough-node .label text,#mermaid-svg-dDCSwpClY5jViGXV .node .label text,#mermaid-svg-dDCSwpClY5jViGXV .image-shape .label,#mermaid-svg-dDCSwpClY5jViGXV .icon-shape .label{text-anchor:middle;}#mermaid-svg-dDCSwpClY5jViGXV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dDCSwpClY5jViGXV .rough-node .label,#mermaid-svg-dDCSwpClY5jViGXV .node .label,#mermaid-svg-dDCSwpClY5jViGXV .image-shape .label,#mermaid-svg-dDCSwpClY5jViGXV .icon-shape .label{text-align:center;}#mermaid-svg-dDCSwpClY5jViGXV .node.clickable{cursor:pointer;}#mermaid-svg-dDCSwpClY5jViGXV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dDCSwpClY5jViGXV .arrowheadPath{fill:#333333;}#mermaid-svg-dDCSwpClY5jViGXV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dDCSwpClY5jViGXV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dDCSwpClY5jViGXV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dDCSwpClY5jViGXV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dDCSwpClY5jViGXV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dDCSwpClY5jViGXV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dDCSwpClY5jViGXV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dDCSwpClY5jViGXV .cluster text{fill:#333;}#mermaid-svg-dDCSwpClY5jViGXV .cluster span{color:#333;}#mermaid-svg-dDCSwpClY5jViGXV 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-dDCSwpClY5jViGXV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dDCSwpClY5jViGXV rect.text{fill:none;stroke-width:0;}#mermaid-svg-dDCSwpClY5jViGXV .icon-shape,#mermaid-svg-dDCSwpClY5jViGXV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dDCSwpClY5jViGXV .icon-shape p,#mermaid-svg-dDCSwpClY5jViGXV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dDCSwpClY5jViGXV .icon-shape .label rect,#mermaid-svg-dDCSwpClY5jViGXV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dDCSwpClY5jViGXV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dDCSwpClY5jViGXV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dDCSwpClY5jViGXV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 死信处理
正常消费
重试3次失败
Topic
消费组A

通知服务
消费组B

日志服务
消费组C

积分服务
死信队列

DLQ
告警通知
人工重试

5.5 坑五:事件风暴------一个操作触发一百个事件

症状:用户点了一个按钮,系统产生了几百个事件,MQ 被塞满,消费延迟飙升。

为什么 :批量操作没有合并事件。比如"批量导入 1000 条客户",如果每条都发一个 CustomerCreated 事件,就是 1000 个事件。

修复方案

方案 说明
事件合并 批量操作发一个 CustomerBatchImported 事件,包含 ID 列表
事件节流 限制同一源头在单位时间内的最大事件数
批量消费 消费者一次拉取多条消息批量处理

5.6 坑六:同步伪装成异步------发事件后还在等结果

症状 :Controller 发了事件后,还轮询数据库等结果,或者用 Thread.sleep 等待。

为什么是坑:本质上还是同步思维,白白浪费了异步的优势,还多了一层 MQ 的开销。

修复方案:真正的异步是"发了就走"。前端需要结果?用 WebSocket 推送,或者轮询状态接口。

5.7 坑七:事件模式未定义------生产者和消费者各说各话

症状:生产者改了事件字段名,消费者还在用旧字段名读取------线上静默失败,数据全是 null。

修复方案

  1. 事件模式注册:每个事件有明确的 Schema(JSON Schema / Protobuf)
  2. 版本管理:事件结构变更时递增版本号,消费者兼容多版本
  3. 契约测试:生产者和消费者共享 Schema,CI 中校验兼容性

5.8 坑八:忽视可观测性------出了问题完全无法排查

症状:用户说"我的流程没触发",你查了一圈不知道事件到哪儿了。

修复方案------事件追踪三件套

维度 做法 价值
Trace ID 事件携带全局唯一的追踪 ID 串联从生产到消费的全链路
事件日志 记录每个事件的投递/消费/ACK 状态 出问题时有据可查
消费监控 监控消费延迟/堆积量/失败率 问题发生前就能预警

六、事件溯源(Event Sourcing):把事件当数据库

6.1 什么是事件溯源?

传统做法:数据库里存"当前状态"------一条订单记录存了 status = 'COMPLETED',你不知道它经历了什么。

事件溯源:不存状态,只存事件。当前状态 = 所有事件的叠加。
#mermaid-svg-oFu7iVwhhv2fofxT{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-oFu7iVwhhv2fofxT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oFu7iVwhhv2fofxT .error-icon{fill:#552222;}#mermaid-svg-oFu7iVwhhv2fofxT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oFu7iVwhhv2fofxT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oFu7iVwhhv2fofxT .marker.cross{stroke:#333333;}#mermaid-svg-oFu7iVwhhv2fofxT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oFu7iVwhhv2fofxT p{margin:0;}#mermaid-svg-oFu7iVwhhv2fofxT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster-label text{fill:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster-label span{color:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster-label span p{background-color:transparent;}#mermaid-svg-oFu7iVwhhv2fofxT .label text,#mermaid-svg-oFu7iVwhhv2fofxT span{fill:#333;color:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .node rect,#mermaid-svg-oFu7iVwhhv2fofxT .node circle,#mermaid-svg-oFu7iVwhhv2fofxT .node ellipse,#mermaid-svg-oFu7iVwhhv2fofxT .node polygon,#mermaid-svg-oFu7iVwhhv2fofxT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oFu7iVwhhv2fofxT .rough-node .label text,#mermaid-svg-oFu7iVwhhv2fofxT .node .label text,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape .label,#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape .label{text-anchor:middle;}#mermaid-svg-oFu7iVwhhv2fofxT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oFu7iVwhhv2fofxT .rough-node .label,#mermaid-svg-oFu7iVwhhv2fofxT .node .label,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape .label,#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape .label{text-align:center;}#mermaid-svg-oFu7iVwhhv2fofxT .node.clickable{cursor:pointer;}#mermaid-svg-oFu7iVwhhv2fofxT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oFu7iVwhhv2fofxT .arrowheadPath{fill:#333333;}#mermaid-svg-oFu7iVwhhv2fofxT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oFu7iVwhhv2fofxT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oFu7iVwhhv2fofxT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oFu7iVwhhv2fofxT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oFu7iVwhhv2fofxT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oFu7iVwhhv2fofxT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oFu7iVwhhv2fofxT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster text{fill:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster span{color:#333;}#mermaid-svg-oFu7iVwhhv2fofxT 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-oFu7iVwhhv2fofxT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oFu7iVwhhv2fofxT rect.text{fill:none;stroke-width:0;}#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape p,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape .label rect,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oFu7iVwhhv2fofxT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oFu7iVwhhv2fofxT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oFu7iVwhhv2fofxT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 事件溯源:事件存储
重放
Event Store
OrderCreated
PaymentReceived
OrderShipped
OrderDelivered
当前状态

= COMPLETED
传统:状态存储
MySQL

status = COMPLETED

6.2 事件溯源的收益

收益 说明
完整审计 每一步变更都有记录,满足金融/医疗合规要求
时间旅行 可以回放任意时间点的事件,重建当时的状态
投影灵活 同一组事件可以投影成不同的读模型(订单视图/统计视图/风控视图)
天然解耦 写操作只追加事件,读操作从投影读取

6.3 事件溯源的代价

代价 说明
查询复杂 不能直接查"所有已完成的订单"------需要投影
性能开销 重放百万条事件来重建状态,需要快照机制
学习曲线 团队要从"更新状态"的思维转为"追加事件"的思维
模式演进 事件结构变更后,旧事件和新事件的兼容处理

适用场景:金融交易审计、保险保单全生命周期、医疗病历------任何"需要知道发生了什么而不只是现在是什么"的场景。


七、事件驱动 + CQRS:读写分离的终极形态

7.1 架构全景

#mermaid-svg-f5mx92VaRWH5T4wc{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-f5mx92VaRWH5T4wc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f5mx92VaRWH5T4wc .error-icon{fill:#552222;}#mermaid-svg-f5mx92VaRWH5T4wc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f5mx92VaRWH5T4wc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f5mx92VaRWH5T4wc .marker.cross{stroke:#333333;}#mermaid-svg-f5mx92VaRWH5T4wc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f5mx92VaRWH5T4wc p{margin:0;}#mermaid-svg-f5mx92VaRWH5T4wc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster-label text{fill:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster-label span{color:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster-label span p{background-color:transparent;}#mermaid-svg-f5mx92VaRWH5T4wc .label text,#mermaid-svg-f5mx92VaRWH5T4wc span{fill:#333;color:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .node rect,#mermaid-svg-f5mx92VaRWH5T4wc .node circle,#mermaid-svg-f5mx92VaRWH5T4wc .node ellipse,#mermaid-svg-f5mx92VaRWH5T4wc .node polygon,#mermaid-svg-f5mx92VaRWH5T4wc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f5mx92VaRWH5T4wc .rough-node .label text,#mermaid-svg-f5mx92VaRWH5T4wc .node .label text,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape .label,#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape .label{text-anchor:middle;}#mermaid-svg-f5mx92VaRWH5T4wc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-f5mx92VaRWH5T4wc .rough-node .label,#mermaid-svg-f5mx92VaRWH5T4wc .node .label,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape .label,#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape .label{text-align:center;}#mermaid-svg-f5mx92VaRWH5T4wc .node.clickable{cursor:pointer;}#mermaid-svg-f5mx92VaRWH5T4wc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-f5mx92VaRWH5T4wc .arrowheadPath{fill:#333333;}#mermaid-svg-f5mx92VaRWH5T4wc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f5mx92VaRWH5T4wc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f5mx92VaRWH5T4wc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f5mx92VaRWH5T4wc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-f5mx92VaRWH5T4wc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f5mx92VaRWH5T4wc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-f5mx92VaRWH5T4wc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster text{fill:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster span{color:#333;}#mermaid-svg-f5mx92VaRWH5T4wc 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-f5mx92VaRWH5T4wc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-f5mx92VaRWH5T4wc rect.text{fill:none;stroke-width:0;}#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape p,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape .label rect,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f5mx92VaRWH5T4wc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-f5mx92VaRWH5T4wc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-f5mx92VaRWH5T4wc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询
读端:Query
写端:Command
事件流
写入 API
命令处理器
领域模型

业务规则校验
事件存储

Event Store
事件投影器
订单视图

MySQL
统计视图

Doris
搜索视图

ES
查询 API

写端 只负责接收命令 → 校验业务规则 → 追加事件。

读端 消费事件 → 更新各自的读模型(可以是不同的数据库)。

查询端直连读模型,毫秒级返回。

7.2 iPaaS 的实践:执行日志的 CQRS

我们在 iPaaS 的执行日志场景就使用了类似 CQRS 的思路:
#mermaid-svg-KXOAOKNwTAQb8rrR{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-KXOAOKNwTAQb8rrR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KXOAOKNwTAQb8rrR .error-icon{fill:#552222;}#mermaid-svg-KXOAOKNwTAQb8rrR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KXOAOKNwTAQb8rrR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KXOAOKNwTAQb8rrR .marker.cross{stroke:#333333;}#mermaid-svg-KXOAOKNwTAQb8rrR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KXOAOKNwTAQb8rrR p{margin:0;}#mermaid-svg-KXOAOKNwTAQb8rrR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster-label text{fill:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster-label span{color:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster-label span p{background-color:transparent;}#mermaid-svg-KXOAOKNwTAQb8rrR .label text,#mermaid-svg-KXOAOKNwTAQb8rrR span{fill:#333;color:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .node rect,#mermaid-svg-KXOAOKNwTAQb8rrR .node circle,#mermaid-svg-KXOAOKNwTAQb8rrR .node ellipse,#mermaid-svg-KXOAOKNwTAQb8rrR .node polygon,#mermaid-svg-KXOAOKNwTAQb8rrR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KXOAOKNwTAQb8rrR .rough-node .label text,#mermaid-svg-KXOAOKNwTAQb8rrR .node .label text,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape .label,#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape .label{text-anchor:middle;}#mermaid-svg-KXOAOKNwTAQb8rrR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KXOAOKNwTAQb8rrR .rough-node .label,#mermaid-svg-KXOAOKNwTAQb8rrR .node .label,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape .label,#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape .label{text-align:center;}#mermaid-svg-KXOAOKNwTAQb8rrR .node.clickable{cursor:pointer;}#mermaid-svg-KXOAOKNwTAQb8rrR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KXOAOKNwTAQb8rrR .arrowheadPath{fill:#333333;}#mermaid-svg-KXOAOKNwTAQb8rrR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KXOAOKNwTAQb8rrR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KXOAOKNwTAQb8rrR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KXOAOKNwTAQb8rrR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KXOAOKNwTAQb8rrR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KXOAOKNwTAQb8rrR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster text{fill:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster span{color:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR 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-KXOAOKNwTAQb8rrR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR rect.text{fill:none;stroke-width:0;}#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape p,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape .label rect,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KXOAOKNwTAQb8rrR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KXOAOKNwTAQb8rrR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KXOAOKNwTAQb8rrR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 读端
数据同步
写端
Flink CDC
流程引擎
MySQL

权威数据源
Doris

列存引擎
查询 API

  • 写链路:流程执行日志写入 MySQL,经过业务规则校验
  • 同步层:Flink CDC 异步将数据同步到 Doris
  • 读链路:查询直接走 Doris 列存引擎,亿级日志毫秒级返回

八、事件驱动架构落地 Checklist

事件设计

  • 事件名使用过去式(OrderCreated 而非 CreateOrder
  • 事件载荷包含足够下游处理的信息,不过多不过少
  • 事件携带元数据(时间戳、来源、Trace ID)
  • 事件 Schema 有版本管理,变更向后兼容

消息可靠性

  • 生产端有发送确认 + 失败重试
  • MQ 配置持久化 + 主从复制
  • 消费端实现幂等处理
  • 死信队列配置 + 告警

架构设计

  • 生产者和消费者通过 MQ 解耦,不直接依赖
  • 关键流程使用顺序消息保证有序性
  • 消费者有独立消费组,互不影响
  • 事件中心有削峰能力(MQ 缓冲)

可观测性

  • 事件全链路有 Trace ID 串联
  • 消费延迟/堆积量有监控告警
  • 事件日志可查看投递和消费状态
  • 有死信队列的巡检和重试机制

九、事件驱动架构的七个反模式

反模式 1:同步伪装异步

Controller 发了事件后轮询数据库等结果,本质上还是同步。

修复:发了就走,前端用 WebSocket 或轮询状态接口获取结果。

反模式 2:事件大杂烩

所有业务变更都发一个 DataChanged 事件,消费者要自己判断到底是什么变了。

修复 :事件类型要细粒度------OrderCreatedOrderPaidOrderShipped,消费者只订阅自己关心的事件。

反模式 3:消费者耦合

消费者在处理事件时直接调用另一个消费者的 API------变成了隐式的同步调用链。

修复:消费者只处理事件、产生新事件,不直接调用其他消费者。

反模式 4:忽视幂等

消费者假设"每条消息只会被处理一次"------重试时产生重复数据。

修复:每个消费者必须实现幂等处理。用 Redis 去重、数据库唯一约束、或状态机校验。

反模式 5:上帝事件

一个事件包含了系统中所有字段(几百个字段),每个消费者只用其中几个。

修复:事件只包含必要信息。消费者需要更多数据?通过事件中的 ID 去查询。

反模式 6:事件黑洞

事件被消费后没有任何可观测的输出------不知道处理成功了还是失败了。

修复:每个消费者的处理结果(成功/失败/跳过)都要有日志和指标。关键事件处理完可以发一个"结果事件"。

反模式 7:过度事件化

一个三层的 CRUD 后台,连用户改个密码都发事件。简单的事情用方法调用就够了。

修复:只在以下场景使用事件------跨服务通信、一个操作多个下游、需要异步处理、需要审计追溯。其他场景用同步调用。


十、事件驱动 vs 其他架构模式

维度 同步调用 事件驱动 请求-响应 + 事件混合
耦合度 高(硬编码调用) 低(通过 MQ 解耦) 中等
延迟 高(串行累加) 低(并行处理) 视场景而定
吞吐量 受限于最慢环节 高(MQ 削峰缓冲)
一致性 强一致(分布式事务) 最终一致 混合
可观测性 调用链清晰 需要额外追踪 较复杂
调试难度 低(单步调试) 高(异步链路) 中等
适用场景 强一致/简单流程 高吞吐/多下游/异步 大多数企业系统

没有银弹:大部分企业系统最终会走向"混合架构"------关键路径用同步调用保证强一致,旁路操作用事件驱动解耦。


十一、常见问题(FAQ)

Q:事件驱动和消息队列是一回事吗?

A:不是。消息队列是事件驱动架构的基础设施之一,但事件驱动是一套架构思想------包括事件设计、拓扑选择、可靠性保证、幂等处理等一系列设计决策。用了 MQ 不代表做了事件驱动,就像用了 Spring 不代表做了微服务。

Q:事件驱动能保证数据一致性吗?

A:能保证,但保证的是"最终一致性"而非"强一致性"。如果业务必须强一致(如银行转账),要么用分布式事务(Saga/TCC),要么这部分用同步调用。事件驱动和强一致性不是对立的------RocketMQ 的事务消息可以在一定程度上兼顾两者。

Q:消费者处理失败了怎么办?

A:三层兜底:① MQ 自动重试(指数退避);② 死信队列 + 告警;③ 人工介入 + 手动重试。关键是消费者要幂等------重试不能产生副作用。

Q:事件太多会不会把 MQ 撑爆?

A:会的。需要做好容量规划:① 合理设置 Topic 分区数;② 事件有过期时间(TTL);③ 监控消费延迟和堆积量;④ 批量事件做合并。

Q:什么时候该用顺序消息?

A:当同一实体的事件有严格的时序要求时。比如"订单创建"必须在"订单支付"之前被消费。通过分区键(如 orderId)保证同一实体的事件进入同一分区,分区内有序。

Q:事件驱动和微服务是什么关系?

A:事件驱动是微服务之间通信的最佳实践之一。微服务解决"服务怎么拆",事件驱动解决"服务怎么连"。同步 RPC 适合强依赖,事件驱动适合松耦合。两者结合使用。


十二、小结

事件驱动架构是分布式系统中最强大、也最容易被误用的架构模式之一。三句话总结:

  1. 事件驱动的本质是"解耦+异步"------生产者不管谁在消费,消费者不管事件从哪来,中间通过 MQ 做缓冲和路由
  2. 事件驱动不是银弹------它用"最终一致性"和"架构复杂度"换取"高弹性"和"高扩展性"------适合多下游、高吞吐、异步处理的场景,不适合强一致、简单查询的场景
  3. 落地事件驱动的三大纪律是"可靠投递+幂等消费+全链路追踪"------做不到这三点,事件驱动就是一个"出了问题查不到"的黑盒

从明天开始,审视你手头的项目:哪些同步调用可以改成事件驱动?哪些事件的可靠性没有保证?消费者的幂等做了吗?

想清楚这些,你的分布式系统就稳了一半。


附录:本文涉及的架构图全部使用 Mermaid 绘制,源码可直接复制到任何支持 Mermaid 的 Markdown 编辑器(Typora / VSCode / GitHub / GitLab / Notion)查看。


关于数环通

数环通 iPaaS 是一款面向企业的低代码集成平台,提供 600+ 应用连接器、可视化流程编排、全链路执行监控等核心能力,帮助企业快速打通系统孤岛,实现业务流程自动化。


标签:#事件驱动架构 #EDA #消息队列 #RocketMQ #Kafka #异步架构 #事件溯源 #CQRS #分布式系统 #微服务 #Webhook #CDC #架构设计 #系统设计 #技术分享

事件驱动架构从概念到落地------让系统像神经反射一样响应变化

🔔 本文 8000+ 字深度原创,含 10+ 张架构图和 iPaaS 事件中心、IoT、电商三大实战案例拆解。创作不易,如果对你有帮助,请点赞 👍 收藏 ⭐ 关注 🔥 三连支持,你的认可是我持续输出的最大动力!


写在前面

做了几年分布式系统之后,我发现一个很有意思的现象:

大部分系统的"慢"和"脆",不是因为单个服务写得差,而是因为服务之间"连"得太紧。

你肯定见过这些场景:

  • 用户下了一个订单,系统同步调了库存服务、支付服务、通知服务、积分服务------一个超時全链路挂掉
  • 老板说"加一个新的下游系统",你打开代码发现要改上游三四个服务的代码
  • 系统吞吐量上不去,因为所有操作都串行执行,一个慢查询拖垮全链路
  • Webhook 回调超时了,但业务逻辑还在同步执行,连接池被撑爆

这些问题的根源都是同一个------同步调用的紧耦合架构

事件驱动架构(Event-Driven Architecture,EDA)就是为了解决这类问题而生的。它的核心思想很简单:系统中的组件不直接调用彼此,而是通过"事件"来通信------我做了什么(事件),谁关心谁来听。

本文从事件驱动的本质出发,拆解核心概念、架构拓扑、消息可靠投递、事件溯源、在 iPaaS 平台事件中心的实战落地,以及我在真实项目中踩过的坑。不讲教科书定义,只讲"能落地"的东西。


一、事件驱动架构到底在解决什么问题?

1.1 同步调用的"三重困境"

在传统的 RPC/HTTP 同步调用架构中,系统面临三重困境:
#mermaid-svg-jNQdkODuinvZ4p2X{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-jNQdkODuinvZ4p2X .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jNQdkODuinvZ4p2X .error-icon{fill:#552222;}#mermaid-svg-jNQdkODuinvZ4p2X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jNQdkODuinvZ4p2X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jNQdkODuinvZ4p2X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jNQdkODuinvZ4p2X .marker.cross{stroke:#333333;}#mermaid-svg-jNQdkODuinvZ4p2X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jNQdkODuinvZ4p2X p{margin:0;}#mermaid-svg-jNQdkODuinvZ4p2X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster-label text{fill:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster-label span{color:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster-label span p{background-color:transparent;}#mermaid-svg-jNQdkODuinvZ4p2X .label text,#mermaid-svg-jNQdkODuinvZ4p2X span{fill:#333;color:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .node rect,#mermaid-svg-jNQdkODuinvZ4p2X .node circle,#mermaid-svg-jNQdkODuinvZ4p2X .node ellipse,#mermaid-svg-jNQdkODuinvZ4p2X .node polygon,#mermaid-svg-jNQdkODuinvZ4p2X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jNQdkODuinvZ4p2X .rough-node .label text,#mermaid-svg-jNQdkODuinvZ4p2X .node .label text,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape .label,#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape .label{text-anchor:middle;}#mermaid-svg-jNQdkODuinvZ4p2X .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jNQdkODuinvZ4p2X .rough-node .label,#mermaid-svg-jNQdkODuinvZ4p2X .node .label,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape .label,#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape .label{text-align:center;}#mermaid-svg-jNQdkODuinvZ4p2X .node.clickable{cursor:pointer;}#mermaid-svg-jNQdkODuinvZ4p2X .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jNQdkODuinvZ4p2X .arrowheadPath{fill:#333333;}#mermaid-svg-jNQdkODuinvZ4p2X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jNQdkODuinvZ4p2X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jNQdkODuinvZ4p2X .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jNQdkODuinvZ4p2X .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jNQdkODuinvZ4p2X .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jNQdkODuinvZ4p2X .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jNQdkODuinvZ4p2X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster text{fill:#333;}#mermaid-svg-jNQdkODuinvZ4p2X .cluster span{color:#333;}#mermaid-svg-jNQdkODuinvZ4p2X 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-jNQdkODuinvZ4p2X .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jNQdkODuinvZ4p2X rect.text{fill:none;stroke-width:0;}#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape p,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jNQdkODuinvZ4p2X .icon-shape .label rect,#mermaid-svg-jNQdkODuinvZ4p2X .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jNQdkODuinvZ4p2X .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jNQdkODuinvZ4p2X .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jNQdkODuinvZ4p2X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 困境三:吞吐瓶颈
串行执行
串行执行
串行执行
API入口
处理1
处理2
处理3
困境二:扩展耦合
硬编码调用
硬编码调用
硬编码调用
订单服务
积分服务
风控服务
数据服务
困境一:级联故障
同步调用
同步调用
超时!
订单服务
库存服务
通知服务
全链路阻塞

困境 表现 后果
级联故障 下游超时,上游连接堆积 一个慢服务拖垮全系统
扩展耦合 加一个下游要改上游代码 系统无法独立演进
吞吐瓶颈 串行同步执行 整体延迟 = 各环节延迟之和

1.2 事件驱动的核心主张

事件驱动架构用一句话概括:系统中的事件是核心,组件通过异步产生和响应事件来协作,解耦生产者和消费者。
#mermaid-svg-RURz89mARnW2Wv71{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-RURz89mARnW2Wv71 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RURz89mARnW2Wv71 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RURz89mARnW2Wv71 .error-icon{fill:#552222;}#mermaid-svg-RURz89mARnW2Wv71 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RURz89mARnW2Wv71 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RURz89mARnW2Wv71 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RURz89mARnW2Wv71 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RURz89mARnW2Wv71 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RURz89mARnW2Wv71 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RURz89mARnW2Wv71 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RURz89mARnW2Wv71 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RURz89mARnW2Wv71 .marker.cross{stroke:#333333;}#mermaid-svg-RURz89mARnW2Wv71 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RURz89mARnW2Wv71 p{margin:0;}#mermaid-svg-RURz89mARnW2Wv71 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RURz89mARnW2Wv71 .cluster-label text{fill:#333;}#mermaid-svg-RURz89mARnW2Wv71 .cluster-label span{color:#333;}#mermaid-svg-RURz89mARnW2Wv71 .cluster-label span p{background-color:transparent;}#mermaid-svg-RURz89mARnW2Wv71 .label text,#mermaid-svg-RURz89mARnW2Wv71 span{fill:#333;color:#333;}#mermaid-svg-RURz89mARnW2Wv71 .node rect,#mermaid-svg-RURz89mARnW2Wv71 .node circle,#mermaid-svg-RURz89mARnW2Wv71 .node ellipse,#mermaid-svg-RURz89mARnW2Wv71 .node polygon,#mermaid-svg-RURz89mARnW2Wv71 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RURz89mARnW2Wv71 .rough-node .label text,#mermaid-svg-RURz89mARnW2Wv71 .node .label text,#mermaid-svg-RURz89mARnW2Wv71 .image-shape .label,#mermaid-svg-RURz89mARnW2Wv71 .icon-shape .label{text-anchor:middle;}#mermaid-svg-RURz89mARnW2Wv71 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RURz89mARnW2Wv71 .rough-node .label,#mermaid-svg-RURz89mARnW2Wv71 .node .label,#mermaid-svg-RURz89mARnW2Wv71 .image-shape .label,#mermaid-svg-RURz89mARnW2Wv71 .icon-shape .label{text-align:center;}#mermaid-svg-RURz89mARnW2Wv71 .node.clickable{cursor:pointer;}#mermaid-svg-RURz89mARnW2Wv71 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RURz89mARnW2Wv71 .arrowheadPath{fill:#333333;}#mermaid-svg-RURz89mARnW2Wv71 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RURz89mARnW2Wv71 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RURz89mARnW2Wv71 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RURz89mARnW2Wv71 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RURz89mARnW2Wv71 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RURz89mARnW2Wv71 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RURz89mARnW2Wv71 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RURz89mARnW2Wv71 .cluster text{fill:#333;}#mermaid-svg-RURz89mARnW2Wv71 .cluster span{color:#333;}#mermaid-svg-RURz89mARnW2Wv71 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-RURz89mARnW2Wv71 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RURz89mARnW2Wv71 rect.text{fill:none;stroke-width:0;}#mermaid-svg-RURz89mARnW2Wv71 .icon-shape,#mermaid-svg-RURz89mARnW2Wv71 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RURz89mARnW2Wv71 .icon-shape p,#mermaid-svg-RURz89mARnW2Wv71 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RURz89mARnW2Wv71 .icon-shape .label rect,#mermaid-svg-RURz89mARnW2Wv71 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RURz89mARnW2Wv71 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RURz89mARnW2Wv71 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RURz89mARnW2Wv71 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 发布事件
发布事件
订阅分发
订阅分发
订阅分发
生产者A
事件总线

Event Broker
生产者B
消费者1
消费者2
消费者3

三个核心主张:

  1. 解耦(Decoupling):生产者不知道消费者的存在,消费者不知道事件从哪来。加一个新的消费者?注册一个订阅就行,生产者的代码一行不改。

  2. 异步(Asynchrony):生产者发出事件就返回,不等消费者处理完。整体延迟从"串行累加"变成"并行处理"。

  3. 弹性(Resilience):消费者挂了?事件在消息队列里等着,恢复后继续消费。不会因为一个节点故障导致全链路崩溃。

1.3 事件驱动适合什么系统?

系统类型 是否适合 EDA 原因
CRUD 管理后台 ❌ 不适合 请求-响应模型,同步调用更直观
报表查询系统 ❌ 不适合 用户需要即时结果,异步无意义
IoT 物联网平台 ✅ 非常适合 海量设备异步上报,高吞吐实时处理
实时推荐系统 ✅ 非常适合 用户行为事件流 → 实时特征计算 → 推荐更新
iPaaS 集成平台 ✅ 非常适合 Webhook/CDC/定时触发 → 异步流程编排
电商交易系统 ✅ 部分适合 下单后的库存、通知、积分适合事件驱动
金融交易系统 ⚠️ 谨慎使用 高吞吐场景适合,但强一致性场景需额外设计

判断标准:你的系统是否存在"一个操作触发多个下游"、"需要高吞吐异步处理"、"下游数量经常变化"这三个特征中的至少一个。


二、事件驱动架构的核心概念

2.1 事件(Event)

事件是"已经发生的事实"------不可变的、轻量级的、带有时间戳的状态变更记录。
#mermaid-svg-40snaFmQ2a0nh5tM{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-40snaFmQ2a0nh5tM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-40snaFmQ2a0nh5tM .error-icon{fill:#552222;}#mermaid-svg-40snaFmQ2a0nh5tM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-40snaFmQ2a0nh5tM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-40snaFmQ2a0nh5tM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-40snaFmQ2a0nh5tM .marker.cross{stroke:#333333;}#mermaid-svg-40snaFmQ2a0nh5tM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-40snaFmQ2a0nh5tM p{margin:0;}#mermaid-svg-40snaFmQ2a0nh5tM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster-label text{fill:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster-label span{color:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster-label span p{background-color:transparent;}#mermaid-svg-40snaFmQ2a0nh5tM .label text,#mermaid-svg-40snaFmQ2a0nh5tM span{fill:#333;color:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .node rect,#mermaid-svg-40snaFmQ2a0nh5tM .node circle,#mermaid-svg-40snaFmQ2a0nh5tM .node ellipse,#mermaid-svg-40snaFmQ2a0nh5tM .node polygon,#mermaid-svg-40snaFmQ2a0nh5tM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-40snaFmQ2a0nh5tM .rough-node .label text,#mermaid-svg-40snaFmQ2a0nh5tM .node .label text,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape .label,#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape .label{text-anchor:middle;}#mermaid-svg-40snaFmQ2a0nh5tM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-40snaFmQ2a0nh5tM .rough-node .label,#mermaid-svg-40snaFmQ2a0nh5tM .node .label,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape .label,#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape .label{text-align:center;}#mermaid-svg-40snaFmQ2a0nh5tM .node.clickable{cursor:pointer;}#mermaid-svg-40snaFmQ2a0nh5tM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-40snaFmQ2a0nh5tM .arrowheadPath{fill:#333333;}#mermaid-svg-40snaFmQ2a0nh5tM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-40snaFmQ2a0nh5tM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-40snaFmQ2a0nh5tM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-40snaFmQ2a0nh5tM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-40snaFmQ2a0nh5tM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-40snaFmQ2a0nh5tM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-40snaFmQ2a0nh5tM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster text{fill:#333;}#mermaid-svg-40snaFmQ2a0nh5tM .cluster span{color:#333;}#mermaid-svg-40snaFmQ2a0nh5tM 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-40snaFmQ2a0nh5tM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-40snaFmQ2a0nh5tM rect.text{fill:none;stroke-width:0;}#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape p,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-40snaFmQ2a0nh5tM .icon-shape .label rect,#mermaid-svg-40snaFmQ2a0nh5tM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-40snaFmQ2a0nh5tM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-40snaFmQ2a0nh5tM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-40snaFmQ2a0nh5tM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 事件的本质
事件名(过去式)

OrderCreated
事件载荷(Payload)

订单ID/金额/商品列表
元数据(Metadata)

时间戳/来源/追踪ID

事件和命令(Command)的区别:

维度 事件(Event) 命令(Command)
语义 "已经发生了什么" "请去做什么"
命名 过去式:OrderCreated 祈使句:CreateOrder
目标 无特定目标,广播给所有订阅者 有明确目标,发给指定处理器
耦合度 低(生产者不知道谁在消费) 高(调用方知道被调用方)
失败处理 消费者各自重试 调用方重试或回滚
java 复制代码
// 事件:已经发生的事实
public record OrderCreatedEvent(
    String orderId,
    String userId,
    BigDecimal totalAmount,
    List<OrderItem> items,
    Instant createdAt          // 时间戳
) {}

// 命令:要求执行的操作
public record CreateOrderCommand(
    String userId,
    List<OrderItem> items
) {}

2.2 事件通道(Event Channel)

事件从生产者到消费者的传输管道。根据传输方式不同,分为三种:
#mermaid-svg-TknrVCHhrLAf2QZD{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-TknrVCHhrLAf2QZD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TknrVCHhrLAf2QZD .error-icon{fill:#552222;}#mermaid-svg-TknrVCHhrLAf2QZD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TknrVCHhrLAf2QZD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TknrVCHhrLAf2QZD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TknrVCHhrLAf2QZD .marker.cross{stroke:#333333;}#mermaid-svg-TknrVCHhrLAf2QZD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TknrVCHhrLAf2QZD p{margin:0;}#mermaid-svg-TknrVCHhrLAf2QZD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster-label text{fill:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster-label span{color:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster-label span p{background-color:transparent;}#mermaid-svg-TknrVCHhrLAf2QZD .label text,#mermaid-svg-TknrVCHhrLAf2QZD span{fill:#333;color:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .node rect,#mermaid-svg-TknrVCHhrLAf2QZD .node circle,#mermaid-svg-TknrVCHhrLAf2QZD .node ellipse,#mermaid-svg-TknrVCHhrLAf2QZD .node polygon,#mermaid-svg-TknrVCHhrLAf2QZD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TknrVCHhrLAf2QZD .rough-node .label text,#mermaid-svg-TknrVCHhrLAf2QZD .node .label text,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape .label,#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape .label{text-anchor:middle;}#mermaid-svg-TknrVCHhrLAf2QZD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TknrVCHhrLAf2QZD .rough-node .label,#mermaid-svg-TknrVCHhrLAf2QZD .node .label,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape .label,#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape .label{text-align:center;}#mermaid-svg-TknrVCHhrLAf2QZD .node.clickable{cursor:pointer;}#mermaid-svg-TknrVCHhrLAf2QZD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TknrVCHhrLAf2QZD .arrowheadPath{fill:#333333;}#mermaid-svg-TknrVCHhrLAf2QZD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TknrVCHhrLAf2QZD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TknrVCHhrLAf2QZD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TknrVCHhrLAf2QZD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TknrVCHhrLAf2QZD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TknrVCHhrLAf2QZD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TknrVCHhrLAf2QZD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster text{fill:#333;}#mermaid-svg-TknrVCHhrLAf2QZD .cluster span{color:#333;}#mermaid-svg-TknrVCHhrLAf2QZD 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-TknrVCHhrLAf2QZD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TknrVCHhrLAf2QZD rect.text{fill:none;stroke-width:0;}#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape p,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TknrVCHhrLAf2QZD .icon-shape .label rect,#mermaid-svg-TknrVCHhrLAf2QZD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TknrVCHhrLAf2QZD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TknrVCHhrLAf2QZD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TknrVCHhrLAf2QZD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 请求-响应通道
请求事件
响应事件
请求者
请求队列
响应者
响应队列
发布-订阅通道
生产者
Topic
消费者A
消费者B
消费者C
点对点通道
生产者
队列
唯一消费者

通道类型 典型实现 适用场景
点对点 RabbitMQ Queue、RocketMQ 普通消息 任务分发,一个事件只需一个消费者处理
发布-订阅 Kafka Topic、RocketMQ Topic 事件广播,一个事件需要多个消费者各自处理
请求-响应 NATS Request-Reply 需要异步获取结果的场景

2.3 事件代理(Event Broker)

事件代理是事件驱动架构的"中枢神经"------所有事件都经过它来路由和分发。
#mermaid-svg-8qcp7oeZDGL4pONH{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-8qcp7oeZDGL4pONH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8qcp7oeZDGL4pONH .error-icon{fill:#552222;}#mermaid-svg-8qcp7oeZDGL4pONH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8qcp7oeZDGL4pONH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8qcp7oeZDGL4pONH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8qcp7oeZDGL4pONH .marker.cross{stroke:#333333;}#mermaid-svg-8qcp7oeZDGL4pONH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8qcp7oeZDGL4pONH p{margin:0;}#mermaid-svg-8qcp7oeZDGL4pONH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster-label text{fill:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster-label span{color:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster-label span p{background-color:transparent;}#mermaid-svg-8qcp7oeZDGL4pONH .label text,#mermaid-svg-8qcp7oeZDGL4pONH span{fill:#333;color:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .node rect,#mermaid-svg-8qcp7oeZDGL4pONH .node circle,#mermaid-svg-8qcp7oeZDGL4pONH .node ellipse,#mermaid-svg-8qcp7oeZDGL4pONH .node polygon,#mermaid-svg-8qcp7oeZDGL4pONH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8qcp7oeZDGL4pONH .rough-node .label text,#mermaid-svg-8qcp7oeZDGL4pONH .node .label text,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape .label,#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape .label{text-anchor:middle;}#mermaid-svg-8qcp7oeZDGL4pONH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8qcp7oeZDGL4pONH .rough-node .label,#mermaid-svg-8qcp7oeZDGL4pONH .node .label,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape .label,#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape .label{text-align:center;}#mermaid-svg-8qcp7oeZDGL4pONH .node.clickable{cursor:pointer;}#mermaid-svg-8qcp7oeZDGL4pONH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8qcp7oeZDGL4pONH .arrowheadPath{fill:#333333;}#mermaid-svg-8qcp7oeZDGL4pONH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8qcp7oeZDGL4pONH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8qcp7oeZDGL4pONH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8qcp7oeZDGL4pONH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8qcp7oeZDGL4pONH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8qcp7oeZDGL4pONH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8qcp7oeZDGL4pONH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster text{fill:#333;}#mermaid-svg-8qcp7oeZDGL4pONH .cluster span{color:#333;}#mermaid-svg-8qcp7oeZDGL4pONH 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-8qcp7oeZDGL4pONH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8qcp7oeZDGL4pONH rect.text{fill:none;stroke-width:0;}#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape p,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8qcp7oeZDGL4pONH .icon-shape .label rect,#mermaid-svg-8qcp7oeZDGL4pONH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8qcp7oeZDGL4pONH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8qcp7oeZDGL4pONH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8qcp7oeZDGL4pONH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 事件代理
事件接收

协议适配/鉴权
事件存储

持久化/回放
事件路由

Topic/Tag过滤
事件投递

推送/拉取
生产者1
生产者2
消费者1
消费者2
消费者3

主流消息中间件的选型对比:

中间件 吞吐量 延迟 消息模型 适用场景
RocketMQ 十万级/秒 毫秒级 发布-订阅+点对点 电商/金融/企业集成
Kafka 百万级/秒 毫秒级 发布-订阅(Log) 日志/大数据/IoT
RabbitMQ 万级/秒 微秒级 灵活路由 企业内部集成/任务队列
Pulsar 百万级/秒 毫秒级 多租户+分层存储 超大规模多租户场景

我们在 iPaaS 平台选择了 RocketMQ------它的事务消息、顺序消息、延时消息等企业级特性,非常契合集成平台的场景。

2.4 事件处理的三种模式

#mermaid-svg-HhTUkAXIssch2uaE{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-HhTUkAXIssch2uaE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HhTUkAXIssch2uaE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HhTUkAXIssch2uaE .error-icon{fill:#552222;}#mermaid-svg-HhTUkAXIssch2uaE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HhTUkAXIssch2uaE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HhTUkAXIssch2uaE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HhTUkAXIssch2uaE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HhTUkAXIssch2uaE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HhTUkAXIssch2uaE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HhTUkAXIssch2uaE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HhTUkAXIssch2uaE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HhTUkAXIssch2uaE .marker.cross{stroke:#333333;}#mermaid-svg-HhTUkAXIssch2uaE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HhTUkAXIssch2uaE p{margin:0;}#mermaid-svg-HhTUkAXIssch2uaE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HhTUkAXIssch2uaE .cluster-label text{fill:#333;}#mermaid-svg-HhTUkAXIssch2uaE .cluster-label span{color:#333;}#mermaid-svg-HhTUkAXIssch2uaE .cluster-label span p{background-color:transparent;}#mermaid-svg-HhTUkAXIssch2uaE .label text,#mermaid-svg-HhTUkAXIssch2uaE span{fill:#333;color:#333;}#mermaid-svg-HhTUkAXIssch2uaE .node rect,#mermaid-svg-HhTUkAXIssch2uaE .node circle,#mermaid-svg-HhTUkAXIssch2uaE .node ellipse,#mermaid-svg-HhTUkAXIssch2uaE .node polygon,#mermaid-svg-HhTUkAXIssch2uaE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HhTUkAXIssch2uaE .rough-node .label text,#mermaid-svg-HhTUkAXIssch2uaE .node .label text,#mermaid-svg-HhTUkAXIssch2uaE .image-shape .label,#mermaid-svg-HhTUkAXIssch2uaE .icon-shape .label{text-anchor:middle;}#mermaid-svg-HhTUkAXIssch2uaE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HhTUkAXIssch2uaE .rough-node .label,#mermaid-svg-HhTUkAXIssch2uaE .node .label,#mermaid-svg-HhTUkAXIssch2uaE .image-shape .label,#mermaid-svg-HhTUkAXIssch2uaE .icon-shape .label{text-align:center;}#mermaid-svg-HhTUkAXIssch2uaE .node.clickable{cursor:pointer;}#mermaid-svg-HhTUkAXIssch2uaE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HhTUkAXIssch2uaE .arrowheadPath{fill:#333333;}#mermaid-svg-HhTUkAXIssch2uaE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HhTUkAXIssch2uaE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HhTUkAXIssch2uaE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HhTUkAXIssch2uaE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HhTUkAXIssch2uaE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HhTUkAXIssch2uaE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HhTUkAXIssch2uaE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HhTUkAXIssch2uaE .cluster text{fill:#333;}#mermaid-svg-HhTUkAXIssch2uaE .cluster span{color:#333;}#mermaid-svg-HhTUkAXIssch2uaE 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-HhTUkAXIssch2uaE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HhTUkAXIssch2uaE rect.text{fill:none;stroke-width:0;}#mermaid-svg-HhTUkAXIssch2uaE .icon-shape,#mermaid-svg-HhTUkAXIssch2uaE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HhTUkAXIssch2uaE .icon-shape p,#mermaid-svg-HhTUkAXIssch2uaE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HhTUkAXIssch2uaE .icon-shape .label rect,#mermaid-svg-HhTUkAXIssch2uaE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HhTUkAXIssch2uaE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HhTUkAXIssch2uaE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HhTUkAXIssch2uaE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Saga 编排
成功
失败
订单事件
步骤1: 扣库存
步骤2: 扣款
补偿: 恢复库存
复杂事件处理(CEP)
事件流
CEP引擎

模式匹配/时间窗口
触发动作
简单事件处理
事件
单处理器

模式 说明 适用场景
简单事件处理 一个事件触发一个处理器 大多数业务场景
复杂事件处理(CEP) 多个事件组合满足模式后触发 风控/告警/IoT 异常检测
Saga 编排 长事务拆成多个事件驱动的步骤 跨服务的分布式事务

三、事件驱动架构的四种拓扑

3.1 中介者拓扑(Mediator Topology)

#mermaid-svg-CenZqxHc5n47vvBb{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-CenZqxHc5n47vvBb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CenZqxHc5n47vvBb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CenZqxHc5n47vvBb .error-icon{fill:#552222;}#mermaid-svg-CenZqxHc5n47vvBb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CenZqxHc5n47vvBb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CenZqxHc5n47vvBb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CenZqxHc5n47vvBb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CenZqxHc5n47vvBb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CenZqxHc5n47vvBb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CenZqxHc5n47vvBb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CenZqxHc5n47vvBb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CenZqxHc5n47vvBb .marker.cross{stroke:#333333;}#mermaid-svg-CenZqxHc5n47vvBb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CenZqxHc5n47vvBb p{margin:0;}#mermaid-svg-CenZqxHc5n47vvBb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CenZqxHc5n47vvBb .cluster-label text{fill:#333;}#mermaid-svg-CenZqxHc5n47vvBb .cluster-label span{color:#333;}#mermaid-svg-CenZqxHc5n47vvBb .cluster-label span p{background-color:transparent;}#mermaid-svg-CenZqxHc5n47vvBb .label text,#mermaid-svg-CenZqxHc5n47vvBb span{fill:#333;color:#333;}#mermaid-svg-CenZqxHc5n47vvBb .node rect,#mermaid-svg-CenZqxHc5n47vvBb .node circle,#mermaid-svg-CenZqxHc5n47vvBb .node ellipse,#mermaid-svg-CenZqxHc5n47vvBb .node polygon,#mermaid-svg-CenZqxHc5n47vvBb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CenZqxHc5n47vvBb .rough-node .label text,#mermaid-svg-CenZqxHc5n47vvBb .node .label text,#mermaid-svg-CenZqxHc5n47vvBb .image-shape .label,#mermaid-svg-CenZqxHc5n47vvBb .icon-shape .label{text-anchor:middle;}#mermaid-svg-CenZqxHc5n47vvBb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CenZqxHc5n47vvBb .rough-node .label,#mermaid-svg-CenZqxHc5n47vvBb .node .label,#mermaid-svg-CenZqxHc5n47vvBb .image-shape .label,#mermaid-svg-CenZqxHc5n47vvBb .icon-shape .label{text-align:center;}#mermaid-svg-CenZqxHc5n47vvBb .node.clickable{cursor:pointer;}#mermaid-svg-CenZqxHc5n47vvBb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CenZqxHc5n47vvBb .arrowheadPath{fill:#333333;}#mermaid-svg-CenZqxHc5n47vvBb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CenZqxHc5n47vvBb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CenZqxHc5n47vvBb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CenZqxHc5n47vvBb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CenZqxHc5n47vvBb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CenZqxHc5n47vvBb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CenZqxHc5n47vvBb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CenZqxHc5n47vvBb .cluster text{fill:#333;}#mermaid-svg-CenZqxHc5n47vvBb .cluster span{color:#333;}#mermaid-svg-CenZqxHc5n47vvBb 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-CenZqxHc5n47vvBb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CenZqxHc5n47vvBb rect.text{fill:none;stroke-width:0;}#mermaid-svg-CenZqxHc5n47vvBb .icon-shape,#mermaid-svg-CenZqxHc5n47vvBb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CenZqxHc5n47vvBb .icon-shape p,#mermaid-svg-CenZqxHc5n47vvBb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CenZqxHc5n47vvBb .icon-shape .label rect,#mermaid-svg-CenZqxHc5n47vvBb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CenZqxHc5n47vvBb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CenZqxHc5n47vvBb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CenZqxHc5n47vvBb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 中介者拓扑
步骤1
事件A完成
步骤2
事件B完成
步骤3
初始事件
事件中介者

编排步骤
事件处理器A
事件处理器B
事件处理器C

特点:有一个"指挥官"决定事件处理的顺序。类似流程引擎------事件到达后,由中介者编排处理步骤。

适用场景:业务流程有明确的步骤顺序,如订单处理(校验→扣库存→支付→发货)。

3.2 代理者拓扑(Broker Topology)

#mermaid-svg-HBdIEfMzQjYjSlpx{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-HBdIEfMzQjYjSlpx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HBdIEfMzQjYjSlpx .error-icon{fill:#552222;}#mermaid-svg-HBdIEfMzQjYjSlpx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HBdIEfMzQjYjSlpx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HBdIEfMzQjYjSlpx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HBdIEfMzQjYjSlpx .marker.cross{stroke:#333333;}#mermaid-svg-HBdIEfMzQjYjSlpx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HBdIEfMzQjYjSlpx p{margin:0;}#mermaid-svg-HBdIEfMzQjYjSlpx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster-label text{fill:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster-label span{color:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster-label span p{background-color:transparent;}#mermaid-svg-HBdIEfMzQjYjSlpx .label text,#mermaid-svg-HBdIEfMzQjYjSlpx span{fill:#333;color:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .node rect,#mermaid-svg-HBdIEfMzQjYjSlpx .node circle,#mermaid-svg-HBdIEfMzQjYjSlpx .node ellipse,#mermaid-svg-HBdIEfMzQjYjSlpx .node polygon,#mermaid-svg-HBdIEfMzQjYjSlpx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HBdIEfMzQjYjSlpx .rough-node .label text,#mermaid-svg-HBdIEfMzQjYjSlpx .node .label text,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape .label,#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape .label{text-anchor:middle;}#mermaid-svg-HBdIEfMzQjYjSlpx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HBdIEfMzQjYjSlpx .rough-node .label,#mermaid-svg-HBdIEfMzQjYjSlpx .node .label,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape .label,#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape .label{text-align:center;}#mermaid-svg-HBdIEfMzQjYjSlpx .node.clickable{cursor:pointer;}#mermaid-svg-HBdIEfMzQjYjSlpx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HBdIEfMzQjYjSlpx .arrowheadPath{fill:#333333;}#mermaid-svg-HBdIEfMzQjYjSlpx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HBdIEfMzQjYjSlpx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HBdIEfMzQjYjSlpx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HBdIEfMzQjYjSlpx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HBdIEfMzQjYjSlpx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HBdIEfMzQjYjSlpx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster text{fill:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx .cluster span{color:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx 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-HBdIEfMzQjYjSlpx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HBdIEfMzQjYjSlpx rect.text{fill:none;stroke-width:0;}#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape p,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HBdIEfMzQjYjSlpx .icon-shape .label rect,#mermaid-svg-HBdIEfMzQjYjSlpx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HBdIEfMzQjYjSlpx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HBdIEfMzQjYjSlpx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HBdIEfMzQjYjSlpx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 代理者拓扑
产生事件B
产生事件C
事件A
处理器1
处理器2
处理器3

特点:没有中心编排者,每个处理器消费事件后产生新事件,下一个处理器自动接力。像"接力赛"------每一棒跑完把接力棒传下去。

适用场景:处理链路比较固定、步骤间有自然的数据流转关系。

3.3 广播拓扑(Broadcast Topology)

#mermaid-svg-AqDYtNo2DK1Brlyn{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-AqDYtNo2DK1Brlyn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AqDYtNo2DK1Brlyn .error-icon{fill:#552222;}#mermaid-svg-AqDYtNo2DK1Brlyn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AqDYtNo2DK1Brlyn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AqDYtNo2DK1Brlyn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AqDYtNo2DK1Brlyn .marker.cross{stroke:#333333;}#mermaid-svg-AqDYtNo2DK1Brlyn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AqDYtNo2DK1Brlyn p{margin:0;}#mermaid-svg-AqDYtNo2DK1Brlyn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster-label text{fill:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster-label span{color:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster-label span p{background-color:transparent;}#mermaid-svg-AqDYtNo2DK1Brlyn .label text,#mermaid-svg-AqDYtNo2DK1Brlyn span{fill:#333;color:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .node rect,#mermaid-svg-AqDYtNo2DK1Brlyn .node circle,#mermaid-svg-AqDYtNo2DK1Brlyn .node ellipse,#mermaid-svg-AqDYtNo2DK1Brlyn .node polygon,#mermaid-svg-AqDYtNo2DK1Brlyn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AqDYtNo2DK1Brlyn .rough-node .label text,#mermaid-svg-AqDYtNo2DK1Brlyn .node .label text,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape .label,#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape .label{text-anchor:middle;}#mermaid-svg-AqDYtNo2DK1Brlyn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AqDYtNo2DK1Brlyn .rough-node .label,#mermaid-svg-AqDYtNo2DK1Brlyn .node .label,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape .label,#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape .label{text-align:center;}#mermaid-svg-AqDYtNo2DK1Brlyn .node.clickable{cursor:pointer;}#mermaid-svg-AqDYtNo2DK1Brlyn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AqDYtNo2DK1Brlyn .arrowheadPath{fill:#333333;}#mermaid-svg-AqDYtNo2DK1Brlyn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AqDYtNo2DK1Brlyn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AqDYtNo2DK1Brlyn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AqDYtNo2DK1Brlyn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AqDYtNo2DK1Brlyn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AqDYtNo2DK1Brlyn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster text{fill:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn .cluster span{color:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn 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-AqDYtNo2DK1Brlyn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AqDYtNo2DK1Brlyn rect.text{fill:none;stroke-width:0;}#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape p,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AqDYtNo2DK1Brlyn .icon-shape .label rect,#mermaid-svg-AqDYtNo2DK1Brlyn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AqDYtNo2DK1Brlyn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AqDYtNo2DK1Brlyn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AqDYtNo2DK1Brlyn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 广播拓扑
事件源
事件总线
处理器1

发通知
处理器2

更新缓存
处理器3

写日志
处理器4

推数据仓库

特点:一个事件同时触发多个独立的处理器,彼此之间无依赖关系。

适用场景:一个业务事件需要多个系统各自独立响应。比如"订单已支付"需要通知、日志、缓存、数仓各自处理。

3.4 中介者-代理者混合拓扑

实际系统大多是混合拓扑------关键流程用中介者编排,非关键旁路用广播分发。
#mermaid-svg-wZDvXTmrOXJg1Axk{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-wZDvXTmrOXJg1Axk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wZDvXTmrOXJg1Axk .error-icon{fill:#552222;}#mermaid-svg-wZDvXTmrOXJg1Axk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wZDvXTmrOXJg1Axk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wZDvXTmrOXJg1Axk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wZDvXTmrOXJg1Axk .marker.cross{stroke:#333333;}#mermaid-svg-wZDvXTmrOXJg1Axk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wZDvXTmrOXJg1Axk p{margin:0;}#mermaid-svg-wZDvXTmrOXJg1Axk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster-label text{fill:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster-label span{color:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster-label span p{background-color:transparent;}#mermaid-svg-wZDvXTmrOXJg1Axk .label text,#mermaid-svg-wZDvXTmrOXJg1Axk span{fill:#333;color:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .node rect,#mermaid-svg-wZDvXTmrOXJg1Axk .node circle,#mermaid-svg-wZDvXTmrOXJg1Axk .node ellipse,#mermaid-svg-wZDvXTmrOXJg1Axk .node polygon,#mermaid-svg-wZDvXTmrOXJg1Axk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wZDvXTmrOXJg1Axk .rough-node .label text,#mermaid-svg-wZDvXTmrOXJg1Axk .node .label text,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape .label,#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape .label{text-anchor:middle;}#mermaid-svg-wZDvXTmrOXJg1Axk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wZDvXTmrOXJg1Axk .rough-node .label,#mermaid-svg-wZDvXTmrOXJg1Axk .node .label,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape .label,#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape .label{text-align:center;}#mermaid-svg-wZDvXTmrOXJg1Axk .node.clickable{cursor:pointer;}#mermaid-svg-wZDvXTmrOXJg1Axk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wZDvXTmrOXJg1Axk .arrowheadPath{fill:#333333;}#mermaid-svg-wZDvXTmrOXJg1Axk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wZDvXTmrOXJg1Axk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wZDvXTmrOXJg1Axk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wZDvXTmrOXJg1Axk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wZDvXTmrOXJg1Axk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wZDvXTmrOXJg1Axk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster text{fill:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk .cluster span{color:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk 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-wZDvXTmrOXJg1Axk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wZDvXTmrOXJg1Axk rect.text{fill:none;stroke-width:0;}#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape p,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wZDvXTmrOXJg1Axk .icon-shape .label rect,#mermaid-svg-wZDvXTmrOXJg1Axk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wZDvXTmrOXJg1Axk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wZDvXTmrOXJg1Axk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wZDvXTmrOXJg1Axk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 步骤1
库存已扣
步骤2
扣款成功
订单已完成
订单创建事件
订单中介者
扣库存
扣款
订单完成事件
广播总线
通知服务
日志服务
积分服务
数据仓库


四、iPaaS 事件中心实战

理论讲够了,来看我们 iPaaS 平台的真实事件驱动架构。

4.1 事件中心在 iPaaS 中的定位

iPaaS 是一个集成平台,核心使命是"让不同的系统通过事件联动起来"。事件中心是整个平台的"神经中枢":
#mermaid-svg-cM5E1rWmm4sVsVZq{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-cM5E1rWmm4sVsVZq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cM5E1rWmm4sVsVZq .error-icon{fill:#552222;}#mermaid-svg-cM5E1rWmm4sVsVZq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cM5E1rWmm4sVsVZq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cM5E1rWmm4sVsVZq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cM5E1rWmm4sVsVZq .marker.cross{stroke:#333333;}#mermaid-svg-cM5E1rWmm4sVsVZq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cM5E1rWmm4sVsVZq p{margin:0;}#mermaid-svg-cM5E1rWmm4sVsVZq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster-label text{fill:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster-label span{color:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster-label span p{background-color:transparent;}#mermaid-svg-cM5E1rWmm4sVsVZq .label text,#mermaid-svg-cM5E1rWmm4sVsVZq span{fill:#333;color:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .node rect,#mermaid-svg-cM5E1rWmm4sVsVZq .node circle,#mermaid-svg-cM5E1rWmm4sVsVZq .node ellipse,#mermaid-svg-cM5E1rWmm4sVsVZq .node polygon,#mermaid-svg-cM5E1rWmm4sVsVZq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cM5E1rWmm4sVsVZq .rough-node .label text,#mermaid-svg-cM5E1rWmm4sVsVZq .node .label text,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape .label,#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape .label{text-anchor:middle;}#mermaid-svg-cM5E1rWmm4sVsVZq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cM5E1rWmm4sVsVZq .rough-node .label,#mermaid-svg-cM5E1rWmm4sVsVZq .node .label,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape .label,#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape .label{text-align:center;}#mermaid-svg-cM5E1rWmm4sVsVZq .node.clickable{cursor:pointer;}#mermaid-svg-cM5E1rWmm4sVsVZq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cM5E1rWmm4sVsVZq .arrowheadPath{fill:#333333;}#mermaid-svg-cM5E1rWmm4sVsVZq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cM5E1rWmm4sVsVZq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cM5E1rWmm4sVsVZq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cM5E1rWmm4sVsVZq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cM5E1rWmm4sVsVZq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cM5E1rWmm4sVsVZq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster text{fill:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq .cluster span{color:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq 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-cM5E1rWmm4sVsVZq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cM5E1rWmm4sVsVZq rect.text{fill:none;stroke-width:0;}#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape p,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cM5E1rWmm4sVsVZq .icon-shape .label rect,#mermaid-svg-cM5E1rWmm4sVsVZq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cM5E1rWmm4sVsVZq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cM5E1rWmm4sVsVZq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cM5E1rWmm4sVsVZq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 基础设施
流程引擎
事件中心
事件来源(600+ 第三方应用)
Webhook 推送

钉钉/企微/飞书/...
定时轮询

HTTP/数据库/API
数据变更捕获

MySQL/PG/MongoDB
定时触发

Cron 表达式
事件接收层

BaseEventController
消息生产者

PushEventProducer
消息消费者

PushEventListener
触发器系统

Push/Polling/CDC
流程编排执行
连接器调用
RocketMQ

push_event Topic
Redis

去重/限流

4.2 事件中心的架构设计

事件中心的核心职责:接收第三方系统推送的事件 → 标准化封装 → 异步投递到 RocketMQ → 引擎消费并触发流程。

4.2.1 事件接收层

每个第三方应用有独立的 EventController,继承 BaseEventController,负责接收 Webhook 回调:

java 复制代码
// 事件接收基类------统一的事件入口
@Component
public class BaseEventController {
    @Resource
    PushEventProducer pushEventProducer;

    /**
     * 构建标准化事件消息体
     */
    protected PushEventMessage buildBody(
            String processor,
            ConnectorKey connectorKey,
            AssetKey assetKey,
            @Nullable Object event,
            String body) {
        PushEventMessage msg = new PushEventMessage();
        msg.setProcessor(processor);
        msg.setConnectorKey(connectorKey);
        msg.setAssetKey(assetKey);
        msg.setEvent(event);
        msg.setMessage(body);
        return msg;
    }

    /**
     * 异步投递事件到 MQ
     */
    protected void sendMessage(PushEventMessage pushEventMessage) {
        String message = JSON.toJSONString(pushEventMessage);
        pushEventProducer.sendMessage(message);
    }

    /**
     * 快速响应第三方------不等流程执行完
     */
    protected Object buildResponse() {
        Map<String, Object> response = new HashMap<>();
        response.put("code", 0);
        response.put("msg", "success");
        return response;
    }
}

以小红书事件控制器为例:

java 复制代码
@RestController
@RequestMapping("/event/xiaohongshu")
public class XiaohongshuEventController extends BaseEventController {

    @PostMapping("/pushEvent")
    public Object pushEvent(
            @RequestHeader("app-key") String appKey,
            @RequestHeader("sign") String sign,
            @RequestBody String body) {

        // 1. 解析第三方推送的事件数据
        List<XiaohongshuPushing> messages = JSON.parseArray(body, ...);

        // 2. 逐条封装为标准化事件消息
        messages.forEach(message -> {
            PushEventMessage msg = this.buildBody(
                PROCESS_NAME,
                new ConnectorKey(CONNECTOR_ID, connectorVersion),
                AssetKey.builder()
                    .type(AssetOwnerTypeEnum.ASSET_UK.getCode())
                    .assetId(message.getSellerId())
                    .build(),
                message.getMsgTag(),
                (String) message.getData()
            );
            // 3. 异步投递到 MQ------不等流程执行
            this.sendMessage(msg);
        });

        // 4. 立即响应第三方------毫秒级返回
        return buildResponse();
    }
}

关键设计决策

  1. 快速 ACK:收到 Webhook 后立即返回成功,不等流程执行完。如果同步执行流程,第三方超时会导致连接堆积
  2. 标准化封装 :600+ 个第三方应用的事件格式千差万别,但在事件中心统一封装成 PushEventMessage,下游引擎不需要关心来源差异
  3. 继承体系BaseEventController 封装公共逻辑,各应用的 Controller 只关注自身的事件解析
4.2.2 消息投递层
java 复制代码
@Component
public class PushEventProducer {
    @Resource
    private Producer producer;

    @Value("${rocketmq.push.event.topic:push_event}")
    private String topic;

    /**
     * 发送普通事件消息
     */
    public void sendMessage(String msgBody) {
        final Message message = provider.newMessageBuilder()
            .setTopic(topic)
            .setKeys(PUSH_EVENT_TAG)
            .setTag(PUSH_EVENT_TAG)
            .setBody(msgBody.getBytes(StandardCharsets.UTF_8))
            .build();
        producer.send(message);
    }

    /**
     * 发送顺序消息------保证同一事件源的顺序性
     */
    public void sendOrderlyMessage(String msgBody, String orderTag) {
        // 按 orderTag 分区,保证同一分区内消息有序
        ...
    }
}
4.2.3 消息消费层
java 复制代码
@Component
public class PushEventListener extends BaseConsumer implements MessageListener {
    @Override
    public ConsumeResult consume(MessageView messageView) {
        // 从 MQ 消费事件消息,触发流程引擎执行
        String body = ByteBufferUtils.byteBufferToString(messageView.getBody());
        return doConsume(body);
    }
}

4.3 引擎侧的触发器体系

事件中心投递消息后,引擎侧通过触发器体系响应事件:
#mermaid-svg-B3BdQ2PWBn9NuLc5{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-B3BdQ2PWBn9NuLc5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .error-icon{fill:#552222;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .marker.cross{stroke:#333333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 p{margin:0;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster-label text{fill:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster-label span{color:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster-label span p{background-color:transparent;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .label text,#mermaid-svg-B3BdQ2PWBn9NuLc5 span{fill:#333;color:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .node rect,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node circle,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node ellipse,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node polygon,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .rough-node .label text,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node .label text,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape .label,#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .rough-node .label,#mermaid-svg-B3BdQ2PWBn9NuLc5 .node .label,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape .label,#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape .label{text-align:center;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .node.clickable{cursor:pointer;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .arrowheadPath{fill:#333333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-B3BdQ2PWBn9NuLc5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B3BdQ2PWBn9NuLc5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster text{fill:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .cluster span{color:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 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-B3BdQ2PWBn9NuLc5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-B3BdQ2PWBn9NuLc5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape p,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .icon-shape .label rect,#mermaid-svg-B3BdQ2PWBn9NuLc5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B3BdQ2PWBn9NuLc5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-B3BdQ2PWBn9NuLc5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-B3BdQ2PWBn9NuLc5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 流程引擎执行
四种触发器类型
推送触发器 PushEventTrigger

接收 Webhook/MQ 事件
轮询触发器 HttpPollingTrigger

Cron 定时拉取
CDC触发器 DataBaseCdcTrigger

数据库变更捕获
定时触发器 TimerTrigger

Cron 定时执行
TriggerExchange

事件上下文
FlowInstance

流程实例
ExecuteMachine

执行机

四种触发模式对比

触发器 事件来源 实时性 适用场景
推送触发器 第三方 Webhook → MQ 实时 钉钉消息回调、电商订单推送
轮询触发器 Cron 定时拉取 API 准实时(秒级) 不支持 Webhook 的系统
CDC 触发器 数据库 Binlog 准实时(秒级) 数据库变更监听
定时触发器 Cron 定时执行 定时(分钟级) 定时报表、数据同步

4.4 一个事件的完整生命周期

以"钉钉机器人消息触发流程"为例:
SAP系统 流程引擎 RocketMQ 事件中心 钉钉服务器 SAP系统 流程引擎 RocketMQ 事件中心 钉钉服务器 #mermaid-svg-tN0G8UoFBJ8DNTOB{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-tN0G8UoFBJ8DNTOB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tN0G8UoFBJ8DNTOB .error-icon{fill:#552222;}#mermaid-svg-tN0G8UoFBJ8DNTOB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tN0G8UoFBJ8DNTOB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tN0G8UoFBJ8DNTOB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tN0G8UoFBJ8DNTOB .marker.cross{stroke:#333333;}#mermaid-svg-tN0G8UoFBJ8DNTOB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tN0G8UoFBJ8DNTOB p{margin:0;}#mermaid-svg-tN0G8UoFBJ8DNTOB .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tN0G8UoFBJ8DNTOB text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tN0G8UoFBJ8DNTOB .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB .sequenceNumber{fill:white;}#mermaid-svg-tN0G8UoFBJ8DNTOB #sequencenumber{fill:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-tN0G8UoFBJ8DNTOB .messageText{fill:#333;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tN0G8UoFBJ8DNTOB .labelText,#mermaid-svg-tN0G8UoFBJ8DNTOB .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .loopText,#mermaid-svg-tN0G8UoFBJ8DNTOB .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .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-tN0G8UoFBJ8DNTOB .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-tN0G8UoFBJ8DNTOB .noteText,#mermaid-svg-tN0G8UoFBJ8DNTOB .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-tN0G8UoFBJ8DNTOB .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tN0G8UoFBJ8DNTOB .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tN0G8UoFBJ8DNTOB .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tN0G8UoFBJ8DNTOB .actorPopupMenu{position:absolute;}#mermaid-svg-tN0G8UoFBJ8DNTOB .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-tN0G8UoFBJ8DNTOB .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tN0G8UoFBJ8DNTOB .actor-man circle,#mermaid-svg-tN0G8UoFBJ8DNTOB line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-tN0G8UoFBJ8DNTOB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 事件在队列中等待消费 POST /event/dingding/webhook 验签 + 解析消息 sendMessage(PushEventMessage) 200 OK(< 50ms) consume(MessageView) 匹配触发器 → 创建流程实例 加载流程定义 → 逐节点执行 HTTP 调用 SAP 接口 返回业务数据 写入执行日志

关键设计要点

  1. 毫秒级响应第三方:事件中心只做"接收 → 封装 → 投递"三步,不做任何业务处理。钉钉服务器不会因为流程执行慢而超时
  2. 削峰填谷:高并发 Webhook 涌入时,事件堆积在 RocketMQ 中,引擎按自己的消费能力逐步处理
  3. 故障隔离:引擎挂了?事件在 MQ 中安全存储,恢复后继续消费,零数据丢失

五、事件驱动架构中最常踩的八个坑

5.1 坑一:事件丢失------消息发了但没人收到

症状:用户反馈"流程没有被触发",查日志发现事件确实发了,但消费者没收到。

为什么丢

  • 生产者发送失败但没重试
  • MQ 宕机时未持久化的消息丢失
  • 消费者处理失败但标记了消费成功

修复方案------三道防线
#mermaid-svg-3FRja9MXT2YFDFoQ{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-3FRja9MXT2YFDFoQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3FRja9MXT2YFDFoQ .error-icon{fill:#552222;}#mermaid-svg-3FRja9MXT2YFDFoQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3FRja9MXT2YFDFoQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3FRja9MXT2YFDFoQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3FRja9MXT2YFDFoQ .marker.cross{stroke:#333333;}#mermaid-svg-3FRja9MXT2YFDFoQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3FRja9MXT2YFDFoQ p{margin:0;}#mermaid-svg-3FRja9MXT2YFDFoQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster-label text{fill:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster-label span{color:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster-label span p{background-color:transparent;}#mermaid-svg-3FRja9MXT2YFDFoQ .label text,#mermaid-svg-3FRja9MXT2YFDFoQ span{fill:#333;color:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .node rect,#mermaid-svg-3FRja9MXT2YFDFoQ .node circle,#mermaid-svg-3FRja9MXT2YFDFoQ .node ellipse,#mermaid-svg-3FRja9MXT2YFDFoQ .node polygon,#mermaid-svg-3FRja9MXT2YFDFoQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3FRja9MXT2YFDFoQ .rough-node .label text,#mermaid-svg-3FRja9MXT2YFDFoQ .node .label text,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape .label,#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-3FRja9MXT2YFDFoQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3FRja9MXT2YFDFoQ .rough-node .label,#mermaid-svg-3FRja9MXT2YFDFoQ .node .label,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape .label,#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape .label{text-align:center;}#mermaid-svg-3FRja9MXT2YFDFoQ .node.clickable{cursor:pointer;}#mermaid-svg-3FRja9MXT2YFDFoQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3FRja9MXT2YFDFoQ .arrowheadPath{fill:#333333;}#mermaid-svg-3FRja9MXT2YFDFoQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3FRja9MXT2YFDFoQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3FRja9MXT2YFDFoQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3FRja9MXT2YFDFoQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3FRja9MXT2YFDFoQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3FRja9MXT2YFDFoQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster text{fill:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ .cluster span{color:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ 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-3FRja9MXT2YFDFoQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3FRja9MXT2YFDFoQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape p,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3FRja9MXT2YFDFoQ .icon-shape .label rect,#mermaid-svg-3FRja9MXT2YFDFoQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3FRja9MXT2YFDFoQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3FRja9MXT2YFDFoQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3FRja9MXT2YFDFoQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 防线三:消费端确认
投递
处理成功
处理失败
MQ
消费者
ACK
NACK → 重试
防线二:Broker持久化
同步刷盘
主从复制
MQ
磁盘
从节点
防线一:生产端确认
发送
ACK
失败重试
生产者
MQ

5.2 坑二:重复消费------一个事件被处理了三次

症状:用户收到三条一样的通知短信,数据库里出现重复记录。

为什么重复

  • 消费者处理成功后 ACK 失败,MQ 重新投递
  • 生产者超时重试,同一条消息发了两次
  • 消费者重启,未 ACK 的消息被重新投递

修复方案------幂等性设计

java 复制代码
/**
 * 幂等消费:同一个事件只处理一次
 */
public ConsumeResult consume(MessageView messageView) {
    String eventId = extractEventId(messageView);
    
    // 1. 幂等检查:用 Redis 记录已处理的事件ID
    if (redisTemplate.opsForValue().setIfAbsent(
            "consumed:" + eventId, "1", 24, TimeUnit.HOURS)) {
        // 2. 第一次处理:执行业务逻辑
        return doProcess(messageView);
    } else {
        // 3. 重复消息:跳过
        log.warn("Duplicate event detected: {}", eventId);
        return ConsumeResult.SUCCESS;
    }
}

幂等设计的核心原则:消费者必须假设"同一条消息可能被投递多次",并确保多次处理的结果和一次处理完全一致。

5.3 坑三:事件顺序错乱------先消费了"取消"再消费"创建"

症状:订单先收到"取消"事件,再收到"创建"事件------状态机乱了。

为什么乱序

  • MQ 的多分区并行消费
  • 消费者多线程处理
  • 网络延迟导致后发的消息先到

修复方案

方案 说明 适用场景
分区有序 同一实体的事件发到同一分区 大部分场景(推荐)
版本号 事件带版本号,低版本丢弃 数据同步场景
时间戳排序 消费者按时间戳重排序 容忍一定延迟的批处理
java 复制代码
// 分区有序:同一 assetId 的事件进入同一分区
public void sendOrderlyMessage(String msgBody, String orderTag) {
    final Message message = provider.newMessageBuilder()
        .setTopic(orderTopic)
        .setKeys(orderTag)          // 用 orderTag 做分区键
        .setTag(PUSH_ORDER_EVENT_TAG)
        .setBody(msgBody.getBytes(StandardCharsets.UTF_8))
        .build();
    producer.send(message);          // 同一 orderTag 保证顺序
}

5.4 坑四:消费者雪崩------一个慢消费者拖垮所有人

症状:某个消费者的处理逻辑特别慢(比如调外部 API 超时),导致消息堆积,其他正常消费者也受影响。

修复方案

  1. 独立消费组:不同类型的消费者使用独立的消费组,互不影响
  2. 消费超时:设置单条消息的最大处理时间
  3. 死信队列:多次重试失败的消息进入死信队列,不阻塞正常消费

#mermaid-svg-dDCSwpClY5jViGXV{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-dDCSwpClY5jViGXV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dDCSwpClY5jViGXV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dDCSwpClY5jViGXV .error-icon{fill:#552222;}#mermaid-svg-dDCSwpClY5jViGXV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dDCSwpClY5jViGXV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dDCSwpClY5jViGXV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dDCSwpClY5jViGXV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dDCSwpClY5jViGXV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dDCSwpClY5jViGXV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dDCSwpClY5jViGXV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dDCSwpClY5jViGXV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dDCSwpClY5jViGXV .marker.cross{stroke:#333333;}#mermaid-svg-dDCSwpClY5jViGXV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dDCSwpClY5jViGXV p{margin:0;}#mermaid-svg-dDCSwpClY5jViGXV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dDCSwpClY5jViGXV .cluster-label text{fill:#333;}#mermaid-svg-dDCSwpClY5jViGXV .cluster-label span{color:#333;}#mermaid-svg-dDCSwpClY5jViGXV .cluster-label span p{background-color:transparent;}#mermaid-svg-dDCSwpClY5jViGXV .label text,#mermaid-svg-dDCSwpClY5jViGXV span{fill:#333;color:#333;}#mermaid-svg-dDCSwpClY5jViGXV .node rect,#mermaid-svg-dDCSwpClY5jViGXV .node circle,#mermaid-svg-dDCSwpClY5jViGXV .node ellipse,#mermaid-svg-dDCSwpClY5jViGXV .node polygon,#mermaid-svg-dDCSwpClY5jViGXV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dDCSwpClY5jViGXV .rough-node .label text,#mermaid-svg-dDCSwpClY5jViGXV .node .label text,#mermaid-svg-dDCSwpClY5jViGXV .image-shape .label,#mermaid-svg-dDCSwpClY5jViGXV .icon-shape .label{text-anchor:middle;}#mermaid-svg-dDCSwpClY5jViGXV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dDCSwpClY5jViGXV .rough-node .label,#mermaid-svg-dDCSwpClY5jViGXV .node .label,#mermaid-svg-dDCSwpClY5jViGXV .image-shape .label,#mermaid-svg-dDCSwpClY5jViGXV .icon-shape .label{text-align:center;}#mermaid-svg-dDCSwpClY5jViGXV .node.clickable{cursor:pointer;}#mermaid-svg-dDCSwpClY5jViGXV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dDCSwpClY5jViGXV .arrowheadPath{fill:#333333;}#mermaid-svg-dDCSwpClY5jViGXV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dDCSwpClY5jViGXV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dDCSwpClY5jViGXV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dDCSwpClY5jViGXV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dDCSwpClY5jViGXV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dDCSwpClY5jViGXV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dDCSwpClY5jViGXV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dDCSwpClY5jViGXV .cluster text{fill:#333;}#mermaid-svg-dDCSwpClY5jViGXV .cluster span{color:#333;}#mermaid-svg-dDCSwpClY5jViGXV 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-dDCSwpClY5jViGXV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dDCSwpClY5jViGXV rect.text{fill:none;stroke-width:0;}#mermaid-svg-dDCSwpClY5jViGXV .icon-shape,#mermaid-svg-dDCSwpClY5jViGXV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dDCSwpClY5jViGXV .icon-shape p,#mermaid-svg-dDCSwpClY5jViGXV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dDCSwpClY5jViGXV .icon-shape .label rect,#mermaid-svg-dDCSwpClY5jViGXV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dDCSwpClY5jViGXV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dDCSwpClY5jViGXV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dDCSwpClY5jViGXV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 死信处理
正常消费
重试3次失败
Topic
消费组A

通知服务
消费组B

日志服务
消费组C

积分服务
死信队列

DLQ
告警通知
人工重试

5.5 坑五:事件风暴------一个操作触发一百个事件

症状:用户点了一个按钮,系统产生了几百个事件,MQ 被塞满,消费延迟飙升。

为什么 :批量操作没有合并事件。比如"批量导入 1000 条客户",如果每条都发一个 CustomerCreated 事件,就是 1000 个事件。

修复方案

方案 说明
事件合并 批量操作发一个 CustomerBatchImported 事件,包含 ID 列表
事件节流 限制同一源头在单位时间内的最大事件数
批量消费 消费者一次拉取多条消息批量处理

5.6 坑六:同步伪装成异步------发事件后还在等结果

症状 :Controller 发了事件后,还轮询数据库等结果,或者用 Thread.sleep 等待。

为什么是坑:本质上还是同步思维,白白浪费了异步的优势,还多了一层 MQ 的开销。

修复方案:真正的异步是"发了就走"。前端需要结果?用 WebSocket 推送,或者轮询状态接口。

5.7 坑七:事件模式未定义------生产者和消费者各说各话

症状:生产者改了事件字段名,消费者还在用旧字段名读取------线上静默失败,数据全是 null。

修复方案

  1. 事件模式注册:每个事件有明确的 Schema(JSON Schema / Protobuf)
  2. 版本管理:事件结构变更时递增版本号,消费者兼容多版本
  3. 契约测试:生产者和消费者共享 Schema,CI 中校验兼容性

5.8 坑八:忽视可观测性------出了问题完全无法排查

症状:用户说"我的流程没触发",你查了一圈不知道事件到哪儿了。

修复方案------事件追踪三件套

维度 做法 价值
Trace ID 事件携带全局唯一的追踪 ID 串联从生产到消费的全链路
事件日志 记录每个事件的投递/消费/ACK 状态 出问题时有据可查
消费监控 监控消费延迟/堆积量/失败率 问题发生前就能预警

六、事件溯源(Event Sourcing):把事件当数据库

6.1 什么是事件溯源?

传统做法:数据库里存"当前状态"------一条订单记录存了 status = 'COMPLETED',你不知道它经历了什么。

事件溯源:不存状态,只存事件。当前状态 = 所有事件的叠加。
#mermaid-svg-oFu7iVwhhv2fofxT{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-oFu7iVwhhv2fofxT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oFu7iVwhhv2fofxT .error-icon{fill:#552222;}#mermaid-svg-oFu7iVwhhv2fofxT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oFu7iVwhhv2fofxT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oFu7iVwhhv2fofxT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oFu7iVwhhv2fofxT .marker.cross{stroke:#333333;}#mermaid-svg-oFu7iVwhhv2fofxT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oFu7iVwhhv2fofxT p{margin:0;}#mermaid-svg-oFu7iVwhhv2fofxT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster-label text{fill:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster-label span{color:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster-label span p{background-color:transparent;}#mermaid-svg-oFu7iVwhhv2fofxT .label text,#mermaid-svg-oFu7iVwhhv2fofxT span{fill:#333;color:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .node rect,#mermaid-svg-oFu7iVwhhv2fofxT .node circle,#mermaid-svg-oFu7iVwhhv2fofxT .node ellipse,#mermaid-svg-oFu7iVwhhv2fofxT .node polygon,#mermaid-svg-oFu7iVwhhv2fofxT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oFu7iVwhhv2fofxT .rough-node .label text,#mermaid-svg-oFu7iVwhhv2fofxT .node .label text,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape .label,#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape .label{text-anchor:middle;}#mermaid-svg-oFu7iVwhhv2fofxT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oFu7iVwhhv2fofxT .rough-node .label,#mermaid-svg-oFu7iVwhhv2fofxT .node .label,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape .label,#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape .label{text-align:center;}#mermaid-svg-oFu7iVwhhv2fofxT .node.clickable{cursor:pointer;}#mermaid-svg-oFu7iVwhhv2fofxT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oFu7iVwhhv2fofxT .arrowheadPath{fill:#333333;}#mermaid-svg-oFu7iVwhhv2fofxT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oFu7iVwhhv2fofxT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oFu7iVwhhv2fofxT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oFu7iVwhhv2fofxT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oFu7iVwhhv2fofxT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oFu7iVwhhv2fofxT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oFu7iVwhhv2fofxT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster text{fill:#333;}#mermaid-svg-oFu7iVwhhv2fofxT .cluster span{color:#333;}#mermaid-svg-oFu7iVwhhv2fofxT 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-oFu7iVwhhv2fofxT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oFu7iVwhhv2fofxT rect.text{fill:none;stroke-width:0;}#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape p,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oFu7iVwhhv2fofxT .icon-shape .label rect,#mermaid-svg-oFu7iVwhhv2fofxT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oFu7iVwhhv2fofxT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oFu7iVwhhv2fofxT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oFu7iVwhhv2fofxT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 事件溯源:事件存储
重放
Event Store
OrderCreated
PaymentReceived
OrderShipped
OrderDelivered
当前状态

= COMPLETED
传统:状态存储
MySQL

status = COMPLETED

6.2 事件溯源的收益

收益 说明
完整审计 每一步变更都有记录,满足金融/医疗合规要求
时间旅行 可以回放任意时间点的事件,重建当时的状态
投影灵活 同一组事件可以投影成不同的读模型(订单视图/统计视图/风控视图)
天然解耦 写操作只追加事件,读操作从投影读取

6.3 事件溯源的代价

代价 说明
查询复杂 不能直接查"所有已完成的订单"------需要投影
性能开销 重放百万条事件来重建状态,需要快照机制
学习曲线 团队要从"更新状态"的思维转为"追加事件"的思维
模式演进 事件结构变更后,旧事件和新事件的兼容处理

适用场景:金融交易审计、保险保单全生命周期、医疗病历------任何"需要知道发生了什么而不只是现在是什么"的场景。


七、事件驱动 + CQRS:读写分离的终极形态

7.1 架构全景

#mermaid-svg-f5mx92VaRWH5T4wc{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-f5mx92VaRWH5T4wc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f5mx92VaRWH5T4wc .error-icon{fill:#552222;}#mermaid-svg-f5mx92VaRWH5T4wc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f5mx92VaRWH5T4wc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f5mx92VaRWH5T4wc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f5mx92VaRWH5T4wc .marker.cross{stroke:#333333;}#mermaid-svg-f5mx92VaRWH5T4wc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f5mx92VaRWH5T4wc p{margin:0;}#mermaid-svg-f5mx92VaRWH5T4wc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster-label text{fill:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster-label span{color:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster-label span p{background-color:transparent;}#mermaid-svg-f5mx92VaRWH5T4wc .label text,#mermaid-svg-f5mx92VaRWH5T4wc span{fill:#333;color:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .node rect,#mermaid-svg-f5mx92VaRWH5T4wc .node circle,#mermaid-svg-f5mx92VaRWH5T4wc .node ellipse,#mermaid-svg-f5mx92VaRWH5T4wc .node polygon,#mermaid-svg-f5mx92VaRWH5T4wc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f5mx92VaRWH5T4wc .rough-node .label text,#mermaid-svg-f5mx92VaRWH5T4wc .node .label text,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape .label,#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape .label{text-anchor:middle;}#mermaid-svg-f5mx92VaRWH5T4wc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-f5mx92VaRWH5T4wc .rough-node .label,#mermaid-svg-f5mx92VaRWH5T4wc .node .label,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape .label,#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape .label{text-align:center;}#mermaid-svg-f5mx92VaRWH5T4wc .node.clickable{cursor:pointer;}#mermaid-svg-f5mx92VaRWH5T4wc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-f5mx92VaRWH5T4wc .arrowheadPath{fill:#333333;}#mermaid-svg-f5mx92VaRWH5T4wc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f5mx92VaRWH5T4wc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f5mx92VaRWH5T4wc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f5mx92VaRWH5T4wc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-f5mx92VaRWH5T4wc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f5mx92VaRWH5T4wc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-f5mx92VaRWH5T4wc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster text{fill:#333;}#mermaid-svg-f5mx92VaRWH5T4wc .cluster span{color:#333;}#mermaid-svg-f5mx92VaRWH5T4wc 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-f5mx92VaRWH5T4wc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-f5mx92VaRWH5T4wc rect.text{fill:none;stroke-width:0;}#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape p,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-f5mx92VaRWH5T4wc .icon-shape .label rect,#mermaid-svg-f5mx92VaRWH5T4wc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f5mx92VaRWH5T4wc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-f5mx92VaRWH5T4wc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-f5mx92VaRWH5T4wc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询
读端:Query
写端:Command
事件流
写入 API
命令处理器
领域模型

业务规则校验
事件存储

Event Store
事件投影器
订单视图

MySQL
统计视图

Doris
搜索视图

ES
查询 API

写端 只负责接收命令 → 校验业务规则 → 追加事件。

读端 消费事件 → 更新各自的读模型(可以是不同的数据库)。

查询端直连读模型,毫秒级返回。

7.2 iPaaS 的实践:执行日志的 CQRS

我们在 iPaaS 的执行日志场景就使用了类似 CQRS 的思路:
#mermaid-svg-KXOAOKNwTAQb8rrR{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-KXOAOKNwTAQb8rrR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KXOAOKNwTAQb8rrR .error-icon{fill:#552222;}#mermaid-svg-KXOAOKNwTAQb8rrR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KXOAOKNwTAQb8rrR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KXOAOKNwTAQb8rrR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KXOAOKNwTAQb8rrR .marker.cross{stroke:#333333;}#mermaid-svg-KXOAOKNwTAQb8rrR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KXOAOKNwTAQb8rrR p{margin:0;}#mermaid-svg-KXOAOKNwTAQb8rrR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster-label text{fill:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster-label span{color:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster-label span p{background-color:transparent;}#mermaid-svg-KXOAOKNwTAQb8rrR .label text,#mermaid-svg-KXOAOKNwTAQb8rrR span{fill:#333;color:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .node rect,#mermaid-svg-KXOAOKNwTAQb8rrR .node circle,#mermaid-svg-KXOAOKNwTAQb8rrR .node ellipse,#mermaid-svg-KXOAOKNwTAQb8rrR .node polygon,#mermaid-svg-KXOAOKNwTAQb8rrR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KXOAOKNwTAQb8rrR .rough-node .label text,#mermaid-svg-KXOAOKNwTAQb8rrR .node .label text,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape .label,#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape .label{text-anchor:middle;}#mermaid-svg-KXOAOKNwTAQb8rrR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KXOAOKNwTAQb8rrR .rough-node .label,#mermaid-svg-KXOAOKNwTAQb8rrR .node .label,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape .label,#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape .label{text-align:center;}#mermaid-svg-KXOAOKNwTAQb8rrR .node.clickable{cursor:pointer;}#mermaid-svg-KXOAOKNwTAQb8rrR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KXOAOKNwTAQb8rrR .arrowheadPath{fill:#333333;}#mermaid-svg-KXOAOKNwTAQb8rrR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KXOAOKNwTAQb8rrR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KXOAOKNwTAQb8rrR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KXOAOKNwTAQb8rrR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KXOAOKNwTAQb8rrR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KXOAOKNwTAQb8rrR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster text{fill:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR .cluster span{color:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR 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-KXOAOKNwTAQb8rrR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KXOAOKNwTAQb8rrR rect.text{fill:none;stroke-width:0;}#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape p,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KXOAOKNwTAQb8rrR .icon-shape .label rect,#mermaid-svg-KXOAOKNwTAQb8rrR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KXOAOKNwTAQb8rrR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KXOAOKNwTAQb8rrR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KXOAOKNwTAQb8rrR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 读端
数据同步
写端
Flink CDC
流程引擎
MySQL

权威数据源
Doris

列存引擎
查询 API

  • 写链路:流程执行日志写入 MySQL,经过业务规则校验
  • 同步层:Flink CDC 异步将数据同步到 Doris
  • 读链路:查询直接走 Doris 列存引擎,亿级日志毫秒级返回

八、事件驱动架构落地 Checklist

事件设计

  • 事件名使用过去式(OrderCreated 而非 CreateOrder
  • 事件载荷包含足够下游处理的信息,不过多不过少
  • 事件携带元数据(时间戳、来源、Trace ID)
  • 事件 Schema 有版本管理,变更向后兼容

消息可靠性

  • 生产端有发送确认 + 失败重试
  • MQ 配置持久化 + 主从复制
  • 消费端实现幂等处理
  • 死信队列配置 + 告警

架构设计

  • 生产者和消费者通过 MQ 解耦,不直接依赖
  • 关键流程使用顺序消息保证有序性
  • 消费者有独立消费组,互不影响
  • 事件中心有削峰能力(MQ 缓冲)

可观测性

  • 事件全链路有 Trace ID 串联
  • 消费延迟/堆积量有监控告警
  • 事件日志可查看投递和消费状态
  • 有死信队列的巡检和重试机制

九、事件驱动架构的七个反模式

反模式 1:同步伪装异步

Controller 发了事件后轮询数据库等结果,本质上还是同步。

修复:发了就走,前端用 WebSocket 或轮询状态接口获取结果。

反模式 2:事件大杂烩

所有业务变更都发一个 DataChanged 事件,消费者要自己判断到底是什么变了。

修复 :事件类型要细粒度------OrderCreatedOrderPaidOrderShipped,消费者只订阅自己关心的事件。

反模式 3:消费者耦合

消费者在处理事件时直接调用另一个消费者的 API------变成了隐式的同步调用链。

修复:消费者只处理事件、产生新事件,不直接调用其他消费者。

反模式 4:忽视幂等

消费者假设"每条消息只会被处理一次"------重试时产生重复数据。

修复:每个消费者必须实现幂等处理。用 Redis 去重、数据库唯一约束、或状态机校验。

反模式 5:上帝事件

一个事件包含了系统中所有字段(几百个字段),每个消费者只用其中几个。

修复:事件只包含必要信息。消费者需要更多数据?通过事件中的 ID 去查询。

反模式 6:事件黑洞

事件被消费后没有任何可观测的输出------不知道处理成功了还是失败了。

修复:每个消费者的处理结果(成功/失败/跳过)都要有日志和指标。关键事件处理完可以发一个"结果事件"。

反模式 7:过度事件化

一个三层的 CRUD 后台,连用户改个密码都发事件。简单的事情用方法调用就够了。

修复:只在以下场景使用事件------跨服务通信、一个操作多个下游、需要异步处理、需要审计追溯。其他场景用同步调用。


十、事件驱动 vs 其他架构模式

维度 同步调用 事件驱动 请求-响应 + 事件混合
耦合度 高(硬编码调用) 低(通过 MQ 解耦) 中等
延迟 高(串行累加) 低(并行处理) 视场景而定
吞吐量 受限于最慢环节 高(MQ 削峰缓冲)
一致性 强一致(分布式事务) 最终一致 混合
可观测性 调用链清晰 需要额外追踪 较复杂
调试难度 低(单步调试) 高(异步链路) 中等
适用场景 强一致/简单流程 高吞吐/多下游/异步 大多数企业系统

没有银弹:大部分企业系统最终会走向"混合架构"------关键路径用同步调用保证强一致,旁路操作用事件驱动解耦。


十一、常见问题(FAQ)

Q:事件驱动和消息队列是一回事吗?

A:不是。消息队列是事件驱动架构的基础设施之一,但事件驱动是一套架构思想------包括事件设计、拓扑选择、可靠性保证、幂等处理等一系列设计决策。用了 MQ 不代表做了事件驱动,就像用了 Spring 不代表做了微服务。

Q:事件驱动能保证数据一致性吗?

A:能保证,但保证的是"最终一致性"而非"强一致性"。如果业务必须强一致(如银行转账),要么用分布式事务(Saga/TCC),要么这部分用同步调用。事件驱动和强一致性不是对立的------RocketMQ 的事务消息可以在一定程度上兼顾两者。

Q:消费者处理失败了怎么办?

A:三层兜底:① MQ 自动重试(指数退避);② 死信队列 + 告警;③ 人工介入 + 手动重试。关键是消费者要幂等------重试不能产生副作用。

Q:事件太多会不会把 MQ 撑爆?

A:会的。需要做好容量规划:① 合理设置 Topic 分区数;② 事件有过期时间(TTL);③ 监控消费延迟和堆积量;④ 批量事件做合并。

Q:什么时候该用顺序消息?

A:当同一实体的事件有严格的时序要求时。比如"订单创建"必须在"订单支付"之前被消费。通过分区键(如 orderId)保证同一实体的事件进入同一分区,分区内有序。

Q:事件驱动和微服务是什么关系?

A:事件驱动是微服务之间通信的最佳实践之一。微服务解决"服务怎么拆",事件驱动解决"服务怎么连"。同步 RPC 适合强依赖,事件驱动适合松耦合。两者结合使用。


十二、小结

事件驱动架构是分布式系统中最强大、也最容易被误用的架构模式之一。三句话总结:

  1. 事件驱动的本质是"解耦+异步"------生产者不管谁在消费,消费者不管事件从哪来,中间通过 MQ 做缓冲和路由
  2. 事件驱动不是银弹------它用"最终一致性"和"架构复杂度"换取"高弹性"和"高扩展性"------适合多下游、高吞吐、异步处理的场景,不适合强一致、简单查询的场景
  3. 落地事件驱动的三大纪律是"可靠投递+幂等消费+全链路追踪"------做不到这三点,事件驱动就是一个"出了问题查不到"的黑盒

从明天开始,审视你手头的项目:哪些同步调用可以改成事件驱动?哪些事件的可靠性没有保证?消费者的幂等做了吗?

想清楚这些,你的分布式系统就稳了一半。


标签:#事件驱动架构 #EDA #消息队列 #RocketMQ #Kafka #异步架构 #事件溯源 #CQRS #分布式系统 #微服务 #Webhook #CDC #架构设计 #系统设计 #技术分享

相关推荐
隔窗听雨眠3 小时前
原生一体化多模态大模型技术研究:从拼接到统一的架构革命
人工智能·架构
小短腿的代码世界3 小时前
Qt D-Bus深度解析:跨进程通信高级架构与源码实现
qt·架构·系统架构
名不经传的养虾人3 小时前
从0到1:企业级AI项目迭代日记 Vol.44|功能建好,和功能接通,是两件完全不同的事
人工智能·架构·agent·ai编程·企业ai
:mnong3 小时前
学习模型驱动架构和图灵完备运行环境
架构
heimeiyingwang4 小时前
【架构实战】分布式会话:从Session到JWT的演进
微服务·云原生·架构
lulu12165440785 小时前
大模型API聚合平台技术架构深度对比:六大平台协议转换、路由调度与安全治理全解析 - 微元算力(weytoken)
java·人工智能·安全·架构·ai编程
wb043072016 小时前
仓库搬家不停业——从阿明的“在线换仓库“,看数据库迁移与 Schema 演进的实战方法论
数据库·adb·架构
小二·6 小时前
微服务架构设计与实践
微服务·架构·wpf
调试优选官6 小时前
2026上海GEO优化公司技术能力解析:从监测架构到知识库落地
架构·技术分享·geo·上海