13 消息队列原理与实战 | 青训营笔记

98 阅读6分钟

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

资料: juejin.cn/post/719632…

课程1: juejin.cn/course/byte…

课程2: juejin.cn/course/byte…

课程3: juejin.cn/course/byte…

课程4: juejin.cn/course/byte…

讲师: 黄庭坚

思考时间

案例一: 系统崩溃

 搜索直播间
 搜索行为记录 ->↓
 点击商品    记录存储
 点击行为记录 ->
 如果此时记录存储程序所在的机房被删库跑路了, 上面这个流程会发生什么问题?
 解决方案: 解耦
 搜索商品
 搜索行为记录 ->↓
 点击商品    消息队列->存储服务
 点击行为记录 ->

案例二: 服务能力有限

 面对庞大的请求量, 处理订单的服务一脸茫然, 它的命运该何去何从?
 解决方案: 削峰
 发起订单
 消息队列
 处理订单(每次只获取10个请求进行处理)

案例三: 链路耗时长尾

 发起订单(5ms)->库存记录-1(100ms)->订单记录+1(100ms)->通知商家(30s)->下单成功
     
 对于这个流程应该怎样优化来挽回这个暴躁的用户?
 解决方案: 异步
 发起订单(5ms)
 消息队列(50ms)->下单成功
 ↓ ↓ ↓
 ↓ ↓ 订单记录+1(100ms)
 ↓ 库存记录-1(10ms)
 通知商家(30s)

案例四: 日志存储

 日志如何处理
 解决方案:
 Log->消息队列->LogStash->ES->Kibana

什么是消息队列?

 消息队列(MQ), 指保存消息的一个容器, 本质是个队列. 但这个队列, 需求支持高吞吐, 高并发, 并且高可用
 生成 -> [Message Message Message] -> 消费

01 消息队列发展历程

 TIB: 诞生于1985年, 服务于金融机构和新闻机构
 IBM MQ/WebSphere: 诞生于1993年, 商业消息队列平台市场主要玩家
 MSMQ: 微软发布于1997年
 JMS: 诞生于2001年, 本质上是一套Java API
 AMQP/RabbitMQ: 规范发布于2004年, 同年RabbitMQ面市
 Kafka: 2010年由Linked开源
 RocketMQ: 2011年阿里中间件团队自研
 Pulsar: 2012年诞生于Yahoo内部

01 业界消息队列对比

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

02 消息队列 - Kafka

2.1 使用场景

搜索服务 直播服务 订单服务 支付服务

日志信息 Metrics数据 用户行为

2.2 如何使用Kafka

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

2.3 基本概念

Topic: 逻辑队列, 不同的业务可以建立不同的Topic
Partition: Topic的分区, 不同分区可以并发处理, 以此提高吞吐能力
Cluster: 物理集群, 每个集群中可以建立多个不同的Topic
Producer: 生产者, 负责将业务消息发送到Topic中
Consumer: 消费者, 负责消息Topic中的消息
ConsumerGroup: 消费者组, 不同组Consumer消费进度互不干涉

Offset: 消息在Partition内的相对位置消息, 可以理解为唯一ID, 在Partition内部严格递增

Replica: 每个分片有多个Replica, Leader Replica将会在ISR中选出 ISR: In-Sync Replicas(Replica-Leader以及与Leader相差不大的Replica-Follower们)

2.4 数据复制

 Broker代表Kafka中的节点, Broker中会选出Controller
 Broker主要功能是持久化Producer发送的消息, 以及将消息队列中的消息从发送端传输到消费端(Consumer)
 Controller管理集群中所有分区和副本的状态并执行相应的管理操作

2.5 Kafka架构

见PPT图

2.6 一条消息的自述

 Producer -> Broker -> Consumer
 为什么Kafka可以这么高的吞吐?

2.7 Producer

批量发送

 批量发送可以减少IO次数, 从而加强发送能力
 如果消息量很大, 网络带宽不够用, 如何解决?

数据压缩

 通过压缩, 减小消息大小, 目前支持Snappy, Gzip, LZ4, ZSTD压缩算法

2.8 Broker

消息文件结构

LogSegment
.log(日志文件)
.index(偏移量索引文件)
.timeindex(时间戳索引文件)
其他文件\

磁盘结构

 磁盘: 移动磁头找到对应磁道, 磁盘转动, 找到对应扇区, 最后写入. 寻道成本比较高
⭐因此, 顺序写可以减少寻道所带来的时间成本

如何找到消息

Conumer通过发送FetchRequest请求消息数据, Broker会将指定Offset处的消息, 按照时间窗口和消息大小窗口发送给Consumer
寻找数据这个细节是如何做到的呢?

偏移量索引文件

 二分查找小于目标offset的最大索引位置

时间戳索引文件

 先二分查找小于目标时间戳最大的索引位置, 再通过offset的方式找到数据

零拷贝

2.9 Consumer

如何解决Partition在Consumer Group中的分配问题?

 方法一: 手动分配
 缺点:
     不能自动容灾
     扩容要启停
 方法二: 自动分配

Rebalance

见PPT图

小结

Kafka如何提高吞吐或者稳定性的?
Producer: 批量发送, 数据压缩
Broker: 顺序写, 消息索引, 零拷贝
Consumer: Rebalance

2.11Kafka缺点

1 运维成本高
数据复制问题: 节点变动(重启, 替换, 扩容, 缩容)都会带来数据复制问题, 带来很大的时间, 人力成本
2 对于复杂不均衡的场景, 解决方案复杂
3 没有字节的缓存, 完全依赖Page Cache
4 Controller和Coordinator和Broker在同一进程中, 大量IO会造成性能下降

03 消息队列 - BMQ

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

加了层Proxy

引入了HDFS分布式存储

运维操作由分钟级甚至天级, 优化为秒级

Partition状态机, 保证对于任意分片在同一时刻只能在一个Broker上存活

Parquet: Hadoop生态圈中一种新型列式存储格式

小结

 1 BMQ的架构模型(解决Kafka存在的问题)
 2 BMQ的读写流程(Failover机制, 写入状态机)
 3 BMQ高级特性(泳道, Databus, Mirror, Index, Parquet)

04 消息队列 - RocketMQ

基本概念

名称 | Kafka | RocketMQ
逻辑队列 | Topic | Topic
消息体 | Message | Message
标签 | 无 | tag
分区 | Partition | ConsumerQueue
生产者 | Producer | Producer
生产者集群 | 无 | Producer Group
消费者 | Consumer | Consumer
消费者集群 | Consumer Group | Consumer Group
集群控制器 | Controller | Nameserver

小结

 1 RocketMQ的基本概念(Queue, Tag)
 2 RocketMQ的底层原理(架构模型, 存储模型)
 3 RocketMQ的高级特性(事务消息, 重试和死信队列, 延迟队列)