消息队列

1. 消息队列是什么?如何选型?

  • 定义的广义化: 消息队列本质上是具有 “缓冲作用”“发布/订阅能力” 的存储引擎。
  • 技术的发展由 需求底层架构 双重驱动:
    • 需求面(功能演进):消息 (异步/解耦) –> 流 (高吞吐/削峰) –> 消息和流融合 (一站式解决)
    • 架构面(技术演进):单机 –> 分布式 (分区/副本) –> 云原生/Serverless (计算存储分离/按量付费)
  • 选型标准: 取决于 数据量业务复杂度
    • 非标准方案: 如果数据结构简单、流量不大,用 Redis (List/PubSub)MySQL 模拟消息队列更省运维成本。
    • 标准方案: 只有在面临 大消息、高并发、持久化堆积 等复杂需求时,才必须引入专门的消息队列产品。

追求大数据下的绝对稳定,选 Kafka;如果你是国内微服务业务架构,选 RocketMQ;如果你想尝试最新的云原生架构并解决扩容痛点,可以关注 Pulsar。

2. 消息队列的基本特性与基本概念

基本特性

基本特性:高性能、高吞吐、低延时

常见的使用场景:

  • 一份数据需要被多个下游系统处理,单流程是一个典型的 系统解耦消息分发 的场景。
  • 日志采集流程,一般日志数据都很大,直接发到下游,下游系统可能会扛不住崩溃,所以会把数据先缓存到消息队列中。

架构层面的基本概念

  1. Broker Broker 本质上是一个进程,比如 RocketMQ 的 Broker 就是指RocketMQ Server 启动成功后的一个进程。在实际部署过程中,通常一个物理节点只会起一个进程,所以大部分情况下我们认为 Broker 就表示一个节点,但是在一些特殊场景下,一个物理节点中也可以起多个进程,就表示一台节点有多个Broker。

  2. Topic(主题) 在大部分消息队列中,Topic 都是指用来组织分区关系的一个逻辑概念。通常情况下,一个 Topic 会包含多个分区。但是 RabbitMQ 是一个例外,Topic 是指具体某一种主题模式。

  3. Partition/Queue/MessageQueue(分区/分片): 在消息队列中,分区、分片、Partiton、Queue、MessageQueue 是一个概念,后面统一用分区来称呼,都是用来表示数据存储的最小单位。一般可以直接将消息写入到一个分区中,也可以将消息写入到Topic,再分发到具体某个分区。一个Topic 通常会包含一个或多个分区。

  4. Producer(生产者): 生产者指消息的发送方,即发送消息的客户端,也叫生产端。

  5. Consumer(消费者):消费者指消息的接收方,即接收消息的客户端,也叫消费端。

  6. ConsumerGroup/Subscription(消费分组/订阅):一般情况下,消息队列中消费分组和订阅是同一个概念,后面统一用消费分组来称呼。它是用来组织消费者和分区关系的逻辑概念,也有保存消费进度的作用。

  7. Message(消息):指一条真实的业务数据,消息队列的每条数据一般都叫做一条消息。

  8. Offset/ConsumerOffset/Cursor(位点/消费位点/游标): 指消费者消费分区的进度,即每个消费者都会去消费分区,为了避免重复消费进度,都会保存消费者消费分区的进度信息。

  9. ACK/OffsetCommit(确认/位点提交):确认和位点提交一般都是指提交消费进度的操作,即数据消费成功后,提交当前的消费位点,确保不重复消费。

  10. Leader/Follower(领导者/追随者,主副本/从副本):Leader 和 Follower一般是分区维度副本的概念,即集群中的分区一般会有多个副本。此时就会有主从副本的概念,一般是一个主副本配上一个或多个从副本。

  11. Segment(段/数据分段):段是指消息数据在底层具体存储时,分为多个文件存储时的文件,这个文件就叫做分区的数据段。即比如每超过 1G 的文件就新起一个文件来存储,这个文件就是Segment。基本所有的消息队列都有段的概念,比如Kakfa的Segment、Pulsar的Ledger等等。

  12. StartOffset/EndOffset(起始位点/结束位点):起始位点和结束位点是分区维度的概念。即数据是顺序写入到分区的,一般从0的位置开始往后写,此时起始位点就是0。因为数据有过期的概念,分区维度较早的数据会被清理。此时起始位点就会往后移,表示当前阶段最早那条有效消息的位点。结束位点是指最新的那条数据的写入位置。因为数据一直在写入分区,所以起始位点和结束位点是一直动态变化的。

  13. ACL(访问控制技术):ACL 全称是Access Control List,用来对集群中的资源进行权限控制,比如控制分区或Topic的读和写等。

功能层面的基本概念

相比于数据库的基本操作是增删改查,消息队列的基本操作就是生产和消费,即读和写。消息队列一般是不支持客户端修改和删除单条数据的。接下来我们就从功能的角度,来了解一些常见的基本概念。

  1. 顺序消息: 是指从生产者和消费者的视角来看,生产者按顺序写入Topic的消息,在消费者这边能不能按生产者写入的顺序消费到消息,如果能就是顺序消息。

  2. 延时消息/定时消息:都是指生产者发送消息到 Broker 时,可以设置这条消息在多久后能被消费到,当时间到了后,消息就会被消费到。延时的意思就是指以 Broker 收到消息的时间为准,多久后消息能被消费者消费,比如消息发送成功后的30分钟才能被消费。定时是指可以指定消息在设置的时间才能被看到,比如设置明天的20:00才能被消费。从技术上来看,两者是一样的;从客户端的角度,功能上稍微有细微的差别;从内核的角度,一般两种消息是以同一个概念出现的。

  3. 事务消息:消息队列的事务因为在不同的消息队列中的实现方式不一样,所以定义也不太一样。正常情况下,事务表示多个操作的原子性,即一批操作要么一起成功,要么一起失败。在消息队列中,一般指发送一批消息,要么同时成功,要么同时失败。

  4. 消息重试:消息重试分为生产者重试和消费者重试。生产者重试是指当消息发送失败后,可以设置重试逻辑,比如重试几次、多久后重试、重试间隔多少。消费者重试是指当消费的消息处理失败后,会自动重试消费消息。

  5. 消息回溯:是指当允许消息被多次消费,即某条消息消费成功后,这条消息不会被删除,还能再重复到这条消息。

  6. 广播消费:广播听起来是一个主动的,即 Broker 将一条消息广播发送给多个消费者。但是在消息队列中,广播本质上是指一条消息能不能被很多个消费者消费到。只要能被多个消费者消费到,就能起到广播消费的效果,就可以叫做广播消费。

  7. 死信队列:死信队列是一个功能,不是一个像分区一样的实体概念。它是指当某条消息无法处理成功时,则把这条消息写入到死信队列,将这条消息保存起来,从而可以处理后续的消息的功能。大部分情况下,死信队列在消费端使用得比较多,即消费到的消息无法处理成功,则将数据先保存到死信队列,然后可以继续处理其他消息。当然,在生产的时候也会有死信队列的概念,即某条消息无法写入Topic,则可以先写入到死信队列。从功能上来看,死信队列的功能业务也可以自己去实现。消息队列中死信队列的意思是,消息队列的SDK已经集成了这部分功能,从而让业务使用起来就很简单。

  8. 优先级队列:优先级队列是指可以给在一个分区或队列中的消息设置权重,权重大的消息能够被优先消费到。大部分情况下,消息队列的消息处理是FIFO先进先出的规则。此时如果某些消息需要被优先处理,基于这个规则就无法实现。所以就有了优先级队列的概念,优先级是消息维度设置的。

  9. 消息过滤:是指可以给每条消息打上标签,在消费的时候可以根据标签信息去消费消息。可以理解为一个简单的查询消息的功能,即通过标签去查询过滤消息。消息过滤主要在消费端生效。

  10. 消息过期/删除(TTL):是指消息队列中的消息会在一定时间或者超过一定大小后会被删除。因为消息队列主要是缓冲作用,所以一般会要求消息在一定的策略后会自动被清理。

  11. 消息轨迹:是指记录一条消息从生产端发送、服务端保存、消费端消费的全生命周期的流程信息。用来追溯消息什么时候被发送、是否发送成功、什么时候发送成功、服务端是否保存成功、什么时候保存成功、被哪些消费者消费、是否消费成功、什么时候被消费等等信息。

  12. 消息查询:是指能够根据某些信息查询到消息队列中的信息。比如根据消息ID或根据消费位点来查询消息,可以理解为数据库里面的固定条件的select操作。

  13. 消息压缩:是指生产端发送消息的时候,是否支持将消息进行压缩,以节省物理资源(比如网卡、硬盘)。压缩可以在SDK完成,也可以在Broker完成,并没有严格限制。通常来看,压缩在客户端完成会比较合理。

  14. 多租户:是指同一个集群是否有逻辑隔离,比如一个物理集群能否创建两个名称都为test的主题。此时一般会有一个逻辑概念 Namespace(命名空间)和 Tenant(租户)来做隔离,一般有这两个概念的就是支持多租户。

  15. 消息持久化:是指消息发送到Broker后,会不会持久化存储,比如存储到硬盘。有些消息队列为了保证性能,只会把消息存储在内存,此时节点重启后数据就会丢失。

  16. 消息流控:是指能否对写入集群的消息进行限制。一般会支持Topic、分区、消费分组、集群等维度的限流。

消息队列的核心模块

需要知道的一共是5块:通信协议、网络、存储、生产端、消费端。

pulsar基本理解

在 Pulsar 中,一个完整的消费关系通常表达为:**订阅名称-persistent://租户/命名空间/主题**。

  1. 订阅名称 (Subscription Name)- 之前的部分,代表“谁”在消费。
  2. 协议与租户/命名空间persistent://xdr-tenant01/asset/
    • persistent:数据持久化存储。
    • xdr-tenant01:租户名(通常对应一个项目或部门)。
    • asset:命名空间(通常对应一个子系统)。
  3. **主题名称 (Topic Name)**:event_change,代表消息的内容分类。

可以指向同一个订阅:

  • 一份数据,多种用途:底层的资产变更数据只有一份(event_change),但下游有四个不同的功能模块需要它。
  • 进度独立:每个订阅名称都有自己独立的 Cursor(进度指针)。即使“漏洞关联”处理得慢,也不会影响“定时告警”的消费进度。
  • 互不干扰:删除其中一个订阅(比如不再需要 Cron 任务),不会影响其他三个订阅的运行。