消息队列概要讲解(上)

消息队列概要讲解(上)

大家好,这里是编程Cookbook。本文概要介绍消息队列的核心原理和实现,以及常见问题及其解决方案等。本文不会过多的扩展详细的消息队列系统,如RocketMQ、RabbitMQ、Kafka等,这些会在后续系列文章中详细介绍。

消息队列(Message Queue,MQ) 是在微服务系统和分布式架构中实现异步通信的技术。是分布式系统中重要的组件,主要解决应用耦合异步处理流量削锋等问题,实现系统的高性能,高可用,可伸缩,使用较多的消息队列有RocketMQ、RabbitMQ、Kafka等。

为什么需要消息队列?

随着互联网的快速发展,技术架构从单体架构向微服务和分布式架构转变,服务间相互调用和依赖增多。需要一个工具来解耦服务之间的关系合理控制资源的使用以及缓冲流量洪峰等,消息队列应运而生。

主要功能包括:

  • 异步处理:例如电商订单系统中,下单后订单处理、库存扣减、支付处理等环节可异步进行,提高系统响应速度。
  • 应用解耦:让不同服务专注自身业务,通过消息队列交换信息,如营销系统和支付系统分开。
  • 流量削锋:避免流量过大冲垮系统,例如电商大促期间,通过消息队列缓存订单请求,避免系统崩溃。

典型应用场景包括:

  • 订单系统:电商订单创建、支付、发货等步骤通过消息队列异步处理。
  • 日志处理:将应用系统日志通过消息队列传输到日志处理系统,实现实时分析和监控。
  • 任务调度:在批量任务处理和任务调度系统中,将任务通过消息队列分发给多个工作节点并行处理。
  • 数据同步:在数据同步系统中,利用消息队列将变更数据异步同步到其他存储系统或服务。

下面将详细介绍相关知识的细节

消息队列的核心概念

基础概念

  • 消息(Message):消息是通信的基本单位,通常包含数据和元数据(如消息ID、时间戳等)。
  • 生产者(Producer):负责创建和发送消息的应用程序或服务。
  • 消费者(Consumer):负责接收和处理消息的应用程序或服务。
  • 队列(Queue)消息的存储区域,生产者将消息发送到队列,消费者从队列中获取消息,通常按照 先进先出(FIFO) 的原则处理消息。
  • 中间件(Broker):消息队列的核心组件负责消息的接收、存储和分发,在生产者和消费者之间起到桥梁作用
  • 主题(Topic):用于在发布 / 订阅模型中,消息生产者将消息发布到一个主题,多个订阅该主题的消费者可以接收到相同的消息。

其他概念

  • 消息确认(Ack)消费者处理完消息后,向消息队列系统发送的确认信号(Acknowledgment)。如果消息队列未收到确认,消息会被重新投递给消费者,保证消息不会丢失。

    这种机制确保了消息处理的可靠性,在金融交易系统中尤为重要。例如,当转账消息被银行后台系统处理后,后台系统会向消息队列发送确认,若未收到确认,消息队列会重新发送转账消息,防止转账操作遗漏。

  • 死信队列(DLQ,Dead Letter Queue):死信队列用于记录这些未能成功消费的消息,以便后续分析或人工处理。

    当消息因为消费失败、多次重试后未成功、消息过期或队列达到最大长度等原因被丢弃时,消息可以被转移到死信队列。在数据处理系统中,如果由于数据格式不规范导致消费者无法处理消息,经过多次重试后,这些消息会进入死信队列,供技术人员排查问题。

  • 命名服务(NameServer):在分布式消息队列环境中,存在多个 Broker(消息中间件)NameServer提供了服务发现负载均衡的功能,生产者和消费者通过查询 NameServer 来发现可用的 Broker。

    RocketMQ中的NameServer

    • RocketMQ自主研发的NameServer充当命名服务,以轻量级的特性高效维护Broker的路由信息,助力消息顺畅流转。
    • 它无状态且不存储持久化数据,各NameServer彼此独立,Broker定时上报状态,生产者和消费者借此获取信息实现消息收发。

    Kafka中的命名服务(ZooKeeper)

    • Kafka依托ZooKeeper作为命名服务,ZooKeeper不仅实现服务发现与负载均衡,还深度参与集群和配置管理。
    • ZooKeeper存储Kafka集群关键数据,集群状态变化时能及时更新并通知生产者和消费者,保障消息生产消费流程。

    RabbitMQ(无命名服务)

    • RabbitMQ虽无专门命名服务,但凭借自身集群机制和配置,有效管理Broker关系和消息路由。
    • 集群节点相互通信共享元数据,生产者和消费者连接节点收发消息,节点自动协调路由处理。
  • 集群(Cluster):为了提高消息队列的可靠性和处理能力,将多个Broker组成一个集群。集群架构可以在一个Broker发生故障时,保证消息服务的高可用性

    例如在电商大促期间,消息队列集群可以承受海量订单消息的处理压力,即使个别Broker出现故障,也不会影响整个系统的消息处理流程,确保订单处理、物流通知等业务正常进行。

  • 分区与队列:为了提高并发度,往往发布/订阅模型还会引入队列或者分区的概念,即消息是发往一个主题(Topic)下的某个队列「RocketMQ中叫队列(MessageQueue)」或者某个分区中「Kafka叫分区(Partition)」

    这里的队列要区别于队列模型中的队列,RocketMQ中的队列更多是逻辑概念,用于Topic下的消息存储与消费。一个Topic可以包含多个MessageQueue,这些队列类似于Kafka的分区,用于并发消费。在大数据处理场景中,例如对海量日志数据进行分析,通过将日志消息划分到不同分区或队列,可以实现多个消费者并行处理,提高处理效率。

  • 偏移量(Offset):Offset可以认为是每条消息在分区(队列)中的唯一编号,消费者会记录自己的消费点位,以便在恢复时继续消费未处理的消息,避免消息漏消费或重复消费。

    Kafka和RocketMQ有Offset,RabbitMQ则没有Offset,它主要通过消息确认机制等方式来确保消息被正确处理,消费者处理完消息后向 Broker 发送确认。
    例如在Kafka的日志收集系统中,消费者记录所消费消息的偏移量,当消费者重启后,可以根据偏移量从上次停止的位置继续处理日志消息,保证数据处理的完整性和连贯性。

  • 消费组(Consumer Group):消息队列中用于协调消费者并行消费消息的核心。

    1. 在Kafka中同一消费组内的消费者共享同一个Topic下的分区,一个分区只会被组内的一个消费者消费。

    2. 在RocketMQ中同一消费组内的消费者共享同一个Topic下的队列,一个队列只会被组内的一个消费者消费。

    3. RabbitMQ没有消费组的概念(当然他也没有分区和队列概念),它通过其他方式来实现消费者之间的协作和负载均衡,如多个消费者可以从同一个队列中获取消息进行处理,但没有像Kafka和RocketMQ那样以消费组为单位进行统一协调和管理。

消息队列的工作原理

消息队列的工作流程可以分为以下几个步骤:

(1)生产者发送消息

  • 生产者将消息发送到消息队列中。
  • 消息通常包含两部分:
    • 消息体(Body):实际的数据内容。
    • 元数据(Metadata):如消息ID、时间戳、优先级等。
  • 消息队列接收到消息后,将其存储在队列中,等待消费者处理。

(2)消息存储

  • 消息队列将消息持久化到磁盘或内存中,确保消息不会丢失。
  • 持久化方式可以是:
    • 内存存储:速度快,但消息可能会丢失。
    • 磁盘存储:速度较慢,但消息更可靠。

(3)消费者接收消息

  • 消费者从消息队列中获取消息。
  • 消息队列根据一定的策略(如轮询、优先级等)将消息分发给消费者
  • 消费者处理消息后,可以向消息队列发送确认(ACK),表示消息已成功处理。

(4)消息确认(ACK)

  • 消费者处理完消息后,会向消息队列发送确认信号(ACK)。
  • 如果消息队列未收到 ACK,则认为消息处理失败,可能会将消息重新放回队列,等待重试。

(5)消息重试和死信队列

  • 如果消费者处理消息失败,消息队列可以将消息重新放回队列,等待重试。
  • 如果消息重试多次仍失败,消息队列可能会将其转移到死信队列(Dead Letter Queue,DLQ),供后续处理。

消息队列的主要功能及其应用场景

消息队列(Message Queue,MQ)是分布式系统中用于实现异步通信的重要组件。它通过异步处理、应用解耦和流量削锋等功能,帮助系统实现高效、可靠且可扩展的通信服务。以下是消息队列的主要功能及其应用场景的详细说明:

应用解耦

  • 功能描述
    将消息的发送者(生产者)和接收者(消费者)解耦,双方不需要直接通信,只需通过消息队列交互,避免调用接口失败导致整个过程失败;

mq-1-1.png

  • 优势
    • 系统的各个部分可以独立开发、部署和扩展,降低耦合度,便于维护和提高服务整体性能。
    • 当某个服务发生故障时,不会直接影响其他服务。

异步通信

  • 功能描述
    生产者发送消息后无需等待消费者处理,可以立即返回并继续执行其他任务。消费者在合适的时间从队列中获取消息并处理。多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间;不需要立即处理请求的场景下,可以将请求放入消息系统。

mq-1-2.png

  • 优势
    • 提高系统的响应速度和吞吐量
    • 适用于耗时操作(如发送邮件、生成报表等),避免阻塞主流程,提高系统的响应速度。

流量削峰

  • 功能描述
    在高并发场景下,消息队列可以缓存大量请求,广泛应用于秒杀或抢购活动中,避免流量过大导致系统过载。

mq-1-3.png

  • 优势
    • 平滑系统负载,防止突发流量导致系统崩溃。
    • 适用于电商大促、秒杀活动等高并发场景。

上面是消息队列的主要使用场景,下面还有一些特点/功能,需要阐述一下:

可靠性

  • 功能描述
    消息队列通常支持消息的持久化存储和重试机制,确保消息不会丢失,并能被消费者正确处理。
  • 优势
    • 即使系统发生故障,消息也不会丢失。
    • 支持消息的确认机制(ACK),确保消息被成功处理。

扩展性

  • 功能描述
    消息队列支持分布式部署,可以通过灵活的增加消费者来提高消息的处理能力。
  • 优势
    • 系统可以根据需求动态扩展,适应业务增长
    • 适用于需要高吞吐量和低延迟的大规模系统。

消息顺序

  • 功能描述
    某些消息队列(如 RocketMQ)支持严格的消息顺序,确保消息按照发送顺序被消费。
  • 优势
    • 适用于需要保证顺序的场景,如订单处理、日志记录等。

消息过滤

  • 功能描述
    消息队列支持基于条件(如 Tag 或 SQL92 语法)的消息过滤,消费者可以只接收感兴趣的消息。
    • 优势
      • 减少不必要的消息传输,提高系统效率。

消息队列的常见通信模式

消息队列支持多种通信模式,以满足不同场景下的需求。以下是三种常见的通信模式及其详细介绍:


1. 队列模型(点对点模型)

工作原理

  • 生产者将消息发送到一个特定的队列中
  • 队列中的消息只能被一个消费者接收和处理
  • 一旦消息被消费者处理并确认(ACK),消息将从队列中移除。

特点

  • 一对一通信每条消息只能被一个消费者处理
  • 消息顺序:通常保证消息按照发送顺序被消费(取决于消息队列的实现)。
  • 可靠性:支持消息的持久化和重试机制,确保消息不会丢失。

应用场景

  • 任务分发:将任务分发给多个工作节点处理,例如订单处理、文件转换等。
  • 异步处理:将耗时操作(如发送邮件、生成报表)放入队列,由消费者异步处理。
  • 负载均衡:多个消费者可以同时从队列中拉取消息,实现负载均衡。

典型系统

  • RabbitMQ:原生支持队列模型,通过队列实现点对点通信。
  • ActiveMQ:支持队列模型,适用于传统的消息队列场景。
  • RocketMQ:支持队列模型,适用于高吞吐量的任务分发场景。

Kafka不支持队列模型(点对点) 。但是Kafka 通过消费者组实现队列模型。在同一个消费者组中,多个消费者可以共同消费一个主题(Topic)的分区(Partition),每条消息只会被组内的一个消费者消费。

示例

  • 订单系统中,订单消息被发送到队列,由库存服务或支付服务消费并处理。

2. 发布/订阅(Publish/Subscribe)

消息被多个订阅者引用时,中间件维护引用计数,直到所有订阅者确认消费后才删除

工作原理

  • 生产者将消息发送到一个主题(Topic),而不是特定的队列
  • 多个消费者可以订阅该主题,并接收相同的消息
  • 每个订阅者都会收到消息的副本,并独立处理。

特点

  • 一对多通信一条消息可以被多个消费者接收和处理
  • 消息广播:适用于需要将消息广播多个订阅者的场景。
  • 灵活性:消费者可以动态订阅或取消订阅主题。

应用场景

  • 日志收集:将应用程序的日志发送到主题,多个日志分析服务订阅并处理
  • 事件通知:在微服务架构中,服务之间通过发布/订阅模式传递事件。
  • 实时数据分发:例如股票行情、新闻推送等实时数据的分发。

典型系统

  • Kafka:以发布/订阅模型为核心,支持高吞吐量的消息广播和日志收集。
  • RocketMQ支持发布/订阅模型,适用于大规模分布式系统中的事件驱动架构。
  • RabbitMQ引入多队列交换机的绑定实现发布/订阅模式,同时将消息发给多个队列模拟出消息发布/订阅的效果,但本质上它还是基于队列模型的

示例

  • 电商系统中,订单创建事件被发布到主题,库存服务、物流服务和通知服务分别订阅并处理。

3. 路由(Routing)

工作原理

  • 生产者将消息发送到交换机(Exchange),并指定路由规则(Routing Key)「消息过滤规则一样」。
  • 交换机根据路由规则将消息分发到不同的队列
  • 消费者从队列中获取消息并处理。

特点

  • 灵活的消息分发:根据路由规则将消息分发到特定的队列。
  • 多种路由模式:支持直接路由(Direct)、主题路由(Topic)、头部路由(Headers)等模式。
  • 解耦生产者与消费者:生产者只需关注发送消息,消费者只需关注接收消息。

应用场景

  • 条件分发:根据消息的内容或属性将消息分发到不同的处理服务。
  • 多步骤流程:例如订单处理流程中,不同步骤的消息被路由到不同的队列。
  • 优先级处理:将高优先级的消息路由到特定的队列,优先处理。

典型系统

  • RabbitMQ:是路由功能强大的代表,提供了 多种交换机类型 以支持不同的路由模式,如 Direct Exchange、Topic Exchange、Headers Exchange 等,可根据不同的路由规则将消息精准分发到相应队列。
  • RocketMQ:在路由方面也有出色的表现。它的消息队列具有Topic和Queue的概念,生产者将消息发送到指定的Topic,而Topic可以通过标签(Tag)来进一步细分路由规则
  • Kafka:虽然Kafka主要以发布/订阅模型为核心,但也可以通过一些方式实现类似路由的功能。可以通过在主题(Topic)下设置不同的分区(Partition),并根据消息的某些属性(如键值)进行分区分配,从而实现消息的路由分发。

示例

  • 在物流系统中,根据订单的目的地将消息路由到不同的区域处理中心。

总结

通信模式 点对点(Point-to-Point) 发布/订阅(Publish/Subscribe) 路由(Routing)
通信方式 一对一 一对多 根据规则分发
典型场景 任务分发、订单处理 日志收集、事件通知 条件分发、多步骤流程
优势 简单、可靠 灵活、支持广播 灵活、支持复杂路由规则
示例 订单处理系统 电商事件通知 物流系统订单路由

消息队列的通信模式为不同的业务场景提供了灵活的解决方案。选择合适的通信模式可以更好地满足业务需求,提升系统的效率和可靠性。

补充:

  • 多对一本质是多个生产者共享一个消费者(P2P 的扩展)。
  • 多对多可通过发布/订阅或路由规则实现(如 Kafka 多 Topic + 多 Consumer Group)。