从前有座城市,面包店、酒馆、铁匠铺各自营生,互相之间要传话。没有邮局的日子,面包店要通知裁缝铺“明早出炉新面包”,只能自己一条街一条街地跑,每条街都得跑一遍。后来城市建起中央邮局,所有人只需把信投进邮筒,谁想收信就去登记自己的信箱。这座邮局,就是微服务世界里的“事件总线”。订单服务不用再挨个调用库存、支付、通知服务,而是把“订单已创建”这封信扔进事件总线,谁关心谁订阅。接下来,你会看到邮局如何分拣信件(事件路由)、如何挂号保证不丢(可靠性)、如何识别重复信件(幂等去重)。学完这篇,你将掌握微服务解耦通信的设计骨架,成为团队里最懂“通信调度”的架构师。

岗位关联

被 A 调 B、B 调 C 的连环调用折磨的你是否在仰望“松耦合”的优雅?掌握事件总线设计,就掌握了微服务通信底盘的核心逻辑,这是后端工程师进阶架构师、在服务拆分中少踩坑的关键一步。

目录
  1. 可视化演示
  2. 事件总线概述
  3. 总线与消息队列
  4. 发布订阅模式
  5. 事件路由策略
  6. 可靠性保障
  7. 幂等与去重
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示事件总线hub-spoke拓扑、发布订阅消息流向及主题/直连/广播路由策略,揭示微服务松耦合通信的核心机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

事件总线Hub-Spoke架构 发布者:微服务将事件发布到事件总线Hub 事件总线:中心化事件分发,解耦生产与消费 订阅者:按订阅规则接收匹配的事件 Bus: Publisher → Hub → Subscriber(s) 事件总线实现服务间松耦合通信

事件总线概述

很久以前,一座城市没有邮局。面包店想告诉裁缝铺、酒馆、铁匠铺“明早出炉新面包”,只能自己一条街一条街地跑。店铺越多,跑腿的路径越密集,链路数随店铺数量平方增长,跑断腿也只通知了几家。后来城市建起中央邮局,面包店只需把信投进邮筒,剩下的事全部交给邮局。

这座中央邮局,就是微服务里的“事件总线”。在微服务架构中,下单、支付、发货就是一个个“事件”,事件总线负责接收、路由、分发这些事件,让服务之间不必互相知道对方的 IP 和接口。服务间通信从“跑腿递信”变成“投进邮筒”,这是架构层面一次彻底的结构简化。

事件总线的核心设计目标是“解耦”:发布者只关心自己发布了什么事件,不关心谁在处理;订阅者只声明自己关心哪些事件,不关心事件从哪来。解耦后,新增一个订阅方无需改动发布方代码,系统扩展从“改接口”变成“加订阅”。当前市场上的 Kafka、RabbitMQ、RocketMQ,都可以作为铺设这条“邮路”的基础设施。

城市有了邮局,跑腿的路被一条条大路取代;服务有了事件总线,点对点的强关联也退居二线。解耦带来的直接好处,就是业务扩展不再伤筋动骨,新增一家“店铺”只需要多修一条通往邮局的路。

Ldirect=N(N−1)2,Lbus=N\displaystyle L_{\text{direct}} = \frac{N(N-1)}{2}, \qquad L_{\text{bus}} = N
通俗说明:N 个服务互相通信,点对点模式需要维护 N(N-1)/2 条通信链路;引入事件总线后,每个服务只需要与总线建立一条连接,链路数降为 N。
  1. 直接通信链路数 L_directN 个服务两两直接调用,共有 C(N,2) 种组合,即 N 乘以 N 减 1 再除以 2。
    Ldirect=N(N−1)2\displaystyle L_{\text{direct}} = \frac{N(N-1)}{2}
  2. 总线链路数 L_bus引入事件总线后,每个服务只与总线单独建连,总共 N 条链路即可覆盖所有通信。
    Lbus=N\displaystyle L_{\text{bus}} = N

总线与消息队列

邮局里有一个很能干的邮件分拣车间,这就是“消息队列”。如果你把整个机构叫做“事件总线”,分拣车间只是其中最常用的工具之一。很多人把两者混为一谈,其实级别完全不一样:事件总线是“邮局的定位”,消息队列是“分拣车间的机器”。

更准确地说,事件总线是架构层面的“设计意图”,它回答的是“谁关心什么事件、事件如何到达”;消息队列是落地层面的“技术选型”,它回答的是“消息怎么存、怎么转发、怎么重试”。同一个事件总线,完全可以用不同 MQ 实现,也可以不用 MQ,而用 Redis Stream 甚至内存消息中间件。

实践中的常见做法是:用 Kafka 实现高吞吐日志同步型事件流,用 RabbitMQ 实现复杂路由和灵活交换器,用 RocketMQ 获得事务消息和更丰富的可靠性语义。但无论选哪个,都要先想清楚“事件类型定义”和“订阅关系治理”这两件事,它们属于架构层,不是 MQ 配置能替代的。选型的本质,是让“工具”匹配“设计意图”。

邮局是“总目标”,分拣车间是“分目标”的承重墙。理解了这层关系,你就不会在架构评审时闹出“把 MQ 当总线”或“总线必须用 MQ”的笑话,也能在技术选型时说出“我要的是可靠投递,RabbitMQ 比 Kafka 更合适”这样有理有据的判断。

EventBus=架构模式(事件类型与订阅关系)⏟设计层+MQ 技术(存储、重试、投递)⏟实现层\displaystyle \text{EventBus} = \underbrace{\text{架构模式}(\text{事件类型与订阅关系})}_{\text{设计层}} + \underbrace{\text{MQ 技术}(\text{存储、重试、投递})}_{\text{实现层}}
通俗说明:事件总线是“设计层”的约定,消息队列是“实现层”的载体。两者是分工关系,而非等价关系。
  1. 设计层架构模式事件总线首先要定义事件类型、订阅关系、责任边界,这属于架构设计的工作。
    架构模式(事件类型与订阅关系)\displaystyle \text{架构模式}(\text{事件类型与订阅关系})
  2. 实现层 MQ 技术消息队列负责具体的存储、重试、投递动作,是实现事件总线的常用载体。
    MQ 技术(存储、重试、投递)\displaystyle \text{MQ 技术}(\text{存储、重试、投递})

发布订阅模式

想象每家每户订杂志。杂志社不关心谁在读,只负责印刷好交给邮局;读者搬家了,杂志社也不用改计划。这就是发布订阅:生产者和消费者彻底“互不打扰”。在微服务架构中,这种模式是事件总线最基础的行为特征,几乎所有的解耦故事都从这里开始。

发布订阅有三个角色:发布者、事件总线、订阅者。发布者发布一条事件,不指定接收者;订阅者事先向总线声明自己的兴趣,例如“我关心 order.created”;当事件到达时,总线把消息复制给所有登记过的订阅者。发布者只认识总线,订阅者只认识总线,双方就像杂志社和读者一样从未见过面。

这种模式在工程上落地时,事件类型就是“杂志名”,订阅关系就是“订阅单”。总线内部维护事件类型到订阅者列表的映射,发布者调用 publish,总线遍历映射找到所有订阅者并投递。它的解耦表现非常直观:新增订阅者无需改动发布者,移除订阅者也不会让生产链路感知到变化,服务生命周期完全独立。

这就是杂志发行模式:一本杂志出版,所有订阅读者都会收到。发布订阅把微服务之间的“点名式呼叫”改变为“广播式触达”,让系统的扩展像增加订阅读者一样自然,也让服务的生命周期彼此独立、随时上下线。

Publisher→发布Bus→按订阅关系投递Subscribers\displaystyle \text{Publisher} \xrightarrow{\text{发布}} \text{Bus} \xrightarrow{\text{按订阅关系投递}} \text{Subscribers}
通俗说明:发布者把事件投给总线,总线根据订阅关系把事件分发给所有感兴趣的订阅者,发布者和订阅者全程不直接接触。
  1. 发布者发布事件发布者只负责把事件写进总线,不需要知道谁在监听。
    Publisher→发布Bus\displaystyle \text{Publisher} \xrightarrow{\text{发布}} \text{Bus}
  2. 总线按订阅关系投递总线维护了一张“事件类型 → 订阅者列表”的映射表,并据此完成消息复制和投递。
    Bus→按订阅关系投递Subscribers\displaystyle \text{Bus} \xrightarrow{\text{按订阅关系投递}} \text{Subscribers}

事件路由策略

邮局收到一封信,怎么决定送到哪里?看收件人的地址和类型。事件路由也是同样的道理:总线拿到一条事件后,要根据规则决定把它交给谁。有些通知要发给全城所有人,有些信只有特定订阅者才需要拆。这些规则就是事件总线的“分拣员”。

常见路由策略有三类。广播路由:把事件发给所有订阅者,适合全局通知类消息,比如“系统升级公告”。内容路由:根据事件内容中的字段,比如订单金额、用户等级、地区编码,决定发给哪些订阅者,适合精细化分发。基于主题的匹配:订阅者声明自己关心哪些主题,总线通过主题匹配完成过滤。

工程实现上,路由规则收敛在总线的路由表里。路由表由“事件类型 + 过滤条件”组成,订阅者在注册时提供这些信息,总线在分发时逐一匹配。这套机制让系统的新增和调整都集中在路由配置上,业务代码无需感知分拣逻辑,就像寄信人从不需要知道信走的是哪条街道。

分拣员按地址分类,总线按路由规则分发,道理一模一样。一个好的路由策略,能让事件在正确的时间到达正确的订阅者,既不会把无关服务卷入风暴,也不会漏掉任何真正关心消息的接收方。

Target={ s∣subscription(s)⊇event_type(e) }\displaystyle \text{Target} = \{\, s \mid \text{subscription}(s) \supseteq \text{event\_type}(e) \,\}
通俗说明:目标订阅者集合由所有“订阅内容覆盖该事件类型”的订阅者组成,总线只向这个集合投递。
  1. 枚举订阅者 s总线遍历当前系统里所有已注册的订阅者个体。
    s∈Subscribers\displaystyle s \in \text{Subscribers}
  2. 匹配判断如果某个订阅者的订阅集合包含了当前事件的类型,说明它对这条消息感兴趣。
    subscription(s)⊇event_type(e)\displaystyle \text{subscription}(s) \supseteq \text{event\_type}(e)
  3. 组成目标集合把所有满足匹配条件的订阅者收集起来,构成这一条事件的投递目标列表。
    Target={ s∣匹配结果 }\displaystyle \text{Target} = \{\, s \mid \text{匹配结果} \,\}

可靠性保障

重要信函要寄挂号信。邮局给你一张回执,信丢了可以查询、可以补寄、可以追责。事件总线也要给“重要事件”上保险:消息先持久化到磁盘,防止服务重启后信纸被烧毁;投递失败时自动重试,重试策略不可太野蛮;多次失败后转入死信队列,等待人工处理。

可靠性工程的第一件事是“消息持久化”。事件一旦被总线接收,就写入磁盘或消息队列的存储层,这样即使发布者或订阅者重启,事件也不会凭空消失。第二件事是“消费重试”。订阅者处理失败时,总线不会立即放弃,而是按一定间隔重新投递,直到成功或达到上限。

重试最怕的是“风暴式重试”:服务一恢复,所有积压消息同时涌来,瞬间又把服务打垮。所以工程上普遍采用指数退避,每次重试间隔翻倍,给系统留出恢复时间。当重试次数耗尽仍失败时,事件进入死信队列。死信队列是“收容所”,它把异常事件隔离起来,供开发人员排查原因后重新处置,而不是无限挤占正常消息的通道。

挂号信不会因为一次投递失败就消失,事件总线也不会因为一次消费失败就抛弃消息。持久化、指数退避、死信队列,这三件套让“消息可靠”不再是一句空话,而是一个可度量、可监控、可干预的工程承诺。

tn=t0×2n(n=0,1,2,…)\displaystyle t_n = t_0 \times 2^n \quad (n = 0, 1, 2, \ldots)
通俗说明:第 n 次重试前,先等待 t0 乘以 2 的 n 次方的时间。比如初始等待 1 秒,第一次重试等 2 秒,第二次等 4 秒,以此类推。
  1. 初始等待 t0重试策略的基准时间,通常取 1 秒或 2 秒,根据业务容忍度配置。
    t0\displaystyle t_0
  2. 指数项 2 的 n 次方每重试一次,指数加 1,等待时间翻倍,让系统逐步恢复而不是被瞬间打满。
    2n\displaystyle 2^n
  3. 第 n 次等待时间 tn初始等待时间乘以指数项,就是第 n 次重试前的总等待时长。
    tn=t0×2n\displaystyle t_n = t_0 \times 2^n

幂等与去重

同一封信,邮递员粗心送了两次。收件人第一次签了“收到”,第二次再签一次,回执单就多记了一条。怎么处理?让收件人看一眼信上的编号——同一编号处理过就不再处理。这就是幂等设计。分布式系统里,“重投”是常态,必须在消费端做好去重保护。

为什么重复不可避免?因为很多事件总线采用“至少一次”投递语义:为了确保不丢消息,允许重复投递。所以消费端会收到重复事件,这不可怕,可怕的是重复事件引发重复业务操作,比如重复扣库存、重复发短信、重复加积分。要兜住这批重复消息,必须引入“事件唯一 ID”和“幂等消费”。

工程上最常用的两种手段是:其一,消费者维护一个已处理事件 ID 的存储(如 Redis 或数据库表),处理前先查重;其二,利用数据库唯一约束,把事件 ID 作为唯一键插入,重复插入直接失败。更彻底的做法是在业务表上设计“幂等号”,以订单号、请求号等业务标识作为约束,保证同一业务操作只生效一次。

邮局可以犯错,但收件人凭编号把关,系统依然稳如泰山。幂等设计是事件驱动架构的“最后一道防线”,它把不可控的重复问题转化为可控的去重逻辑,让“至少一次”语义真正具备生产落地的资格。

process(deduplicate(e))=process(e),∀e\displaystyle \text{process}(\text{deduplicate}(e)) = \text{process}(e), \quad \forall e
通俗说明:先对事件去重再处理,得到的结果和第一次处理完全相同。也就是说,重复事件不会改变业务状态。
  1. 去重操作 deduplicate(e)根据事件携带的唯一 ID,在存储中检查该事件是否已经被处理过。
    deduplicate(e)\displaystyle \text{deduplicate}(e)
  2. 处理结果保持不变去重后再执行处理,与直接处理原始事件的效果一致,确保了业务状态最终唯一。
    process(deduplicate(e))=process(e)\displaystyle \text{process}(\text{deduplicate}(e)) = \text{process}(e)

AI 应用场景

推荐系统实时特征管道

阶段:数据采集与事件驱动特征工程

解决的问题:推荐系统需要实时消费用户行为事件(点击、收藏、下单),传统做法是服务端定时批量同步数据库,时效性差,特征更新滞后,且各服务与数据处理管道之间点对点耦合,新增一种行为类型就要改一遍上游接口。

具体案例:某电商平台的推荐系统,用户点击商品后,行为服务通过事件总线发布 user.clicked 事件,特征管道订阅该事件并实时更新用户向量特征库,推荐服务在秒级内拿到最新特征完成排序。点击量激增时,下游特征管道可以独立扩容,不用改动行为服务代码。

订单履约异步编排

阶段:业务系统异步解耦与状态联动

解决的问题:订单状态变化需要同步通知库存、积分、物流等多个服务。用 HTTP 逐个调用时,任何一个下游超时都会拖慢下单主流程,甚至导致订单创建失败。事件总线让订单服务只发布事件,下游各自订阅处理,互不阻塞。

具体案例:用户下单成功后,订单服务发布 order.created 事件,库存服务订阅后扣减库存,积分服务订阅后增加积分,物流服务订阅后创建运单。即使积分服务短暂不可用,订单主流程也不受影响,积分服务恢复后可重新消费积压事件。

监控告警事件流

阶段:日志指标采集与聚合分析

解决的问题:微服务数量多,异常日志散落在各个服务中,告警系统要统一收拢异常事件需要逐个对接接口,接入成本高且扩展性差。事件总线可以统一收集 service.error 事件,让监控系统只订阅一个来源即可获得全量异常数据。

具体案例:某金融系统所有微服务在捕获到异常时发布 service.error 事件,监控中心订阅事件后聚合不同服务的错误率,结合时间窗口自动触发告警。新增服务时,只需接入总线并发布事件,告警系统无需任何改动,AI 告警模型也能基于完整事件流做异常趋势预测。

Python 演示

python53 行
import timeimport uuid  class EventBus:    # 极简事件总线: 支持订阅, 发布, 退订    def __init__(self):        self.handler_map = {}     def subscribe(self, event_type, handler):        if event_type not in self.handler_map:            self.handler_map[event_type] = []        self.handler_map[event_type].append(handler)        return handler     def unsubscribe(self, event_type, handler):        if event_type in self.handler_map:            self.handler_map[event_type].remove(handler)            return True        return False     def publish(self, event_type, payload=None):        event = {            "id": str(uuid.uuid4()),            "type": event_type,            "payload": payload or {},            "timestamp": time.time(),        }        for handler in self.handler_map.get(event_type, []):            handler(event)        return event  def main():    bus = EventBus()     def order_created_notify(event):        order_id = event["payload"]["order_id"]        print("[notify] send sms for order", order_id)     def order_created_stock(event):        sku = event["payload"]["sku"]        print("[stock] reduce stock for sku", sku)     bus.subscribe("order.created", order_created_notify)    bus.subscribe("order.created", order_created_stock)     bus.publish("order.created", {"order_id": "20250101001", "sku": "SKU-A100"})    print("publish done")  if __name__ == "__main__":    main()
目的:这段代码实现了一个极简内存版事件总线,演示了事件总线的注册订阅与广播分发核心逻辑,正好对应正文中“订阅管理”和“一对多广播”两个知识点。
  1. self.handler_map = {}用字典保存“事件类型到订阅者列表”的映射,相当于总线维护的订阅关系表。
  2. self.handler_map.setdefault(...)` 的替代写法 `if event_type not in self.handler_map首次注册某事件类型时初始化订阅者列表,保证重复订阅不覆盖旧数据。
  3. event = {"id": ..., "type": ..., "payload": ..., "timestamp": ...}为每个事件生成带唯一 ID 和时标的统一结构,ID 是幂等去重的基础。
  4. for handler in self.handler_map.get(event_type, [])发布时遍历所有订阅者的处理函数,逐个同步分发事件。
  5. bus.subscribe("order.created", order_created_notify)业务侧注册订阅,通知服务声明自己关心“订单已创建”事件。
  6. bus.publish("order.created", {...})业务侧发布事件,发布者完全不知道也不关心有哪些订阅者。
  7. if __name__ == "__main__":程序入口,保证模块被导入时不会误执行演示逻辑。

常见误区

误区很多初学者认为“事件总线就是 Kafka 或 RabbitMQ”,觉得用了 MQ 就等于做了事件总线架构,于是只关心怎么配置 Topic,从不设计事件类型和订阅关系。

正解事件总线是架构模式,MQ 是实现工具。就像“邮局”是一个机构定位,而“分拣车间”是内部设备,两者不在同一个层面。正确的做法是先设计事件类型、订阅关系、路由规则,再选择 MQ 支撑这些设计。如果只上 MQ 不设计总线,最终还是会写成点对点消息透传。

为何常见:因为绝大多数事件总线确实建立在 MQ 之上,看起来“用了 MQ 就有了总线”,于是忽略了架构设计才是真正的核心。

误区有人觉得“用了事件总线以后,服务之间就没有任何关系了”,于是随便定义事件结构,后续改字段也不做版本管理,结果下游服务连环报错。

正解事件本身也是一种“接口契约”。发布者和订阅者虽然在运行生命周期上解耦,但在事件数据结构上仍然存在依赖。事件格式变更会影响所有订阅方,所以工程上要引入事件版本号(如 order.created.v1)和 Schema 校验,让契约演进可控。

为何常见:因为“解耦”这个词给人彻底松绑的错觉,让人忽略了数据契约也是一种耦合,只是从运行时耦合转移到了设计期耦合。

误区不少初学者为了“消息绝不丢失”,把消费重试设置为无限次,结果下游服务故障时,消息在总线和消费者之间反复横跳,堆积成山,系统雪上加霜。

正解可靠性不意味着“永不失败”,而是“可控失败”。正确的做法是设置合理重试次数,配合指数退避降低压力,超过上限后转入死信队列隔离,等待人工介入。死信队列不是失败的表现,而是可靠性设计的正常组成部分。

为何常见:因为刚接触可靠性概念时,容易把“不丢消息”理解成“任何情况下都不能放弃”,忽略了无限重试本身会制造更大的故障。

误区很多需求方在评审时提出“消息既不能重复也不能丢失,必须恰好一次”。初学者连连点头,最后发现分布式环境下实现“恰好一次”的成本极高,大多数系统根本不需要。

正解分布式系统的现实是:分区、超时、网络抖动都会导致重复或丢失。工程上更务实的选择是“至少一次 + 幂等消费”:用“至少一次”保证不丢,用幂等逻辑消化重复。只有极少数强一致场景(如金融支付)才需要“恰好一次”,而且通常也要借助外部存储配合实现。

为何常见:因为工程师习惯了单机数据库事务的 ACID 直觉,把“要么成功要么回滚”的模型直接带到分布式消息领域,没意识到网络不可靠带来的根本约束。

面试题

Q1. 事件总线架构主要解决微服务之间的什么问题?

单选题 · easy

  • A.数据库读写竞争
  • B.服务间点对点强耦合← 正确答案
  • C.前端页面加载缓慢
  • D.代码部署频率过低

答案:B。事件总线的核心价值是解耦,让服务通信从点对点调用变成“发布-订阅”模式。A 属于存储层性能问题,C 属于前端渲染问题,D 属于 DevOps 问题,都跟服务间通信耦合没有直接关系。

Q2. 关于事件总线与消息队列的关系,正确说法是?

单选题 · mid

  • A.总线等于消息队列
  • B.总线是一种架构模式← 正确答案
  • C.消息队列无法替代总线
  • D.总线只能基于 MQ 存在

答案:B。事件总线是架构层面的设计模式,MQ 是实现它的常用工具。A 混淆了层级,C 太绝对,MQ 可以在一定程度上承担总线的职责,D 错误,因为总线也可以用 Redis Stream、内存消息等实现,并非只能依赖 MQ。

Q3. 在发布订阅模式中,发布者不需要知道以下哪项?

单选题 · easy

  • A.消息格式约定
  • B.消息类型名称
  • C.订阅者的位置← 正确答案
  • D.消息具体内容

答案:C。发布者必须知道事件格式和事件类型,不然写不出正确的消息;也必然知道自己要发布的内容。它唯一不需要知道的是订阅者在哪里、有几个。这正是发布订阅模式与传统点对点调用的关键区别。

Q4. 事件总线根据事件内容字段(如订单金额、用户等级)决定分发目标,这属于哪种路由?

单选题 · mid

  • A.点对点路由
  • B.广播路由
  • C.内容路由← 正确答案
  • D.静态路由

答案:C。内容路由是总线通过解析事件内容里的业务字段来决定目标订阅者,比如金额大于一万才发给风控服务。广播路由是发给所有订阅者,点对点路由是按固定地址投递,静态路由不涉及内容判断。

Q5. 要求消息不丢失但允许重复投递,这种投递语义称为?

单选题 · mid

  • A.最多一次
  • B.至少一次← 正确答案
  • C.恰好一次
  • D.零次投递

答案:B。至少一次语义保证每条消息至少被送达一次,允许重复,但不允许丢失。最多一次允许丢失但不重复,恰好一次要求不重不丢,实现成本最高。多数事件总线默认采用至少一次,配套幂等消费解决重复问题。

Q6. 指数退避重试策略中,第 n 次重试的等待时间约为?

单选题 · hard

  • A.等待时间指数增长← 正确答案
  • B.等待时间线性增长
  • C.等待时间固定不变
  • D.等待时间随机浮动

答案:A。指数退避的公式是 t₀×2ⁿ,等待时间随重试次数指数增长。这样做的目的是给故障系统留出恢复窗口,避免重试风暴。线性增长太激进,固定不变容易连续打爆下游,随机浮动缺少可预测性,都不是标准方案。

Q7. 服务 A 需要通知服务 B 和 C 而不用知晓它们的地址,最合适的通信方式是?

单选题 · easy

  • A.共享内存缓存
  • B.事件总线分发← 正确答案
  • C.定时任务轮询
  • D.人工邮件通知

答案:B。事件总线天然支持一对多广播,发布者无需知道订阅者的地址。共享缓存需要服务主动去读,定时轮询存在时效性差和资源浪费,人工邮件不是技术方案。这正是事件总线在服务解耦场景中的典型应用。

Q8. 以下哪些措施能提升事件总线可靠性?

多选题 · mid

  • A.事件持久化存储← 正确答案
  • B.失败消费重试← 正确答案
  • C.设置死信队列← 正确答案
  • D.完全禁用日志

答案:ABC。持久化防止重启丢消息,重试解决临时性故障,死信队列隔离最终失败的消息,这三者共同构成可靠性保障。禁用日志是错误的做法,日志是问题排查的重要手段,与可靠性正相关。

Q9. 消费端实现幂等去重的常用方法包括?

多选题 · mid

  • A.用唯一 ID 去重← 正确答案
  • B.数据库唯一约束← 正确答案
  • C.先查询再更新
  • D.不做任何处理

答案:AB。事件携带唯一 ID,消费者查重后可跳过重复消息;数据库唯一约束则让重复插入直接失败,从存储层保证幂等。先查询再更新存在竞态窗口,不能可靠防重;不做任何处理必然导致重复业务操作。

Q10. 事件总线相对点对点调用的优势包括?

多选题 · extreme

  • A.解耦服务生命周期← 正确答案
  • B.减少通信总链路数← 正确答案
  • C.天然保证事件有序
  • D.支持一到多广播← 正确答案

答案:ABD。总线让发布者和订阅者生命周期互不依赖,也把 N×M 条链路收敛为 N+M,还天然支持一对多广播。但“天然保证有序”是错误选项,消息顺序在分布式事件总线中需要额外机制保障,例如分区键和序列号设计,并非总线自带属性。