引入
案例一:系统崩溃
- 解决方案:解耦
- 解决方案:解耦
案例二:服务能力有限
- 解决方案:削峰
- 解决方案:削峰
案例三:链路耗时长尾
- 解决方案:异步
- 解决方案:异步
案例四:日志存储
- 服务器故障日志丢失
- 解决方案:
什么是消息队列?
- 消息队列(MQ),指保存消息的一个容器,本质是个队列,但这个队列需要支持高吞吐、高并发、并且高可用
前世今生
消息队列发展历程
业内消息队列对比
消息队列-Kafka
使用场景
如何使用 Kafka
基本概念
Topic:逻辑队列,不同 Topic 可以建立不同的 Topic
Cluster:物理集群,每个集群中可以建立多个不同的 Topic
Producer:生产者,负责将业务消息发送到 Topic 中
Consumer:消费者,负责消费 Topic 中的消息
ConsumerGroup:消费者组,不同组 Consumer 消费进度互不干涉
Offset:消息在 partition 内的相对位置信息,可以理解为唯一 ID,在 partition 内部严格递增
Replica:每个分片有多个 Replica,Leader Replica 将会从 ISR 中选出
数据复制
Kafka 架构
一条消息的自述
- 从一条消息的视角,看看为什么 Kafka 能支撑这么高的吞吐?
- 如果发送一条消息,等到其成功后再发一条会有什么问题?
Producer
- 批量发送
- 如果消息量很大,网络宽带不够用,如何解决?
- 数据压缩
Broker
- 数据的存储
- 如何存储到磁盘?
- 消息文件结构
- 数据路径:/Topic/Partition/Segment/(log | index |timeindex | …)
- 磁盘结构
- 移动磁头找到对应磁道,磁盘转动。找到对应扇区,最后写入,寻道成本比较高,因此顺序可以减少寻道所带来的时间成本
- 顺序写
- 采用顺序写的方式进行写入,以提高写入效率
- 如何找到消息
- Consumer 通过发送 FetchRequest 请求消息数据,Broker 会将指定 Offset 处的消息,按照时间窗口和消息大小窗口发送给 Consumer,寻找数据这个细节是如何做到的呢?
- 偏移量索引数据
- 目标:寻找 offset = 28
- 二分找到小于目标 offset 的最大文件
- 时间戳索引文件
- 二分找到小于目标时间戳最大的索引位置,在通过寻找 offset 的方式找到最终数据
- 传统数据拷贝
- 零拷贝
Consumer
- 消息的接收端
- 如何解决 Partition 在 Consumer Group 中的分配问题?
- Low Level
- 通过手动进行分配,哪一个 Consumer 消费哪一个 Partition 完全由业务来决定
- 这种方式的缺点是什么?
- 如果 Consumer3 挂掉了,7,8 分片就停止消费了
- 如果新增了一台 Consumer4 ,需要重新停掉整个集群,重新修改配置再上线,保证 Consumer4 也可以消费数据
- High Level
- Rebalance
- Rebalance
一些可以帮 Kafka 提高吞吐或者稳定性的功能
- Producer:批量发送、数据压缩
- Broker:顺序写,消息索引,零拷贝
- Consumer:Reblance
问题
- 数据复制问题
- 重启操作
- 替换、扩容、缩容
- 负载不均衡
- 问题总结
- 运维成本高
- 对于负载不均衡的场景,解决方案复杂
- 没有自己的缓存,完全依赖 Page cache
- Controller 和 Coordinator 和 Broker 在同一进程中,大量 IO 会造成其性能下降
消息队列-BMQ
BMQ 简介
兼容 Kafka 协议,存算分离,云原生消息队列
BMQ 介绍
运维操作对比
HDFS 写文件流程
BMQ 文件结构
Broker
- Partition 状态机
- 保证对于任意分片在同一时刻只能在一个 Broker 上存活
- 写文件流程
- 写文件 Failover
- 如果 DataNode 节点挂了或者是其他原因导致我们写文件失败,应该如何处理?
Proxy
多机房部署
高级特性
泳道消息
- BOE:Bytedance Offline Environment,是一套完全独立的线下机房环境
- PPE:Product Preview Environment,即产品预览环境
多个人同时测试,需要等待上一个人测试完成
每多一个测试人员,都需要重新搭建一个相同配置的 Topic,造成人力和资源的浪费
对于 PPE 的消费者来说,资源没有生产环境多,所以无法承受生产环境的流量
解决主干泳道流量隔间问题以及泳道资源重复创建问题
Databus
- 直接使用原生 SDK 会有什么问题?
- 客户端配置较为复杂
- 不支持动态配置
- 对于 latency 不是很敏感的业务,batch 效率不佳
- 简化消息队列客户端复杂配置
- 解耦业务与 Topic
- 缓解集群压力,提高吞吐
Mirror
是否可以通过多机房部署的方式,解决跨 Region 读写的问题?
使用 Mirror 通过最终一致的方式,解决跨 Region 读写问题
Index
如果希望通过写入的 LogId、UserId 或者其他的业务字段进行消费的查询,应该怎么做?
直接在 BMQ 中将数据结构化,配置索引 DDL,异步构建索引后,通过 Index Query 服务读出数据
Parquet
Apache Parquet 是 Hadoop 生态圈中一钟新型列式存储格式,它可以兼容 Hadoop 生态圈中大多数计算框架(Hadoop、Spark 等),被多种查询引擎支持(Hive、Impala、Drill 等)
直接在 BMQ 中将数据结构化,通过 Parquet Engine,可以使用不同的方式构建 Parquet 格式文件
小结
- BMQ 的架构模型(解决 Kafka 存在的问题)
- BMQ 读写流程(Failover 机制,写入状态机)
- BMQ 高级特性(泳道、Databus、Mirror、Index、Parquet)
消息队列-RocketMQ
使用场景
例如,针对电商业务线,其业务涉及广泛,如注册、订单、库存、物流等;同时,也会涉及许多业务峰值时刻,如秒杀活动、周年庆、定期特惠等
基本概念
架构
存储模型
高级特性
事务场景
事务消息
延迟发送
延时消息
处理失败
该如何处理失败的消息呢?
消费重试和死信队列
小结
- RocketMQ 的基本概念(Queue、Tag)
- RocketMQ 的底层原理(架构模型、存储模型)
- RocketMQ 的高级特性(事务消息、重试和死信队列、延迟队列)