[02实践应用 - 消息队列] 13 - 消息队列原理与实战 | 青训营笔记

21 阅读6分钟

这是我参与「第五届青训营」伴学笔记创作活动的第13天。今天的内容是关于消息队列的。消息队列是一个保存消息的容器,需要支持高吞吐、高并发,维持高可用。

1 消息队列

背景:系统崩溃、服务能力有限、链路耗时长尾、日志存储

常见解决方案:解耦、削峰、异步、日志处理

消息队列 message queue (MQ):保存消息的容器,本质上是队列,但是需要支持高吞吐、高并发,并且高可用。

发展历程:TIB -> IBM MQ / WebSphere -> MSMQ -> JMS -> AMQP / RabbitMQ -> Kafka -> RocketMQ -> Pulsar

几个重要消息队列:

  • Kafka:分布式、分区、多副本的日志提交服务,在高吞吐场景下发挥出色
  • RocketMQ:阿里自研;低延迟、强一致、高性能、高可靠、万亿级容量和灵活的可扩展性,在一些实时场景中应用广泛。
  • Pulsar:下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算于一体,采用存算分离的架构设计;目前腾讯在用
  • BMQ:和 Pulsar 架构类似,存算分离;字节在用,初期定位是承接高吞吐的离线业务场景,逐步替换掉对应的 Kafka 集群

2 Kafka

使用场景:日志处理、Metrics 数据处理、用户行为分析

使用流程:创建集群、新增 topic、编写生产者逻辑、编写消费者逻辑

2.1 基本概念

  • Topic:逻辑队列
  • Cluster:物理集群,每个集群中可以建立多个不同的 Topic
  • Producer:生产者,负责将业务消息发送到 Topic 中
  • Consumer:消费者,负责消费 Topic 中的消息
  • Partition:通常一个 Topic 中会存在多个分片,不同分片之间的消息可以并发完成
  • Offset:消息在 partition 内的相对位置信息,在 partition 内严格递增
  • Replica:每个 partition 内有多个 replica,leader replica 会从 ISR (In-Sync Replicas) 中选出,follower 会尽量和 leader 保持一致

2.2 数据复制

有一个节点 broker 扮演了 controller 的角色。

2.3 kafka 架构

Zookeeper 和 Cluster 相互配合,负责存储一些集群的元数据信息,包括分区的分配信息等。

2.4 消息的流程

producer 生产,发送到 broker;broker 再发给 consumer 消费。

producer 批量发送:构造 batch 实现批量发送,从而减少 ID 次数,加强发送能力。发送时进行数据压缩,减少消息大小,支持 Snappy、LZ4、ZSTD 等。

broker 存储数据时,最终会以日志(log)的形式写入磁盘,每个 log 会拆分为 LogSegment,写入内容包括 .log 日志文件、.index 偏移量索引文件、.timeindex 时间戳索引文件和一些其它文件。每个 LogSegment 以第一条消息的 offset 命名。

broker 磁盘结构包括磁盘、磁头、磁道、扇区。先将磁头移动到对应的磁道上,旋转磁盘转动到对应的扇区,再进行读写。主要花费时间在寻道上。为了节省时间,采用顺序写入。

查找消息的流程:consumer 通过发送 FetchRequest 请求消息数据,broker 会将指定 offset 处的消息按照时间窗口和消息大小窗口发送给 consumer。

broker 的数据拷贝调用了 sendfile 方法,避免了文件从磁盘复制到 Read Buffer(内核)再复制到 Aplication Buffer (应用空间)再到 Socket Buffer、NIC Buffer、消费者进程(内核)的反复复制,数据直接从 Read Buffer 发给 NIC Buffer。

consumer group 中多个分片可以并发消费,但是需要自动或手动进行 partition 到 consumer 的分配。

  • 手动分配:哪个 consumer 消费,哪个 partition 完全都由业务决定。但是无法自动容灾,无法高效扩缩容
  • 自动分配:选择一个 coordinator 协调者,帮助完成每个 consumer group 中给每个 consumer 分配 partition 的问题。

Consumer Rebalance:给每个 consumer 对应的负载最低的 broker 发送请求,获取 coordinator 是谁;再向 coordinator 发送请求,申请加入 consumer group,coordinator 收到请求后会在当前的 consumer 中选出一个 leader 来计算分配策略;coordinator 返回加入成功后,新增 consumer 会向 coordinaton 申请同步集群的类方案,leader 计算完成后会发给 coordinator;再由 coordinator 发给新加入的 consumer。开始同步后,新加入 consumer 会向 coordinator 发送一段时间的心跳,防止同步信息的 consumer 宕机导致数据不全。

2.5 kafka 的缺点

涉及 leader 的关机、重启时,涉及到切换 leader、追赶进度、leader 回切。在这个过程中可能出现1个 broker 包含多个 leader,IO 压力会很大。

替换、扩容、缩容的操作非常耗时。

负载不均衡时,需要迁出,但是迁出再次引入 IO 问题。

问题总结:运维成本高、负载不均衡场景解决方案负载、没有自己的缓存完全依赖 page cache、controller 和 coordinator 和broker 在同一进程中,大量 IO 会造成性能下降。

3 BMQ

兼容 kafka 协议,存算分离,云原生消息队列。

3.1 和 kafka 区别

controller 和 coordinator 独立出来分开部署,加入了 proxy cluster,底层使用分布式存储系统。

3.2 HDFS 写文件流程

在 DataNode 中随机选择一些节点写入

3.3 写文件流程

Message -> 使用 CRC 进行数据校验 -> Buffer -> Writer Thread -> Storage

Writer Thread: Write Data -> Flush -> Build Index -> Checkpoint -> Roll new segment file

Failover:写文件失败时会更好节点

3.4 Proxy

Fetch Request -> Wait -> Cache -> Storage

等待的作用:设置数据大小和时间窗口,降低 IO 压力。

3.5 多机房部署

防止机房级故障(断电了,炸了等等)

Proxy 每个机房都有全部信息,所有 Broker 加在一起是所有的 Broker。

3.6 高级特性

  • 泳道消息:下游消费者只会处理所在泳道的生产者发来的消息
  • databus:降低配置难度,简化消息队列客户端复杂度;支持动态配置无需停服;解耦业务与 Topic;缓解集群压力,提高吞吐
  • 集群镜像:通过异步拉取,解决跨 region (国家、亚太地区等)读写问题
  • 索引:通过讲数据结构化,配置索引 DDL,异步构建索引后通过 Index Query 实现反查数据(原本只支持 offset 和 timestamp)
  • Parquet:参考 Hadoop 生态中新型列式存储格式,使用不同方式构建 Parquet 格式文件

4 RocketMQ

RocketMQ 最初是基于低延时场景的。

比 kafka 多了一个标签 tag,用于进行细分。完成副本的角色控制范围更大。

所有模型都会先发到 broker 上,每个 broker 有且仅有一个 CommitLog,ConsumerQueue 中存储的是索引(密集索引)。

高级特性:通过最终一致解决事务问题、延迟消息、重试和死心队列。