🎓 总站 🏠 本课目录 01 概论 02 虚拟化 03 云原生 04 K8s基础 05 K8s进阶 06 消息队列 07 分布式存储 08 分布式文件系统 09 并行编程 10 Spark
云计算技术 · 第6讲

消息队列与事件驱动架构

从 MQ 解耦/异步/削峰出发,串讲消息模型、投递语义、Kafka、RabbitMQ、事件驱动架构(EDA)与 Redis Stream。

📚 学习进度
0%

🎯学习目标

  • 理解消息队列(MQ)的概念与三大核心作用:异步解耦、流量削峰、最终一致性;
  • 区分点对点(Queue)与发布/订阅(Topic)两种消息模型;
  • 掌握三种消息投递语义(At Most/Least/Exactly Once)与消息顺序性保证;
  • 理解 Kafka 架构(Broker/Topic/Partition/Offset/Consumer Group)与 RabbitMQ 核心概念及 Exchange 类型;
  • 理解事件驱动架构(EDA)、事件溯源与 CQRS,掌握 Redis Pub/Sub 与 Stream 的差异。

1消息队列基础

消息队列(Message Queue, MQ)是一种应用间通过消息进行异步通信的中间件。分布式系统中服务之间经常需要交换数据、异步处理任务或应对流量波动,MQ 能有效提升系统的解耦性、可扩展性与可维护性。

💡 一句话理解消息队列就是服务之间的"缓冲水库"——发送方把消息丢进去就走,接收方按自己的节奏取出来处理,双方互不等待。

MQ 三大核心作用

🔗

异步解耦

发送方无需等待接收方处理,降低系统耦合。

⛰️

流量削峰

缓冲突发流量,保护下游服务不被压垮。

⚖️

最终一致性

通过可靠消息传递,达成系统最终一致的状态。

同步直连 vs 经 MQ 解耦 生产者 消费者 紧耦合·同步等待 生产者 消息队列缓冲·异步 消费者
图1 · 引入 MQ 后,生产者与消费者通过队列解耦,发送即返回

2消息模型:点对点 vs 发布/订阅

消息队列有两种基本模型,理解它们是后续 Kafka/RabbitMQ 的基础:

📨

点对点 Queue

Point-to-Point
每条消息只被一个消费者消费,消息被取走即删除。FIFO 队列,不支持并行消费
📡

发布/订阅 Topic

Publish-Subscribe
一条消息可被多个订阅者同时接收Kafka 分区支持并行消费
特性队列(Queue)分区(Partition / Topic)
消息顺序严格按发送顺序消费只保证单分区内顺序,跨分区不保证
适用模型点对点(Queue)发布/订阅(Topic)
消费者每条消息只被一个消费者消费每个分区有独立消费者,多消费者并行消费不同分区
并行性不支持并行消费支持并行消费
Publisher Topic 订阅者 A 订阅者 B 订阅者 C
图2 · 发布/订阅模型:一条消息广播给所有订阅者

3消息投递语义与顺序性 ⭐(核心考点)

投递语义:在消息传递过程中,确保消息按预期方式到达消费者的可靠性保障。共三种:

📉

At Most Once

最多一次。可能丢失,绝不重复。高效简单。适合日志收集

🔁

At Least Once

至少一次。不丢失但可能重复,需消费者幂等。适合订单支付

🎯

Exactly Once

恰好一次。不丢不重,实现复杂、开销大。适合银行转账

语义适用场景优点缺点
At Most Once性能高、对丢失不敏感(日志、事件记录)高效、开销低可能丢消息,无法保证完整性
At Least Once数据完整性要求高、重复处理无害(订单、社交)不丢消息可能重复,需幂等处理
Exactly Once高价值交易、金融支付、库存完美保证、无重复实现复杂、性能开销大
⭐ 消息为什么会重复?(必考)① 生产者发送后 Broker 保存成功,但未成功返回 ACK,生产者重发;② 消费者消费后给 Broker 返回 ACK 失败,Broker 未更新偏移量,同条消息再次发送。解决靠消费者幂等性。

消息顺序性保证

  • 单分区保证:同一分区内消息有序(Kafka、Redis Stream);
  • 单队列保证:同一队列 FIFO 先入先出;
  • 多分区/多队列:无法保证全局顺序,只保证分区/队列内顺序。增加分区数可提升并行性,但牺牲全局顺序。

4Apache Kafka

Kafka 最初由 LinkedIn 开发,2010 年贡献给 Apache 基金会成为顶级开源项目,是一个分布式的发布/订阅消息系统。核心由 Broker、Topic、Partition、Consumer Group 组成。

组件说明关键特性
Broker集群中的服务器节点,负责数据存储、请求处理、副本管理水平扩展、高可用(副本机制)
Topic消息的逻辑标识,用于分类隔离数据支持多分区、可配置副本数
PartitionTopic 的物理分片,并行处理与扩展的基本单位分区内有序、分区间可并行
Consumer Group一组消费者实例,共同消费一个或多个 Topic组内负载均衡、组间互不影响

Partition 与 Offset

Topic 是逻辑概念,Partition 是最小存储单元,每个 Partition 都是一个单独的 log 文件,每条记录以追加(append)形式写入。Partition 中每条记录被分配唯一递增、不可变的序号——Offset(偏移量),由 Kafka 自动维护。

⭐ Kafka 顺序性一个 Partition 内部消息有序,一个 Topic 跨 Partition 是无序的。若强制要求 Topic 整体有序,只能让 Topic 只有一个 Partition(牺牲并行)。
Producer P0: 012 P1: 01 P2: 012 → Offset 递增 ConsumerGroup
图3 · Kafka:Topic 被分为多个 Partition,每条记录有递增 Offset,分区间可并行

Kafka 也常用于实时流处理,支持数据的过滤、转换、聚合,常结合 Flink / Spark Streaming 做进一步分析。生产者发布消息示例:

from kafka import KafkaProducer
import json
producer = KafkaProducer(
    bootstrap_servers=['localhost:9093'],
    value_serializer=lambda v: json.dumps(v).encode('utf-8'))
producer.send('test-topic', value={'key': 'value'}, partition=0)  # 指定分区
producer.flush(); producer.close()
from kafka import KafkaConsumer
consumer = KafkaConsumer('test-topic',
    bootstrap_servers=['localhost:9093'],
    group_id='test-consumer-group',     # 消费者组
    auto_offset_reset='earliest')        # 从最早消息开始消费
for msg in consumer:
    print(msg.value, 'from partition', msg.partition)

5RabbitMQ

RabbitMQ 是基于 AMQP(Advanced Message Queuing Protocol)协议实现的消息队列系统。AMQP 是广泛应用的开放标准协议,目标是为消息传递提供标准化方式,关键特性是可靠性、可扩展性、灵活性

核心概念说明
Exchange负责接收消息并将其路由到适当的队列
Queue存储消息,消费者从中获取消息处理
Binding将 Exchange 与 Queue 绑定的规则,定义路由路径
Routing Key消息的路由关键字,用于匹配 Exchange 与 Queue 的绑定

三种 Exchange 类型

生产者把消息发给 Exchange(而非直接发给 Queue),由 Exchange 按规则路由到一个或多个队列,便于解耦、扩展与灵活路由:

🎯

Direct

Routing Key 与 Binding Key 完全匹配。精确分类投递,如日志等级 info/warning/error。

📢

Fanout

忽略 Routing Key,广播到所有绑定队列。如系统公告、缓存失效广播。

🧩

Topic

模式匹配*匹配一个单词,#匹配零或多个。适合事件驱动。

绑定键含义
order.*匹配 order.created、order.paid
*.error匹配 db.error、app.error
order.#匹配所有 order 开头的主题

可靠性机制

  • ACK 消息确认:消费者处理完消息后发 ACK 告知成功,只有确认后消息才被标记为已处理,确保不丢失。
  • 消息持久化:持久化消息同时写入磁盘和内存;非持久化消息一般重启后丢失。
  • 死信队列 DLQ:无法被正常消费的消息(死信)转移到的专门队列。常见原因:消息 TTL 过期、队列达最大长度被丢弃、消息被拒绝。
  • 延迟消息:通过延迟交换机指定延迟时间,消息在指定时间后再被消费(如下单后限时未支付自动失效)。

6事件驱动架构(EDA)

事件驱动架构(Event-Driven Architecture, EDA)是一种基于异步处理的架构,通过事件来驱动系统行为,而非通过请求和响应。特点:松耦合、异步处理、高可扩展性。

特性事件驱动架构(EDA)请求/响应模式
通信方式通过事件传递,发布者与消费者异步解耦客户端发请求,服务器同步响应
系统耦合松耦合紧耦合,直接依赖
响应时间异步处理,响应时间不确定同步处理,响应时间明确
扩展性易扩展,轻松增加新事件与服务较差,需修改请求/响应逻辑
性能高并发场景下性能较好受限于同步方式

事件溯源与 CQRS

📖 概念事件溯源(Event Sourcing):存储所有事件(而非最终状态)来重建系统状态,事件作为状态变化的记录。CQRS(命令查询职责分离):将写入(Command,事件持久化)与读取(Query,从事件序列派生)分离,由不同模型处理,可独立优化与扩展。

7Redis Pub/Sub 与 Stream

Redis 是开源高性能键值对存储系统,常作数据库、缓存和轻量级消息中间件使用。核心思想是数据放内存(RAM)加快读写,优势是低延迟、高性能、高吞吐、轻量级。

Pub/Sub 模式

发布者发布消息,订阅者订阅消息。优点:松耦合、实时性、多对多通信;缺点:订阅者离线则消息丢失、不保证顺序、难以跟踪消息处理状态。

import redis
client = redis.StrictRedis(host='localhost', port=6379, db=0)
client.publish('chat_channel', 'hello')      # 发布
pubsub = client.pubsub(); pubsub.subscribe('chat_channel')  # 订阅
for message in pubsub.listen():
    if message['type'] == 'message':
        print('Received:', message['data'].decode())

Redis Stream

Redis Stream 是新特性,允许持久化存储消息,可对消息消费、确认和追踪,支持按时间排序确保有序消费。

特性StreamPub/Sub
持久化支持(RDB/AOF)不支持,丢失无法恢复
消息确认支持 ACK不支持
消费者组支持,可并行消费不支持
顺序性支持顺序消费不保证顺序
适用场景高吞吐、日志收集、事件驱动、消息队列实时推送、通知、广播

重点例题

例题1:支付系统与日志收集如何选择投递语义? 分析:日志收集——丢失少量日志不影响整体分析,对可靠性要求低,选 At Most Once(高效、开销低);② 订单支付(请求记录)——绝不能丢,但重复可由幂等处理,选 At Least Once;③ 银行转账/扣款——既不能丢也不能重复扣款,选 Exactly Once(配合幂等与事务)。
口诀:能容忍丢→AtMostOnce;不能丢能去重→AtLeastOnce;分文不差→ExactlyOnce。
例题2:Kafka 中如何保证一个 Topic 的消息全局有序? 思路:Kafka 仅保证单 Partition 内有序,跨 Partition 无序。要让整个 Topic 全局有序,必须把该 Topic 设为只有 1 个 Partition
代价:只有一个分区就只能由一个消费者串行消费,牺牲了并行性与吞吐。实践中通常按业务 Key(如同一用户)路由到同一分区,实现"局部有序"而非全局有序。
例题3:RabbitMQ 中"领域.动作"事件命名应选哪种 Exchange? 解答:Topic Exchange。事件天然适合 order.createdorder.paiduser.login 这种"领域.动作"命名,通过 order.#*.error 等模式匹配灵活订阅,特别适合微服务事件总线与事件驱动架构。

🎯自测(点击展开)

消息队列的三大核心作用是什么?
异步解耦、流量削峰、最终一致性。
点对点和发布/订阅模型的核心区别?
点对点每条消息只被一个消费者消费、不支持并行;发布/订阅一条消息可被多个订阅者接收,分区支持并行消费。
为什么 At Least Once 会产生重复消息?如何应对?
Broker 保存成功但未返回 ACK 导致生产者重发,或消费者 ACK 失败导致重发。应对方式是消费者实现幂等性。
Kafka 的 Offset 是什么?
Partition 中每条记录的唯一、递增、不可变序号,由 Kafka 自动维护,记录消费位置。
RabbitMQ 三种 Exchange 类型及路由方式?
Direct(完全匹配)、Fanout(广播到所有队列)、Topic(模式匹配,支持 * 和 # 通配符)。
Redis Stream 相比 Pub/Sub 的最大优势?
Stream 支持消息持久化、ACK 确认、消费者组与顺序消费;Pub/Sub 不持久化,订阅者离线即丢失消息。

📝强化题库

选择题点选即时判分;填空题输入后"检查"或"显示答案"。

已答 0/0答对 0正确率
已答 0/0答对 0