消息队列 | 青训营笔记

179 阅读6分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第13天

前言

本文具体介绍了消息队列的概念,并具体讲解了Kafka、BMQ、RocketMQ这三种消息队列,并进行了相互间的对比。本文目前只是简单的科普,具体实践还需要大家自己学习。

1.案例引入

1.系统崩溃

  • 解决方案:解耦image.png

2.服务处理能力优先

  • 解决方案:削峰image.png

3.链路耗时长尾

  • 解决方案:异步image.png

4.日志如何处理

  • 解决方案:多级方法处理image.png

2.消息队列概念

  • 指保存消息的一个容器,本质是个队列。但这个队列呢,需要支持高吞吐,高并发,高可用

1.发展历史

image.png

2.消息队列对比

  • Kafka:分布式的、分区的、多副本的日志提交服务,常用在高吞吐场景下
  • RocketMQ:低延迟、强一致、高性能、高可靠,常用在实时场景下
  • Pulsar:集消息、存储、轻量化函数式计算为一体,采用存算分离的架构设计
  • BMQ:类似Pulsar,存算分离,替换Kafka

3.Kafka

1.使用场景

image.png

2.使用流程

  • 创建集群
  • 新增Topic
  • 编写生产者逻辑
  • 编写消费者逻辑

3.基本概念

  • Topic:逻辑队列,不同 Topic 可以建立不同的 Topic

  • Cluster:物理集群,每个集群中可以建立多个不同的 Topic

  • Producer:生产者,负责将业务消息发送到 Topic 中

  • Consumer:消费者,负责消费 Topic 中的消息

  • ConsumerGroup:消费者组,不同组 Consumer 消费进度互不干涉 image.png

  • Offset :消息在partition内的相对位置信息,可以理解为唯一ID,在partition内部严格递增。 image.png

  • Replica:每个分片有多个Replica, Leader Replica将会从ISR中选出。 image.png

4.数据复制流程

各个Topic的leader在不同的Broker中,每个Broker有不同Topic的不同副本 image.png

5.架构

image.png 其中,ZooKeeper负责存储集群元信息,包括分区分配信息等

6.Producer(消息发送端)

  • 批量发送(减少IO次数,加强发送能力,但受网络宽带限制)
  • 数据压缩(减少消息大小,支持Snappy、 Gzip、 174、 ZSTD压缩算法)

7.Broker(数据存储)

  • 消息文件结构 image.png
  • 磁盘结构
    • 方法:移动磁头找到对应磁道,磁盘转动,找到对应扇区,最后写入。但寻道成本比较高,因此可以减少寻道所带来的时间成本。
  • 采用顺序写的方式写入,提高写入效率
  • 查找消息办法
    • 偏移量索引文件(二分找到小于目标时间戳最大的索引位置)
    • 时间戳索引文件(二分找到小于目标时间戳最大的索引位置,再通过寻找offset的方式找到最终数据)image.png
  • 数据拷贝类型
    • 传统数据拷贝
    • 零拷贝(Reader Buffer直接给NIC Buffer)image.png

8.Consumer(消息接收端)

  • 接收方式
    • Low Level(手动分配):当增减consumer时,会影响Partition消费
    • High Level(自动分配):引入Coordinator,实现Rebalance
  • Rebalance流程
    • Consumer选出Group Coordinator
    • Group Coordinator选出Consumer Leader
    • Consumer Leader发给Group Coordinator分配方案

下图是整体流程: image.png

9.重启操作

  • Leader关闭,重启
  • Leader切换,追赶数据
  • 数据同步完成
  • Leader回切

替换、扩容、缩容

  • 替换,一个需要追更多数据的重启操作,需要复制整个leader的数据,时间会更长
  • 扩容,当分片分配到新的机器上以后,从0开始复制一些新的副本
  • 缩容,分片分配到集群中剩余节点上面,也得从0开始去复制数据

以上三个操作均有数据复制所带来的时间成本问题,所以对于Kafka来说,运维操作所带来的时间成本是不容忽视的

10.问题

  • 运维成本高。数据需要复制
  • 对负载不均衡场景,解决方案复杂
  • 没缓存,完全依赖Page Cache
  • Controller、Coordinator、Broker 在同一进程中,Broker中大量IO会造成其性能下降

4.BMQ

兼容Kafka,存算分离,云原生消息队列

1.架构图

image.png

与Kafka运维对比 image.png

  • HDFS文件写入:随机选择一定数量的DataNode写入

2.文件结构

image.png

  • 对于单个队列,随机分配到不同节点上,解决了负载不均衡的问题

3.Broker

Partition状态机

流程如下: image.png

  • 保证对于任意分片在同一时刻只能在一个Broker上存活

写文件流程 image.png

  • 当写入失败时,不能进行等节点恢复,而是重新找正常的节点写入

4.Proxy

image.png 本质是一种等待机制,降低了fetch请求IO的次数

5.多机房部署

  • 防止单机故障
  • 防止机房级故障,如网络故障等 image.png

6.高级特性

  • 泳道
  • Databus
  • Mirror
  • Index
  • Parquet image.png

7.泳道消息

  • 开发流程:开发->BOE->PPE->Prod

    • BOE是一套完全独立的线下机房环境
    • PPE是产品预览环境
  • 原因:

    • 在BOE测试中,只有一个Topic时,多人同时测试需要等待上一人测试完;多条主干时,每个测试人员都需要重新搭建一个配置相同的Topic,造成资源浪费
    • 在PPE测试中,只有一个Topic,资源没有生产环境多,无法承受生产环境的流量
  • 框架 image.png 解决了主干泳道流量隔离问题和通道资源重复创建问题

8.Databus

  • 原生SDK缺点
    • 客户端配置复杂
    • 不支持动态配置,更改配置需要停掉服务
    • 对于 latency 不是很敏感的业务,batch 效果不佳
  • 框架 image.png
  • 优点
    • 简化消息队列客户端复杂度
    • 解耦业务与Topic
    • 缓解集群压力,提高吞吐量

9.Mirror

image.png 通过最终一致的方式,解决跨Region读写问题

10.Index

image.png 直接在 BMQ 中将数据结构化,配置索引 DDL,异步构建索引后,通过 Index Query 服务读出数据。

11.Parquet

Apache Parquet是Hadoop生态圈中一种新型列式存储格式,它可以兼容 Hadoop 生态圈中大多数计算框架(Hadoop、Spark等),被多种查询引擎支持(Hive、Impala、Drill等)。

  • 框架 image.png 直接在 BMQ 中将数据结构化,通过 Parquet Engine,可以使用不同的方式构建 Parquet格式文件。

5.RocketMQ

1.基本概念

引入了标签、生产者集群,分区命名为ConsumerQueue,集群控制器命名为NameServer

2.架构

image.png

  • 先说数据流也是通过Producer发送给Broker集群,再由Consumer进行消费
  • Broker节点有Master和Slave的概念
  • NameServer为集群提供轻量级服务发现路由

3.存储模型

Broker中所有的消息的会anpend到一个Commitlog上面->按照不同的Queue重新Dispatch到不同的Consumer中->Consumer按照Queue进行拉取消费 image.png 注意:ConsumerQueue所存储的并不是真实的数据,存的是Queue所有消息在CommitLog上面的位置(Queue的一个密集索引),真实的数据只存在CommitLog中

4.高级特性

  • 事务场景(比如库存记录-1和消息队列要成为一个事务,进而有了事务保证)
  • 事务消息(有回滚,并能查询状态) image.png
  • 延迟发送(将定时任务改为消息队列)
  • 延迟消息(增加延时服务)
  • 消费重试和死信队列 image.png

小结

本文主要介绍了三种消息队列的性质、框架和一些高级特性,具体实操还是需要查找相应的资料。通过攥写本文,小编对消息队列的理解也加深了,大家也要多多总结

参考

  • 字节跳动走进消息队列教程