什么是消息队列? 消息队列(MQ),指保存消息的一个容器,本质是个队列。但这个队列, 需要支持高吞吐,高并发,并且高可用。
01.前世今生
TIB
IBM
MQ/WebSphere
MSMO
JMS
AMQP/RabbitMQ
Kafka
RocketMQ
Pulsar
graph LR;
TIB\[TIB,1985]-->B(BM MQ/WebSphere,1993);
B-->C(MSMQ,1997);
C-->D(JMS,2001);
D-->E(AMQP/RabbitMQ,2004);
graph LR
E(AMQP/RabbitMQ,2004)-->F(Kafka,2010);
F-->G(RocketMQ,2011阿里研发)
G-->H(Pulsar,2012,yahoo)
- Kafka:分布式的、分区的、多副本的日志提交服务,在高吞吐场景下发挥较为出色
- RocketMQ:低延迟、强一致、高性能、高可靠、万亿级容量和灵活的可扩展性,在一些 实时场景中运用较广
- Pusr:是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体、 采用存算分离的架构设计
- BMQ:和Pusr架构类似,存算分离,初期定位是承接高吞吐的离线业务场景,逐步 替换掉对应的Kafka集群
2.2 基本概念和原理
一、Topic
Topic:Kakfa中的逻辑队列,可以理解成每一个不同的业务场是就是一个不同的topic,对于这个业务来说,所有的数据都存储在这个topic中
Cluster:Kafka的物理集群,每个集群中可以新建多个不同的topic Producer:顾名思义,也就是消息的生产端,负麦将业务消息发送到Topic当中 Consumer:消息的消费端,负麦消费已经发送到topic中的消息 Partition:通常topic会有多个分片,不同分片直接消息是可以并发来处理的,这样提高单个Topic的吞吐
二、Offset
Offset:消息在partition内的相对位置信息,可以理解为唯一ID,在partition内部严格递增。
对于每一个Partition来说,每一条消息都有一个唯一的Offset
三、Replice
Replice :分片的副本,分布在不同的机器上,可用来容灾,Leader对外服务,Follower异步去拉取Leader的数据进行一个同步,如果Leader挂掉了,可以将Follower提升成Leader再对外进行服务 ISR:意思是同步中的副本,对于Follower来说,始终和lleader是有一定差距的,但当这个差距比较小的时候,我们就可以将这个follower副本加入到ISR中,不在ISR中的副本是不允许提升成Leader的
四、数据复制:
下面这幅图代表着Kafka中副本的分布图。途中Brokert代表每一个Kafka的节点,所有的Broker节点最终组成了一个集群。
整个图表示:图中整个集群,包含了4个Broker机器节点,集群有两个Topic,分别是Topic1和Topic2,Topic1有两个分片,Topic2有1个分片,每个分片都是三副本的状态。
这里中间有一个Brokerl同时也扮演了Controller的角色,Controller是整个集群的大脑,负责对副本和Brokeri进行分配
五、Kafka架构
在集群的基础上,还有一个模块是ZooKeeper,这个模块其实是存储了集群的元数据信息,比如副本的分配信息等等, Controlleri计算好的方案都会放到这个地方
六、为什么快的原因
批量发送
发送1条消息,等到其成功后再发1条-----》批量发送
数据压缩
七、Broker----数据的存储
消息文件结构
先看看Kafaka最终存储的文件结构:
数据路径:Topic/Partition/Segment/(log|index|timeindex|.)
移动磁头找到对应磁道,磁盘转动,找到对应扇区,最后写入。寻道成本比较高.
因此顺序写可以减少寻道所带来的时间成本。就是在末尾追加写入,以提高写入效率
八、如何找到消息
此时我们的消息写入到Broker的磁盘上了,那这些数据又该怎么被找到然后用来消费呢
Consumer通过发送FetchRequest请求消息数据,Broker会将指定Offset处的消息,按照时问窗口和消息 大小窗口发送给Consumer,.寻找数据这个细节是如何做到的呢?
偏移量索引文件
介绍文件:文件名是文件中第一条消息的offset;然后,第一步,通过二分找到小于目标文件的最大文件
目标:寻找Offset = 28
通过二分找到小于目标offset最大的索引位置,再遍历找到目标offset
如果我们需要使用时间图戳来寻找的时候,和ofst相比只是多加了一级索引,也就是通过二分找到时间图戳对应的offset,再重复之前的步骤找到相应的文件数据
传统数据拷贝
Consumer从Broker中读取数据,通过sendfile的方式,将磁盘读到os内核缓冲区后,直接转到NIC bufferi进行网络发送 Producer生产的数据持久化到broker.,采用mmap文件映射,实现l顺序的快速写入
九、消息的接收端
对于一个Consumer Group来说,多个分片可以并发的消费,这样可以大大提高消费的效率,但需要解决的问题是,Consumer和Partition的分配问题,
也就是对于每一个Partition来讲,该由哪一个Consumer来消费的问题。对于这个问题,我们一般有两种解决方法,手动分配和自动分配
手动分配
也就是Kafka中所说的Low Level消费方式进行消费,这种分配方式的一个好处就是启动比较快,因为对于每一个Consumer来说,启动的时候就已经知道了自己应该去消费哪个Partition,就好比图中的Consumer Group1来说,Consumer1去消费Partition1,2,3;Consumer2,去消费456
自动分配
这里也叫做High Level的消费方式,简单的来说,就是在我们的Broker集群中,对于不同的Consumer Group:来讲,都会选取一台Broker当做Coordinator,而Coordinator的作用就是帮助Consumer Group:进行分片的分配;也叫做分片的rebalance,使用这种方式,如果ConsumerGroup中有发生宕机,或者有新的Consumer)加入,整个partition和Consumer都会重新进行分配来达到一个稳定的消费状态
十、Consumer Rebalance
Consumer Rebalance
可以帮助Kafka提高吞吐或者稳定性的功能? Producer:批量发送、数据压缩 Broker:顺序写,消息索引,零拷贝 Consumer : Rebalance
十一、数据复制问题
过前面的介绍我们可以知道,对于Kafka来说,每一个Broker上都有不同topic分区的不同副本,而每一个副本,会将其数据存储到该Kafka节点上面,对于不同的节点之间,通过副本直接的数据复制,来保证数据的最终一致性,与集群的高可用。
十二、重启操作
举个例子来说,如果我们对一个机器进行重启·首先,我们会关闭一个Broker,.此时如果该Broker上存在副本的Leader,.那么该副本将发生leader切换 切换到其他节点上面并且在 ISR 中的Follower副本,可以看到图中是切换到了第二个Broker上面.
而此时,因为数据在不断的写入,对于刚刚关闭重启的Broker来说,和新Leader之间一定会存在数据的滞后,此时这个Broker会追赶数据, 重新加入到ISR当中.
当数据追赶完成之后,我们需要回切leader,这一步叫做prefer leader,这一步的目的是为了避免,在一个集群长期运行后,所有的eader都分布在少数节点上,导致数据的不均衡.
通过上面的一个流程分析,我们可以发现对于一个Broker的重启来说,需要进行数据复制,所以时间成本会比较大,比如一个节点重启需要10分钟, 一个集群有1000个节点,如果该集群需要重启升级,则需要10000分钟,那差不多就是一个星期,这样的时间成本是非常大的 有同学可能会说,老师可以不可以并发多台重启呀,问的好,不可以。为什么呢,在一个两副本的集群中,重启了两台机器,对某一分片来讲,可两个分片都在这台机器上面,则会导致该集群处于不可用的状态。这是更不接受的.
十三、扩容、替换、缩容
如果是替换,或者扩容,或者缩容操作呢,我们来看看。 如果是替换,和刚刚的重启有什么区别,其实替换,本质上来讲就是一个需要追更多数据的重启操作,因为正常重启只需要追一小部分,而替换,则是需要复制整个ad的数据,时间会更长 扩容呢,当分片分配到新的机器上以后,也是相当于要从0开始复制一些新的副本 而缩容,缩容节点上面的分片也会分片到集群中剩余节点上面,分配过去的副本也会从开始去复制数据 以上三个操作均有数据复制所带来的时间成本问题,所以对于Kafka来说,运维操作所带来的时间成本是不容忽视的
十四、负载不均衡
这个场晨当中,同一个Topici有4个分片,两副本,可以看到,对于分片1来说,数据量是明显比其他分片要大的,当我们机器 IO 达到瓶颈的时候,可能就需要把第一台Broker_上面的Partition.3迁移到其他负裁小的Broker.上面,接着往下看
但我们的数据复制又会引起Broker1的 IO 升高,所以问题就变成了,我为了去解决 IO 升高,但解决问题的过程又会带来更高的 IO ,所以就需要权衡 IO 设计出一个极其复杂的负裁均衡策略
Kafka问题总结
运维成本高 对于负载不均衡的场景,解决方案复杂 没有自己的缓存,完全依赖Page Cache Controller和Coordinator和Bro.er在同一进程中,大量IO会造成其性能下降
第一,因为有数据复制的问题,所以Kafka运维的时间成本和人力人本都不低 第二,对于负裁不均衡的场是,我们需要有一个较为复杂的解决方案进行数据迁移,从而来权衡 IO 升高的问题 除了以上两个问题以外,Kaka自身还存在其他的问题 比如,Kafka没有自己的缓存,在进行数据读取的时候,只有Page Cachei可以用,所以不是很灵活 另外在前面的介绍当中,相信大家也了解到了,Kafka的Controller和Coordinator都是和Brokeri部署在一起的。