百日筑基第十八天-一头扎进消息队列1
先对业界消息队列有个宏观的认识
消息队列的现状
当前开源社区用的较多的消息队列主要有 RabbitMQ、RocketMQ、Kafka 和Pulsar 四款。
国内大厂也一直在自研消息队列,比如阿里的 RocketMQ、腾讯的 CMQ 和 TubeMQ、京东的 JMQ、字节的 BMQ。只是发展程度不一样,有的开源了成为顶级项目,有的慢慢消亡了,有的仅限在公司内部使用。
从消息队列的历史中抽象出两条交织的发展脉络:上层的需求变化和下层的架构演进:
- 从需求发展路径上看,消息队列的发展趋势是:消息 -> 流 -> 消息和流融合
- 从架构发展的角度来看,消息队列的发展趋势是:单机 -> 分布式 -> 云原生/Serverless
消息队列的演进史
- 在 90 年代到 21 世纪初,以 IBM MQ 和 AMQP 为代表的消息队列主要满足业务上对消息的需求,即异步通讯、架构解耦。(异步和解耦如今仍是MQ的主要能力)
- 2010 年左右,移动互联网发展,大数据兴起,传统的消息队列在架构上无法满足大流量的吞吐需求,就发展出了以 Kafka 为代表的消息队列,主打大吞吐、大流量。我们也进入了分布式的时代,现在大家熟知的消息队列都是分布式的架构,所以会有分区、副本、一致性概念。
- 随着业务场景越来越复杂,业务消息的数据量也越来越大。基于开源 AMQP 的 RabbitMQ 在性能和架构上已经无法满足消息场景的需求,从而发展出了 RocketMQ。
- 近几年随着云计算的发展、云原生和 Serverless 的理念兴起,在弹性、成本的驱动下,消息队列的架构往云原生 /Serverless 方向演变,简单来说,就是利用云上的弹性计算、存储等基础设施去实现架构的Serverless ,按需使用、按量付费,最终达到使用端感受到的免运维、低成本。
- 基于云原生架构设计的 Pulsar 开始走向成熟,业界的 MQ 也出现了计算存储分离、分层存储、多租户、弹性计算等概念。
消息队列的常见概念
架构层面的概念
- Broker 本质上是一个进程,比如 RocketMQ 的 Broker 就是指 RocketMQ Server 启动成功后的一个进程。在实际部署过程中,通常一个物理节点只会起一个进程, 所以大部分情况下我们认为 Broker 就表示一个节点,但是在一些特殊场景下,一个物理节 点中也可以起多个进程,就表示一台节点有多个 Broker。
- Topic(主题):在大部分消息队列中,Topic 都是指用来组织分区关系的一个逻辑概念。 通常情况下,一个 Topic 会包含多个分区。但是 RabbitMQ 是一个例外,Topic 是指具体 某一种主题模式。
- Partition/Queue/MessageQueue(分区 / 分片):在消息队列中,分区、分片、 Partiton、Queue、MessageQueue 是一个概念,后面统一用分区来称呼,都是用来表示 数据存储的最小单位。一般可以直接将消息写入到一个分区中,也可以将消息写入到 Topic,再分发到具体某个分区。一个 Topic 通常会包含一个或多个分区。
- Producer(生产者): 生产者指消息的发送方,即发送消息的客户端,也叫生产端。
- Consumer(消费者):消费者指消息的接收方,即接收消息的客户端,也叫消费端。
- ConsumerGroup/Subscription(消费分组 / 订阅):一般情况下,消息队列中消费分 组和订阅是同一个概念,后面统一用消费分组来称呼。它是用来组织消费者和分区关系的逻 辑概念,也有保存消费进度的作用。
- Message(消息):指一条真实的业务数据,消息队列的每条数据一般都叫做一条消息。
- Offset/ConsumerOffset/Cursor(位点 / 消费位点 / 游标):指消费者消费分区的进 度,即每个消费者都会去消费分区,为了避免重复消费进度,都会保存消费者消费分区的进度信息。
- ACK/OffsetCommit(确认 / 位点提交):确认和位点提交一般都是指提交消费进度的操 作,即数据消费成功后,提交当前的消费位点,确保不重复消费。
- Leader/Follower(领导者 / 追随者,主副本 / 从副本):Leader 和 Follower 一般是分 区维度副本的概念,即集群中的分区一般会有多个副本。此时就会有主从副本的概念,一般 是一个主副本配上一个或多个从副本。
- Segment(段 / 数据分段):段是指消息数据在底层具体存储时,分为多个文件存储时的 文件,这个文件就叫做分区的数据段。即比如每超过 1G 的文件就新起一个文件来存储,这 个文件就是 Segment。基本所有的消息队列都有段的概念,比如 Kakfa 的 Segment、 Pulsar 的 Ledger 等等。
- StartOffset/EndOffset(起始位点 / 结束位点):起始位点和结束位点是分区维度的概 念。即数据是顺序写入到分区的,一般从 0 的位置开始往后写,此时起始位点就是 0。因 为数据有过期的概念,分区维度较早的数据会被清理。此时起始位点就会往后移,表示当前 阶段最早那条有效消息的位点。结束位点是指最新的那条数据的写入位置。因为数据一直在 写入分区,所以起始位点和结束位点是一直动态变化的。
- ACL(访问控制技术):ACL 全称是 Access Control List,用来对集群中的资源进行权限 控制,比如控制分区或 Topic 的读和写等。
功能层面的概念
- 顺序消息:是指从生产者和消费者的视角来看,生产者按顺序写入 Topic 的消息,在消费 者这边能不能按生产者写入的顺序消费到消息,如果能就是顺序消息。
- 延时消息 / 定时消息:都是指生产者发送消息到 Broker 时,可以设置这条消息在多久后能 被消费到,当时间到了后,消息就会被消费到。延时的意思就是指以 Broker 收到消息的时 间为准,多久后消息能被消费者消费,比如消息发送成功后的 30 分钟才能被消费。定时是 指可以指定消息在设置的时间才能被看到,比如设置明天的 20:00 才能被消费。从技术上 来看,两者是一样的;从客户端的角度,功能上稍微有细微的差别;从内核的角度,一般两 种消息是以同一个概念出现的。
- 事务消息:消息队列的事务因为在不同的消息队列中的实现方式不一样,所以定义也不太一 样。正常情况下,事务表示多个操作的原子性,即一批操作要么一起成功,要么一起失败。 在消息队列中,一般指发送一批消息,要么同时成功,要么同时失败。
- 消息重试:消息重试分为生产者重试和消费者重试。生产者重试是指当消息发送失败后,可 以设置重试逻辑,比如重试几次、多久后重试、重试间隔多少。消费者重试是指当消费的消 息处理失败后,会自动重试消费消息。
- 消息回溯:是指当允许消息被多次消费,即某条消息消费成功后,这条消息不会被删除,还 能再重复到这条消息。
- 广播消费:广播听起来是一个主动的,即 Broker 将一条消息广播发送给多个消费者。但是 在消息队列中,广播本质上是指一条消息能不能被很多个消费者消费到。只要能被多个消费 者消费到,就能起到广播消费的效果,就可以叫做广播消费。
- 死信队列:死信队列是一个功能,不是一个像分区一样的实体概念。它是指当某条消息无法 处理成功时,则把这条消息写入到死信队列,将这条消息保存起来,从而可以处理后续的消 息的功能。大部分情况下,死信队列在消费端使用得比较多,即消费到的消息无法处理成 功,则将数据先保存到死信队列,然后可以继续处理其他消息。当然,在生产的时候也会有 死信队列的概念,即某条消息无法写入 Topic,则可以先写入到死信队列。从功能上来看, 死信队列的功能业务也可以自己去实现。消息队列中死信队列的意思是,消息队列的 SDK 已经集成了这部分功能,从而让业务使用起来就很简单。
- 优先级队列:优先级队列是指可以给在一个分区或队列中的消息设置权重,权重大的消息能 够被优先消费到。大部分情况下,消息队列的消息处理是 FIFO 先进先出的规则。此时如果 某些消息需要被优先处理,基于这个规则就无法实现。所以就有了优先级队列的概念,优先 级是消息维度设置的。
- 消息过滤:是指可以给每条消息打上标签,在消费的时候可以根据标签信息去消费消息。可 以理解为一个简单的查询消息的功能,即通过标签去查询过滤消息。消息过滤主要在消费端生效。
- 消息过期 / 删除(TTL):是指消息队列中的消息会在一定时间或者超过一定大小后会被删 除。因为消息队列主要是缓冲作用,所以一般会要求消息在一定的策略后会自动被清理。
- 消息轨迹:是指记录一条消息从生产端发送、服务端保存、消费端消费的全生命周期的流程 信息。用来追溯消息什么时候被发送、是否发送成功、什么时候发送成功、服务端是否保存 成功、什么时候保存成功、被哪些消费者消费、是否消费成功、什么时候被消费等等信息。
- 消息查询:是指能够根据某些信息查询到消息队列中的信息。比如根据消息 ID 或根据消费 位点来查询消息,可以理解为数据库里面的固定条件的 select 操作。
- 消息压缩:是指生产端发送消息的时候,是否支持将消息进行压缩,以节省物理资源(比如 网卡、硬盘)。压缩可以在 SDK 完成,也可以在 Broker 完成,并没有严格限制。通常来 看,压缩在客户端完成会比较合理。
- 多租户:是指同一个集群是否有逻辑隔离,比如一个物理集群能否创建两个名称都为 test 的主题。此时一般会有一个逻辑概念 Namespace(命名空间)和 Tenant(租户)来做隔 离,一般有这两个概念的就是支持多租户。
- 消息持久化:是指消息发送到 Broker 后,会不会持久化存储,比如存储到硬盘。有些消息 队列为了保证性能,只会把消息存储在内存,此时节点重启后数据就会丢失。
- 消息流控:是指能否对写入集群的消息进行限制。一般会支持 Topic、分区、消费分组、集 群等维度的限流。
对常见MQ再一次认识
RabbitMQ 和 RocektMQ 属于业务消息类的消息队列,它们的特点是功能丰富、低延时、 数据高可靠性、消息可追踪等等,同时也支持延时消息、优先级队列、消息过滤等功能特 性。RabbitMQ 发展较早,RocketMQ 则是新生的消息类的消息队列,从功能、集群化、 稳定性、性能来看,RocketMQ 都是比 RabbitMQ 表现要好的。所以从某种意义上说, RocketMQ 是可以替代 RabbitMQ 的,但是因为 RabbitMQ 发展悠久、内核稳定以及能 满足大部分的业务消息场景,所以目前用户群体也很大。国内的业务消息类的选型一般以RocketMQ 优先,然后才是 RabbitMQ,而国外的业务消息类选型一般优先的是 RabbitMQ。
Kakfa 属于主打流场景的消息队列。它的特点是追求高吞吐、大流量,在功能上相对简单。 不支持太多消息队列的功能,比如死信队列、延时消息、消息过滤等等。但它的核心竞争力 就是非常稳定、吞吐性能非常高,能承担超大流量的业务场景。所以它是流场景下的消息管 道的不二选择。
Pulsar 从定位上是消息和流一体的。目标就是满足所有消息和流的场景,希望同时满足功 能和性能两方面的需求。所以 Pulsar 的内核会支持很多功能,在性能和吞吐方面也经常拿 来与 Kakfa 做比较。但是因为其发展时间较短,目前还不是那么稳定,正处于快速发展阶段。