消息队列 / Kafka 的好处
(1)缓冲/消峰:有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况;
(2)解耦:允许独立的扩展或修改两边的处理过程,只要确保它们遵守相同的接口约束;
(3)异步通信:允许用户把一个消息放入队列,但并不立即处理它,然后在需要的时候再去处理它们。
消息队列的两种模式
(1)点对点(一对一)
(2) 发布/订阅(一对多)
什么是 Kafka
Kafka是由 Apache 开发的一个分布式的基于发布订阅模式的消息队列,主要应用于大数据的实时处理领域。
Kafka 优缺点
优点
- 高吞吐量、低延迟: Kafka 可解耦数据流,因此延迟非常低,速度极快。即使在非常廉价的机器上,Kafka也能做到每秒处理几十万条消息,而它的延迟最低只有几毫秒。
- 可扩展性:Kafka 集群支持热扩展。Kafka 的分区日志模型允许跨多个服务器分发数据,使其可扩展性超越了在单服务器上应用的情况。
- 持久性、可靠性: 分区可以跨多个服务器分发和和复制,数据全都写入到磁盘。这有助于防止服务器发生故障,使数据获得出色的容错能力和耐久性。
- 容错性:允许集群中节点故障(若副本数量为n,则允许n-1个节点故障)。
- 高并发:支持数千个客户端同时读写。
缺点
- 仅支持统一分区内消息有序,无法实现全局消息有序;
- 监控不完善,需要安装插件;
- 由于是批量发送,数据并非真正的实时;
- 依赖zookeeper进行元数据管理。
你在哪些场景下会选择 Kafka
(1)在系统或应用程序之间构建可靠的用于传输实时数据的管道,消息队列功能
(2)构建实时的流数据处理程序来变换或处理数据流,数据处理功能
讲下 Kafka 的整体结构
(1)Producer:消息生产者,负责向Broker发送消息
(2)Consumer:消息消费者,从负责Broker读取并消费消息
(3)Consumer Group(CG):消费者组,由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
(4)Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。
(5)Topic :主题,使用Topic对消息进行分类,Kafka接收到的每条消息都会放入到一个Topic中。
(6)Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,对Topic的数据进行分布式存储的最小单位。
(7)Replica:副本,为保证集群中的某个节点发生故障时,该节点上的partition数据不丢失,且kafka仍然能够继续工作,kafka提供了副本机制,一个topic的每个分区都有若干个副本,一个leader和若干个Follower。
(8)Leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是leader。
(9)Follower:每个分区多个副本中的“从”,实时从leader中同步数据,保持和leader数据的同步。leader发生故障时,某个Follower会成为新的leader。
Kafka 工作原理 / 流程
Kafka中消息是以topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的。topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于一个log文件,该log文件中存储的就是producer生产的数据。Producer生产的数据会被不断追加到该log文件末端,且每条数据都有自己的offset。消费者组中的每个消费者,都会实时记录自己消费到了哪个offset,以便出错恢复时,从上次的位置继续消费。
Kafka为什么那么快/高效读写的原因 / 实现高吞吐的原理
Kafka 的数据在log文件中,是保存在磁盘上。
(1)Kafka本身是分布式集群,并且采用分区技术,并行度高
(2)读数据采用稀疏索引,可以快速定位到要消费的数据
(3)写 log 文件的时候,一直是追加到文件末端,是顺序写的方式,官网中
说了,同样的磁盘,顺序写能达到 600M/s,而随机写只有 100K/s
(4)实现了零拷贝技术,只用将磁盘文件的数据复制到页面缓冲区一次,然后将数据从页面缓冲区直接发送到网络中,这样就避免了在内核空间和用户空间之间的拷贝
生产者如何提高吞吐量(调优)
(1)batch.size:批次大小,默认 16k,即一次发送消息的批次大小。增大批次大小可以减少单个请求的次数,提高吞吐量。
(2)linger.ms:等待时间,调整等待时间可以让生产者等待更多的消息一起发送,减少请求的频率。
(3)compression.type:压缩 snappy(解压缩速度快,压缩率也还行)
(4)RecordAccumulator:缓冲区大小,增大 RecordAccumulator 的缓冲区大小可以积累更多的消息,减少发送请求的频率。
kafka 消息数据积压,消费者如何提高吞吐量
(1)如果是 Kafka 消费能力不足,则可以考虑增加 Topic 的分区数,并且同时提升消费组的消费者数量,消费者数=分区数。 ( 两者缺一不可)
(2)如果是下游的数据处理不及时,提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间 < 生产速度),使处理的数据小于生产的数据,也会造成数据积压。
- fetch.max.bytes:指定了每次拉取消息的最大字节数。增大这个值可以提高每批次拉取的数据量,加快数据的处理速度。
- max.poll.records:指定了每次拉取请求返回的最大消息数。增大这个值可以一次性获取更多的消息,从而提高每批次处理的数据量。
说一下Kafka生产者如何生产数据,消费者如何消费数据
Kafka生产者写入(生产)数据流程
- 生产者先从Zookeeper的 "/brokers/topics/主题名/partitions/分区名/state"节点找到该 partition 的Leader
- 生产者在Zookeeper中找到该ID找到对应的broker
- broker进程上的Leader将消息写入到本地log中
- Follower从Leader上拉取消息,写入到本地log,并向Leader发送ACK
- Leader接收到所有的ISR中的Replica的ACK后,并向生产者返回ACK
Kafka消费者消费数据流程
- 通过Zookeeper找partition对应的Leader位置以及offset,Leader是负责读的
- 找到该分区的Leader,拉取数据
- 然后开始从offset往后顺序从本地log中读取数据
- 消费者提交offset(自动提交——每隔多少秒提交一次offset、手动提交——放入到事务中提交)
Kafka 中的数据是有序的吗,如何保证有序性?
kafka 中的每个 partition 中的消息在写入时都是有序的,而且单独一个 partition 只能由一个消费者去消费,可以在分区内部保证消息的顺序性。但是分区之间的消息是不保证有序的。
解决办法:
- 设置topic有且仅有一个分区
- 从业务上把需要有序的打到同一个 partition (指定相同的分区号或者使用相同的 key)
指定partition的话,如果发生了数据倾斜,一个key的数据全发到了一个partition会出现什么问题
partition倾斜就是热点倾斜,会导致消息堆积,可以通过hash算法或者亲缘性线程池去解决,但是亲缘性线程池也有热点倾斜问题,最本质的方式是对每个message的hash处理。
亲缘性线程池:针对某些需要保持数据顺序处理或特定数据之间存在关联的情况,可以使用亲缘性线程池来确保相关数据被分配到同一个线程或任务中处理,从而避免热点倾斜问题(避免了频繁的线程切换,减少网络通信开销)。
Kafka 中是如何实现消息的有序的
生产者:通过分区的 leader 副本负责数据以先进先出的顺序写入,来保证消息顺序性。
消费者:同一个分区内的消息只能被一个 group 里的一个消费者消费保证分区内消费有序。
Kafka Producer Batch
Kafka Producer Batch是指Kafka生产者在向Broker发送消息时可以一次性发送多条消息,减少网络传输的开销和提高消息发送的效率。在新建ProducerBatch时需要评估这条消息的大小是否超过batch.size,如果不超过,就以batch.size的大小来创建这个ProducerBatch,这样在使用完后还可以通过BufferPool的管理进行复用。若果超过,则以消息的大小来创建ProducerBatch,此内存区域不会被复用。
broker 的工作流程
在Kafka集群中,broker 指 Kafka 服务器。
(1)broker 启动后在 zk 注册
(2)Kafka 集群中的 broker 都到 zk 的 controller 节点集群注册,谁先注册,哪个broker 就成为controller 。
(3)选举出来的 controller 监听brokers 节点的变化
(4)controller决定Leader选举
(5)controller 将节点信息上传至 zk
(6)其他的 broker 从 zk 同步相关信息
(7)假设 broker1 中的 Leader 挂掉了
(8)controller 监听到节点变化
(9)controller获取该Leader的 ISR 队列
(10)选举新的Leader(以在ISR中存活为前提,按照在AR中排在前面的优先)
(11)更新Leader以及ISR
Kafka 的单播和多播
单播:一条消息只能被同一个消费者组中的一个消费者消费;
多播:一条消息能被多个消费者组中的消费者共同消费
生产者消息为什么分区
(1)可以提高并发,因为可以以 Partition 为单位读写了。(分区也是 Kafka 高效读写数据的主要原因)
(2)对于 Kafka 集群的好处是可以实现负载均衡
生产者分区策略
分区的原因
方便在集群中扩展,每个
Partition可以通过调整以适应它所在的机器,而一个 topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了;可以 提高并发,因为可以以
Partition为单位读写了。(分区也是 Kafka 高效读写数据的主要原因)
(1)既没有 partition 值又没有 key 值的情况下, kafka 采用 Sticky Partition(黏性分区器),会随机选择一个分区,并 尽可能一直使用该分区,待该分区达到了batchsize大小或者默认发送的时间,kafka 再随机一个分区进行使用,只要与上一个分区不同即可。
(2)没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值;
(3)指明 partition 的情况下,直接将指明的值直接作为 partiton 值;
(4)自定义分区器:实现 Partitioner 接口,重写 partition 方法
Kafka分区分配的概念?
一个 consumer group 中有多个 consumer,一个 topic 有多个 partition,所以必然会涉及到 partition 的分配问题,即确定那个 partition 由哪个 consumer 来消费。
消费者分区分配策略
Kafka 有三种分配策略,Range(范围分区) ,RoundRobin(轮询分区),Sticky(粘性分区)。
Range:(针对每个 topic 而言的)首先对同一个 topic 里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序,然后用分区数除以消费者数,得到每个消费者消费几个 partition,然后按分区顺序连续分配若干 partition,除不尽的话前面几个消费者会多分配一个分区的数据。- 问题:如果只是针对 1个 topic,消费者 0 多消费一个分区影响不大;但是如果有 N个 topic,那么消费者 0 就会多消费 N 个分区,那么就容易发生数据倾斜。(该分配策略适用于topic少的情况)
- 再平衡:挂掉了一个消费者之后,45 秒以内重新发送消息,此时剩余的消费者暂时不能消费到挂掉的消费者应该消费的分区,等到了45 秒以后,消费者就真正的挂掉了,此时会把它应该消费的分区数都分配给某个消费者
RoundRobin:(针对所有 topic) 首先将所有 partition 和 consumer 都列出来,然后按照 hashcode 排序,最后通过轮询算法来分配 partition 给到各个消费者。- 再平衡:进行轮询
Sticky:尽量均匀的分配分区给消费者(随机),黏性体现在在执行新的分配之前,考虑上一次的分配结果,尽量少的变动,这样就可以节省大量的开销。- 再平衡:均匀分配
Kafka 分区数可以增加或减少吗
我们可以使用 bin/kafka-topics.sh 命令对 Kafka 增加 Kafka 的分区数据,但是 Kafka 不支持减少分区数。
Kafka 分区数据不支持减少是由很多原因的,比如减少的分区其数据放到哪里去?是删除,还是保留?删除的话,那么这些没消费的消息不就丢了。如果保留这些消息如何放到其他分区里面?追加到其他分区后面的话那么就破坏了 Kafka 单个分区的有序性。如果要保证删除分区数据插入到其他分区保证有序性,那么实现起来逻辑就会非常复杂。
kafka 如果有台机器挂掉会发生什么
(1)follower故障
follower发生故障后会被临时踢出ISR,待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步。等该follower的LEO大于等于该Partition的HW,即follower追上leader之后,就可以重新加入ISR了。
(2)leader故障
leader发生故障之后,会从ISR中选出一个新的leader,之后,为保证多个副本之间的数据一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。
Kafka在哪些地方会有选举过程,使用什么工具支持选举?
Kafka中Producer,Broker,Cousumer的关系
一条消息从生产到消费,可以划分三个阶段:
- 生产阶段:Producer 创建消息,并通过网络发送给 Broker。
- 存储阶段:Broker 收到消息并存储,如果是集群,还要同步副本给其他 Broker。
- 消费阶段:Consumer 向 Broker 请求消息,Broker 通过网络传输给 Consumer。
Kafka 怎么保证数据不丢失 / 可靠性 ***
一条消息从生产到消费,可以划分三个阶段:
- 生产阶段:Producer 创建消息,并通过网络发送给 Broker。
- 存储阶段:Broker 收到消息并存储,如果是集群,还要同步副本给其他 Broker。
- 消费阶段:Consumer 向 Broker 请求消息,Broker 通过网络传输给 Consumer。
这三个阶段都可能丢失数据,所以要保证消息丢失,就需要任意一环都保证可靠。
生产阶段
- ACK机制
- ack=0、ack=1 都有丢失数据的风险。
- ack=all/-1 意味着会等待所有同步副本都收到消息。再结合 min.insync.replicas ,就可以决定在得到确认响应前,至少有多少副本能够收到消息。
存储阶段
- 每个 broker 中的 partition 我们一般都会设置有 replication (副本),的个数,生产者写入的时候首先根据分区分配策略(有 partition 按 partition,有 key 按 key,都没有轮询)写入到 leader 中,follower(副本) 再跟 leader 同步数据,这样有了备份,也可以保证消息数据的不丢失。
消费阶段
- 消费者消费数据的时候会不断提交 ofset,就是消费数据的偏移量,以免挂了,下次可以从上次消费结束的位置继续消费。
Kafka解决两个客户端消费数据的问题
在Kafka中,同一个消费者组内的消费者可以共同消费一个或多个分区的数据。这种方式被称为分区分配。Kafka通过负载均衡来实现分区分配,确保一个分区只由一个消费者组内的消费者来消费。如果在同一个消费者组内有多个消费者,每个消费者可以独立地读取分区数据,并且每个分区只会被分配给一个消费者。这样就能够实现分布式消费数据的效果,同时也保证了消费者读取到的数据是有序的。如果一个消费者挂掉了,那么它所消费的分区将会重新分配给其他消费者,确保数据的高可靠性和可用性。
Kafka分区多副本机制?
Kafka为分区引入了多副本(Replica)机制,通过增加副本数量可以提升容灾能力。同一分区的不同副本中保存的是相同消息(在同一时刻,副本之前并非完全一样),副本之间是“一主多从”的关系,其中leader副本负责处理读写请求,follower副本只负责与leader副本的消息同步。副本处于不同的broker中,当leader副本出现故障时,从follower副本中重新选举新的leader副本对外提供服务。Kafka通过多副本机制实现了故障的自动转义,当Kafka集群中某个broker失效时扔然能够保证服务可用。
Kafka数据的一致性如何保证 ***
不论是旧的Leader还是新选举产生的Leader,Consumer都能读到一样的数据,Kafka是通过引入HW(High Water Mark)机制来保证数据一致性。
每个副本都有一个HW和一个LEO,LEO是每个副本最大的 offset,HW 则是所有副本中最小的LEO,即该Partition对外服务的最大 offset。
Kafka一个生产者可以把消息发到多个分区吗?
支持将不同消息发往多个分区,但是同一消息只能发往某一分区。
Kafka集群为什么挂掉一个broker后还能工作
Kafka使用了副本机制,每个分区的数据会被复制到多个broker上,即使某个broker挂掉,其他broker上的副本也能继续服务,确保了整个集群的可用性。
Kafka设置ack=-1时一定会保证消息不丢失吗
与ack=1或0相比,当其他环境相同的情况下,ack设置为-1/all可以达到最强的可靠性,但这并不意味这消息就一定可靠,因为ISR中可能只有Leader副本,这就成了ack=1的情况,所有此时是不安全。
Kafka的Broker端提供了一个参数min.insync.replicas,该参数控制的是消息至少被写入到多少个副本才算是"真正写入",该值默认值为1,生产环境设定为一个大于1的值可以提升消息的持久性,因为如果同步副本的数量低于该配置值,则生产者会收到错误响应,从而确保消息不丢失。
所以想要获得更高的可靠性,需要配合min.insync.replicas等参数的联动,才能尽量保证消息不丢失。
Kafka Follower如何与Leader同步数据
为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后,都需要向producer发送ack,如果producer收到ack,就会进行下一轮的发送,否则重新发送数据。
Kafka日志保存时间
默认7天
Kafka单条日志的传输大小
默认为单条消息最大值是 1M,但是在我们应用场景中,常常会出现一条消息大于 1M 的情况,如果不对 kafka 进行配置,则会出现生产者无法将消息推送到 kafka 中或消费者消费不到 kafka 里面的数据的情况。我们可以配置两个参数:一个是副本的最大值,一个是单条消息的最大值,来解决消息最大限制的问题。
- message.max.bytes=1M:这个参数用来设置单条消息的最大大小
- replica.fetch.max.bytes=1M:这个参数用来设置副本同步单条消息的最大大小
Kafka消息的消费方式?
Pull 模式。又针对该模式的缺点提出了 timeout 机制
(1)push模式:以消费者最低的消费速率来推送
- 缺点:可能会造成资源的浪费
- 优点:每次推送一定是有新消息的
(2)pull模式:消费者主动拉取消息,拉取时是不知道有没有新消息的 - 缺点:如果Kafka没有数据,消费者可能会陷入循环中,一直返回空数据
- 优点:可以根据消费者的消费能力以适应当前的速率消费消息。
Kafka消息存储机制
kafka 中的消息就是 topic,topic 只是逻辑上的概念,而 partition 才是物理上的概念,每个 partition 对应一个 log 文件,它存储的就是 producer 生产的数据。生产者生产的数据会不断追加到 log 文件中,如果 log 文件很大了,就会导致定位数据变慢。因此 kafka 采取了分片和索引的·机制,将大的 log 文件分为多个 segment,每个 segment 会对应.log 文件和.index 文件和.timeindex 文件,.log 存储数据,.index 存储偏移量索引信息,.timeindex 存储时间戳索引信息。
.index 为稀疏索引,大约每往 log 文件写入 4kb 数据,会往 index 文件写入一条索引。
Index 文件中保存的 offset 为相对 offset,这样能确保 offset 的值所占空间不会过大
Kafka 的 ISR、OSR 和 AR
ISR(InSyncRepli):内部副本同步队列;
OSR(OutSyncRepli):外部副本同步队列;
AR(AllRepli):所有副本,AR = ISR + OSR;
任意一个超过延迟时间阈值的 follower 都会被剔除出 ISR,存入 OSR 列表,新加入的 follower 也会先存放在OSR中。
说下 Kafka 的 ISR 机制
为避免因某个副本的 follower 故障不能同步数据而造成 leader 一直等待,leader 维护了一个列表,为保持同 leader 同步的 follower 集合,该集合中副本和 leader 相差数据不超过一个阈值(可设置),超出一定时间数据未达到该阈值,则 follower 会被踢出 ISR。ISR 主要在 leader 出现故障后作为选举新 leader 的备用池。
Kafka 的 ack 有几种值 / ack 机制
Kafka为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择以下的 acks 参数配置:
- 0:这一操作提供了一个最低的延迟,partition的 leader 接收到消息还没有写入磁盘就已经返回 ack,当 leader 故障时有可能丢失数据;
- 1: partition的 leader 落盘成功后返回ack,如果在follower同步成功之前 leader 故障,那么将会丢失数据;
- -1(all): partition 的 leader 和 follower 全部落盘成功后才返回 ack。但是如果broker发送 ack 之前,leader 发生故障,那么会造成数据重复。
Kafka的 offset 管理
Kafka 0.9 版本之前,consumer 默认将 offset 保存在 Zookeeper 中,从 0.9 版本开始,consumer 默认将 offset 保存在 Kafka 一个内置的 topic 中,该 topic 为__consumer_offsets
消费者提交消费位移时提交的是当前消费到的最新消息的offset还是offset+1?
offset+1
如果有一条offset对应的数据,消费完成之后,手动提交失败,如何处理?
可以给offset提交设置失败后重复提交,如果依旧提交失败,就要进行人工干预了
Kafka为什么同一个消费者组的消费者不能消费相同的分区?
因为这样可能会消费到重复的消息,因为 kafka 的 log 文件对应的数据都会存储自己的偏移量,而它是按照消费者组,主题,分区来进行区分的,那么同一个消费者组中的消费者使用的就是同一份偏移量,这样就很容易消费到重复的消息。
kafka的offset不是为单个消费者存的,是为消费者组存的。
正在消费一条数据,Kafka挂了,重启以后,消费的offset是哪一个
当Kafka服务器挂掉时,消费者正在消费的offset会保存在消费者所在的客户端内存中,并且会定期定时地将消费的offset提交到Kafka集群中。如果此时Kafka服务器挂掉,那么消费者无法将消费的offset提交到Kafka集群中,此时消费者的offset并没有被记录到Kafka集群中,也就是说,在Kafka服务器重启后,消费者的offset会被重置到上一次提交的offset位置,而不是当前正在消费的offset位置。
为什么需要消费者组
消费者组的好处:
- 消费效率更高
- 消费模式灵活
- 便于故障容灾
有哪些情形会造成重复消费
消费者消费后没有提交offset(程序崩溃/强行kill/消费耗时/自动提交偏移情况下unsubscrible)。
有哪些情形会造成消息漏消费
消费者没有处理完消息就提交offset(自动提交偏移 未处理情况下程序异常结束)。
当你使用kafka-topics.sh创建(删除)了一个topic之后,Kafka背后会执行什么逻辑?
创建
(1)会在zookeeper中的/brokers/topics节点下创建一个新的topic节点,如:/brokers/topics/first
(2)触发Controller的监听程序
(3)kafka Controller 负责topic的创建工作,并更新metadata cache
删除
调用脚本删除topic会在zk上将topic设置待删除标志,kafka后台有定时的线程会扫描所有需要删除的topic进行删除,也可以设置一个配置server.properties的delete.topic.enable=true直接删除
Kafka读取消息 Pull 模式的优缺点
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新