这是我参与「第五届青训营」伴学笔记创作活动的第20天
消息队列的前世今生
消息队列:是保存消息的一个容器,本质是一个队列,但有高吞吐、高并发、高可用的特点
消息队列的作用
- 解耦:将存储服务与事件解耦
- 削峰
- 异步:消息队列异步发送给不同的服务
- 日志处理:日志先发到消息队列,消息队列再发给搜索引擎等展示,避免因本地服务崩溃导致本地日志不可察
消息队列发展历程
业界消息队列对比
- Kafka:分布式、分区、多副本的日志提交服务,在高吞吐场景发挥出色
- RocketMQ:低延迟、高性能、该可靠、万亿计容量和灵活的可扩展性,在实时场景运用较广
- Pulsar:下一代云原生分布式消息流平台,集消息、存储、轻量化函数计算为一体、采用存算分离的架构设计
- BMQ:字节研发和Pulsar架构类似,存算分离,初期定位是承接高吞吐的离线业务场景,逐步替换对应的kafka集群
消息队列-Kafka
使用场景
一般在离线消息应用场景较多:日志信息、Metrics数据、用户行为等
基本概念
- Topic:逻辑队列,不同的topic可以建立不同的topic
- Cluster:物理集群,每个集群中可以建立多个不同的Topic
- Producer:生产者,负责将业务消息发送到Topic中
- Consumer:消费者,负责消费Topic中的消息
- ConsumerGroup:消费者组,不同组的消费进度互不干涉
- Partition:Topic的分区,不同分区的消息可以并发处理
7. Offset:在partition内的相对位置信息,可以理解为唯一ID,在partition内部严格递增
- Leader:对外的写入(生产者)和读取(消费者)
- Flower:不断将Leader的数据拉取下来,努力与Leader保持一致,
- ISR(In-Sync Replicas):如果Flower与Leader差距过大,则不许Flower再去拉数据,而将其提出,如果Leader所在机器发生宕机,则选择一个Flower作为Leader
- 差距过大老版本根据Offset判别
- 新版本根据时间判别
- Broker:副本的节点,所有节点组成一个集群
- Controller:集群大脑,对集群的Leader进行分配
13. Zookeeper:与Controler配合,存储集群的运输局信息、分区分配信息
Producer:消息发送
- 批量发送:一个Batch 包含多个message,减少IO次数,加强发送能力
- 压缩:减少消息大小,防止因消息太大导致带宽不够
- 压缩算法:Snappy(默认)、Gzip、LZ4、ZSTD(性能优秀)
Broker:数据存储与查找
消息存储
Brocker将一个个Batch存入磁盘
- 消息都是以
Log的形式写入磁盘的 - 每一个Log分为LogSegment,每个LogSegment又分为四个部分
- 每个LogSegmengt使用该Segmengt内部第一条消息的
Offset作为文件名
4. 顺序写:提高写入效率,在
磁盘的扇区末尾添加,减少磁盘的寻道时间(因为不会像数据库需要对消息进行修改)
如何找到消息
Offset索引:
- 首先根据Offset二分找到该Log在哪个文件内
- 找到小于目标Offset的最大索引位置(稀疏索引)
- 顺序读取Batch,找到消息
时间索引:
- 类似,就是加了一个时间的二级索引
- 二分找到小于目标时间的最大索引位置
- 再通过寻找offset的方式找到最终数据
内存拷贝
零拷贝技术:减少了三次拷贝,直接从内核态拷到网卡 mmap技术:再用户态写入不同先写进内核空间,而是直接写到磁盘
Consumer:消息的接收端
每一个ConsumerGroup都是一个独立的整体,即需要拉取一个Topic中的全部数据,那么哪个Consumer拉取哪个Partition呢?
手动分配:LowLevel
先在代码中分配好,每个consumer就是一个进程
缺点:
不能容灾:如果consumer3挂掉,则partition7和partiton8就会拉取不到数据中断:若consumer1,2性能不够,需要加一个consumer4,**如何重新分配?**必须有一个进程的启停
优点:快
自动分配:HighLevel
对于不同的ConsumerGroup都会从Broker中选取一个作为Coordinator:帮助Consumer自动分配,该过程被叫做Reblance
Reblance
- ConsumerGroup中的Consumer像Broker集群发起询问,选一个负载最低的Broker作为Coordinator
- Coordinator会从当前Consumer中选取一个Leader
- Coordinator向Consumer同步分配方案
- Consumer会定时向Coordinator发送心跳,以避免突然宕机
问题
数据复制问题
- 如果某个Leader重启了
- Leader切换,原Broker重启会疯狂的追赶数据:相当耗时
- 数据同步完成
- Leader回切(方式1-99个Broker全部重启导致所有Leader全部到一个Broker上)
简而言之对于kafka集群,只要有任何节点的变动(替换、扩容、缩容)就会引起数据复制所带来的时间成本问题,导致时间成本、运维成本很高
负载不均衡问题
- 将partition3迁移到controller,以降低Broker的IO负载
- 但是迁走partition3又会导致数据复制问题,从而使得IO变高,
- 死循环
总结
- 运维成本高
- 对于负载不均衡的场景,解决方案复杂
- 没有自己的缓存,依赖Page Cache
- Controler、Coordinator、Broker在同一个进程中,大量IO会导致性能下降