Kafka知识点总结

什么是Kafka

传统定义:Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。

最新定义:Kafka是 一个开源的分布式事件流平台 (Event Streaming Platform),被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用。

为什么要用消息队列

1. 解耦

允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。

2. 可恢复性

系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理 消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。

3. 缓冲

有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。

4. 灵活性与峰值处理能力

在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理 这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的 访问压力,而不会因为突发的超负荷的请求而完全崩溃。

5. 异步通信

很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入 队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

Kafka 基础架构

(1)Producer:消息生产者,就是向 Kafka broker 发消息的客户端。

(2)Consumer:消息消费者,向 Kafka broker 取消息的客户端。

(3)Consumer Group(CG):消费者组,由多个 consumer 组成。消费者组内每个消 费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不 影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。

(4)Broker:一台 Kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。

(5)Topic:可以理解为一个队列,生产者和消费者面向的都是一个 topic。

(6)Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服 务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。

(7)Replica:副本。一个 topic 的每个分区都有若干个副本,一个 Leader 和若干个 Follower。

(8)Leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数 据的对象都是 Leader。

(9)Follower:每个分区多个副本中的“从”,实时从 Leader 中同步数据,保持和 Leader 数据的同步。Leader 发生故障时,某个 Follower 会成为新的 Leader。

生产者

生产者工作流程

(1)生产者main线程初始化

(2)生产者sender线程初始化

(3)发送数据到缓冲区

(4)sender线程发送数据

生产者分区策略

分区便于合理使用存储资源,每个Partition在一个Broker上存储,可以把海量的数据按照分区切割成一 块一块数据存储在多台Broker上。合理控制分区的任务,可以实现负载均衡的效果。

分区提高并行度,生产者可以以分区为单位发送数据;消费者可以以分区为单位进行消费数据。、

分区策略:

(1)指明 partition 的情况下,直接将指明的值直接作为partiton值。 

(2)没有指明partition值但有key的情况下,将key的hash值与topic的 partition数进行取余得到partition值;

(3)既没有partition值又没有key值的情况下,Kafka采用Sticky Partition(黏性分区器),随机选择单个分区发送所有的无Key消息。一旦这个分区的batch已满或处于“已完成”状态,黏性分区器会随机地选择另一个分区并会尽可能地坚持使用该分区——即所谓的粘住这个分区。这样,一旦我们拉长整个运行时间,消息还是能均匀地发布到各个分区上,避免出现分区倾斜,同时Producer还能降低延时,因为这个分配过程中始终能确保形成较大的batch,而非小batch。

ack应答机制

为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的 数据后,都需要向producer发送ack(acknowledgement确认收到),如果producer收到ack,就会进 行下一轮的发送,否则重新发送数据。Kafka为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择以下的配置。

acks参数配置:

0:producer不等待broker的ack,这一操作提供了一个最低的延迟,broker一接收到还没有写入 磁盘就已经返回,当broker故障时有可能丢失数据。(发送即响应)

1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功 之前leader故障,那么将会丢失数据(默认)

-1(all):producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack。 但是如果在follower同步完成后,broker发送ack之前,leader发生故障,那么会造成数据重复。

数据传递语义

至少一次(At Least Once)= ACK级别设置为-1 + 分区副本大于等于2 + ISR里应答的最小副本数量大于等于2 (保证数据不丢失

最多一次(At Most Once)= ACK级别设置为0 (保证数据不重复

精确一次(Exactly Once)= 幂等性(PID, Partition, SeqNumber) + 至少一次  (保证单分区单会话内数据不重复)

生产者事务

Broker

Broker总体工作流程

副本

AR = ISR + OSR

AR,Kafka 分区中的所有副本

ISR,表示和 Leader 保持同步的 Follower 集合。如果 Follower 长时间未向 Leader 发送通信请求或同步数据,则该 Follower 将被踢出 ISR。该时间阈值由 replica.lag.time.max.ms 参数设定,默认 30s。Leader 发生故障之后,就会从 ISR 中选举新的 Leader。

OSR,表示 Follower 与 Leader 副本同步时,延迟过多的副本

Leader 和 Follower 故障处理细节

 

文件存储机制

Topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于一个log文件,该log文件中存储的就是Producer生产的数 据。Producer生产的数据会被不断追加到该log文件末端,为防止log文件过大导致数据定位效率低下,Kafka采取了分片和索引机制, 将每个partition分为多个segment。每个segment包括:“.index”文件、“.log”文件和.timeindex等文件。这些文件位于一个文件夹下,该 文件夹的命名规则为:topic名称+分区序号,例如:first-0。

 

Log文件和Index文件详解

高效读写数据

1)Kafka 本身是分布式集群,可以采用分区技术,并行度高

2)读数据采用稀疏索引,可以快速定位要消费的数据

3)顺序写磁盘,Kafka 的 producer 生产数据,要写入到 log 文件中,写的过程是一直追加到文件末端, 为顺序写。

4)页缓存 + 零拷贝技术。零拷贝:Kafka的数据加工处理操作交由Kafka生产者和Kafka消费者处理。Kafka Broker应用层不关心存储的数据,所以就不用 走应用层,传输效率高;PageCache页缓存:Kafka重度依赖底层操作系统提供的PageCache功能。当上层有写操作时,操作系统只是将数据写入 PageCache。当读操作发生时,先从PageCache中查找,如果找不到,再去磁盘中读取。实际上PageCache是把尽可能多的空闲内存 都当做了磁盘缓存来使用。

消费者

消费方式

Kafka采用pull消费方式

pull:

根据consumer的消费能力进行数据拉取,可以控制速率

可以批量拉取、也可以单条拉取

可以设置不同的提交方式,实现不同的传输语义

缺点:如果kafka没有数据,会导致consumer空循环,消耗资源

解决:通过参数设置,consumer拉取数据为空或者没有达到一定数量时进行堵塞

push:不会导致consumer循环等待

缺点:速率固定,忽略了consumer的消费能力,可能导致拒绝服务或者网络拥堵等情况

消费者工作流程

 

1)消费者初始化

2)消费者订阅主题

3)消费者拉取和处理数据

4)消费者Offset提交

消费者组

Consumer Group(CG):消费者组,由多个consumer组成。形成一个消费者组的条件,是所有消费者的groupid相同。

• 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费。

• 消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。

分区的分配以及再平衡

1、一个consumer group中有多个consumer组成,一个 topic有多个partition组成,现在的问题是,到底由哪个consumer来消费哪个 partition的数据。

2、Kafka有四种主流的分区分配策略: Range、RoundRobin、Sticky、CooperativeSticky。 可以通过配置参数partition.assignment.strategy,修改分区的分配策略。默认策略是Range + CooperativeSticky。Kafka可以同时使用 多个分区分配策略。

1)Range 分区策略

 再平衡

(1)停止掉 0 号消费者,快速重新发送消息观看结果(45s 以内,越快越好)。

1 号消费者:消费到 3、4 号分区数据。

2 号消费者:消费到 5、6 号分区数据。

0 号消费者的任务会整体被分配到 1 号消费者或者 2 号消费者。

说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需 要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。 (2)再次重新发送消息观看结果(45s 以后)。

1 号消费者:消费到 0、1、2、3 号分区数据。

2 号消费者:消费到 4、5、6 号分区数据。

说明:消费者 0 已经被踢出消费者组,所以重新按照 range 方式分配。

2)RoundRobin 分区策略

 再平衡

(1)停止掉 0 号消费者,快速重新发送消息观看结果(45s 以内,越快越好)。

1 号消费者:消费到 2、5 号分区数据

2 号消费者:消费到 4、1 号分区数据

0 号消费者的任务会按照 RoundRobin 的方式,把数据轮询分成 0 、6 和 3 分区数据, 分别由 1 号消费者或者 2 号消费者消费。

说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需 要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。 (2)再次重新发送消息观看结果(45s 以后)。

1 号消费者:消费到 0、2、4、6 号分区数据

2 号消费者:消费到 1、3、5 号分区数据

说明:消费者 0 已经被踢出消费者组,所以重新按照 RoundRobin 方式分配。

3)Sticky分区策略

粘性分区定义:可以理解为分配的结果带有“粘性的”。即在执行一次新的分配之前, 考虑上一次分配的结果,尽量少的调整分配的变动,可以节省大量的开销。

粘性分区是 Kafka 从 0.11.x 版本开始引入这种分配策略,首先会尽量均衡的放置分区 到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分 区不变化。

再平衡

(1)停止掉 0 号消费者,快速重新发送消息观看结果(45s 以内,越快越好)。

1 号消费者:消费到 2、5、3 号分区数据。

2 号消费者:消费到 4、6 号分区数据。

0 号消费者的任务会按照粘性规则,尽可能均衡的随机分成 0 和 1 号分区数据,分别 由 1 号消费者或者 2 号消费者消费。

说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需 要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。 (2)再次重新发送消息观看结果(45s 以后)。

1 号消费者:消费到 2、3、5 号分区数据。

2 号消费者:消费到 0、1、4、6 号分区数据。

说明:消费者 0 已经被踢出消费者组,所以重新按照粘性方式分配。

4)Cooperative sticky分区策略

和sticky类似只是支持了cooperative协议的rebalance

offset

Kafka0.9版本之前, consumer默认将offset 保存在Zookeeper中

从0.9版本开始,consumer默认将offset保存在Kafka 一个内置的topic中,该topic为__consumer_offsets

__consumer_offsets 主题里面采用 key 和 value 的方式存储数据。key 是 group.id+topic+ 分区号,value 就是当前 offset 的值。每隔一段时间,kafka 内部会对这个 topic 进行 compact,也就是每个 group.id+topic+分区号就保留最新数据。

自动提交offset(默认)

手动提交offset:

commitSync(同步提交):必须等待offset提交完毕,再去消费下一批数据。

commitAsync(异步提交) :发送完提交offset请求后,就开始消费下一批数据了。

消费者事务

如果想完成Consumer端的精准一次性消费,那么需要Kafka消费端将消费过程和提交offset 过程做原子绑定。

此时我们需要将Kafka的offset保存到支持事务的自定义介质(比 如 MySQL)。

数据积压

1.实时消费任务挂掉

比如,我们写的实时应用因为某种原因挂掉了,并且这个任务没有被监控程序监控发现通知相关负责人,负责人又没有写自动拉起任务的脚本进行重启。

那么在我们重新启动这个实时应用进行消费之前,这段时间的消息就会被滞后处理,如果数据量很大,可就不是简单重启应用直接消费就能解决的。

解决办法:

a.任务重新启动后直接消费最新的消息,对于"滞后"的历史数据采用离线程序进行"补漏"。

此外,建议将任务纳入监控体系,当任务出现问题时,及时通知相关负责人处理。当然任务重启脚本也是要有的,还要求实时框架异常处理能力要强,避免数据不规范导致的不能重新拉起任务。

b.任务启动从上次提交offset处开始消费处理

如果积压的数据量很大,需要增加任务的处理能力,比如增加资源,让任务能尽可能的快速消费处理,并赶上消费最新的消息

2.Kafka分区数设置的不合理(太少)和消费者"消费能力"不足

Kafka单分区生产消息的速度qps通常很高,如果消费者因为某些原因(比如受业务逻辑复杂度影响,消费时间会有所不同),就会出现消费滞后的情况。

此外,Kafka分区数是Kafka并行度调优的最小单元,如果Kafka分区数设置的太少,会影响Kafka consumer消费的吞吐量。

解决办法:

增加Topic的Partition的个数,同时提升消费者组的消费者数量。

增加下一级消费者消费速度,即提高每批次拉取的数量

如果利用的是Spark流和Kafka direct approach方式,也可以对KafkaRDD进行repartition重分区,增加并行度处理。

3.Kafka消息的key不均匀,导致分区间数据不均衡

在使用Kafka producer消息时,可以为消息指定key,但是要求key要均匀,否则会出现Kafka分区间数据不均衡。

解决办法:

根据业务,合理修改Producer处的key设置规则,解决数据倾斜问题。

可以在Kafka producer处,给key加随机后缀,使其均衡。

数据有序

前提条件:

1)kafka在1.x版本之前保证数据单分区有序,条件如下: max.in.flight.requests.per.connection=1(不需要考虑是否开启幂等性)。

2)kafka在1.x及以后版本保证数据单分区有序,条件如下:

(1)未开启幂等性 max.in.flight.requests.per.connection需要设置为1。

(2)开启幂等性 max.in.flight.requests.per.connection需要设置小于等于5。

 原因说明:因为在kafka1.x以后,启用幂等后,kafka服务端会缓存producer发来的最近5个request的元数据, 故无论如何,都可以保证最近5个request的数据都是有序的。

3)consumer使用单线程或者保证消费顺序的线程模型

方案一:kafka topic 只设置一个partition分区
方案二:producer将消息发送到指定partition分区,consumer数量与分区数一致
方案三:consumer进行排序
posted @ 2022-03-10 22:13  1243741754  阅读(410)  评论(0编辑  收藏  举报