当前位置: 首页 > news >正文

八股文Kafka学习

Kafka 核心概念

体系结构

消息队列 (系统解耦、流量削峰、数据分发),Kafka 可以做消息队列

Kafka 体系结构如下所示:

Kafka 包含若干 Producer、若干 Broker、若干 Consumer 以及一个 Zookeeper 集群。

  • Zookeeper 是 Kafka 用来负责集群元数据管理、控制器选举等操作的。
  • Producer 是负责将消息发送到 Broker 的。
  • Broker 负责接受消息,将消息持久化到磁盘。
  • Consumer 是负责从 Broker 订阅并消费消息。

Broker

  • 一个Kafka的集群通常由多个Broker组成,这样才能实现负载均衡、以及容错
  • Broker是无状态(Sateless)的,它们是通过ZooKeeper来维护集群状态
  • 一个Kafka的Broker每秒可以处理数十万次读写,每个Broker都可以处理TB消息而不影响性能

Zookeeper

  • ZK用来管理和协调 Broker,并且存储了Kafka的元数据(例如:有多少topic、partition、consumer、producer)
  • ZK服务主要用于通知生产者和消费者Kafka集群中有新的Broker加入、或者Kafka集群中出现故障的Broker。

Kafka正在逐步想办法将ZooKeeper剥离,维护两套集群成本较高,社区提出KIP-500就是要替换掉ZooKeeper的依赖。“Kafka on Kafka”——Kafka自己来管理自己的元数据

Topic

Kafka中Topic是一个逻辑概念

  • 所谓发布订阅机制
    • 生产者发布的消息是需要指定一个Topic的,含义即这个消息属于这个“主题”Topic,也可以把“主题”理解为“类别”,生产者就会把消息发送到所指定的Topic。
    • 通常一个Topic中只会专门存储某一类的(消息)信息,比如 Topic(A)专门存储用户观看直播时长信息、Topic(B)专门存储直播PK开始信息。并且在一个Topic中的消息是有固定结构的。
    • 消费者是从它所指定订阅的 “主题”Topic 中去拉取的。
  • 一个主题Topic被分成多个Partition分区

Partition

一个主题Topic被分成多个Partition分区(Topic是逻辑概念,即无实体的)

  • Partition分区 是最小的存储单元,掌握着一个Topic的部分数据。
  • 每个 Partition 分区都是一个单独的 log 文件,每条记录都以追加的形式写入。
  • 一个 Topic 的所有 Partition分区 是分布在多个不同的Broker中的,所有 Partition 分区的数据的并集就是所有数据
  • 一个 Topic 的所有 Partition 放在不同的Broker上,可以提高容错率、提高消息的消费能力。

原因解释:消费者需要消费A类消息,它要去 Broker 中的 Partition分区 (log文件) 获取数据。假设有三个Broker(B1、B2、B3),其上存在 Partition ,如果一个 Topic 的所有 Partition 都存储在 B1 中,此时所有消费者都来 B1 中获取A类消息。如果B1宕机了,又因为只有 B1 上才拥有这个 Topic 的所有Partition,即A类信息只存于B1上,此时这些消费者就无法读取到信息了。但是如果一个 Topic 的Partition 分区是分布在多个不同的 Broker 中的话,那么即使B1宕机了,在B2中也存在这个Topic的Partition,此时消费者可以到B2获取这类信息消费。另外如果把一个Topic的所有Partition都放在一个Broker上,那么这个Topic消息的消费能力将会受限于一个Broker的IO能力

  • 一个 Partition 会生成多个副本Replica,并且把它们分散存储在不同的 Broker 中

原因解释:刚刚解释了为什么一个 Topic 中的所有 Partition 要分布在不同的 Broker 上,但是还有一个问题,因为所有的 Partition 的并集数据才是全量数据,假设某个Broker宕机了,在这个Broker上存在P0分区,此时P0分区所拥有的消息就无法被读取了。且P0分区所拥有的消息是唯一的。因此此时副本就出现了,一个Partition复制多份副本,分散存储在不同的Broker中,此时如果某Broker宕机了,但在其上的P0分区的消息并不是唯一的,在其他未宕机的Broker中有P0分区副本,依然可以获取P0分区的消息。

  • 在Kafka中,一般都会设计副本Replica的个数 > 1

Offset

Offset 的定义
  • Offset 是一个递增的、不可变的数字,当一条记录写入 Partition 的时候,它就被追加到该 Partition 对应的 log 文件的末尾,并被分配一个序号,作为 Offset。即使消息被删除或过期,Offset 也不会改变或重用。
  • Offset 是 Kafka 为每条消息分配的一个唯一的编号,它表示消息在分区中的顺序位置
  • Offset 是从 0 开始的,每当有新的消息写入 Partition 时,Offset 就会加 1。
Offset 的作用
  • offset 的作用主要有两个:
    • 一是用来定位消息。通过指定 offset,消费者可以准确地找到分区中的某条消息,或者从某个位置开始消费消息
    • 二是用来记录消费进度。消费者在消费完一条消息后,需要提交 offset 来告诉 Kafka broker 自己消费到哪里了。这样,如果消费者发生故障或重启,它可以根据保存的 offset 来恢复消费状态。
Offset 的存储
  • 老版本默认Kafka将Offset存储在Zookeeper中
  • Kafka 0.9.0 版本后,offset 的实际存储位置都是在 Kafka 的一个内置主题中:consumer_offsets。这个主题有 50 个分区(可配置),每个分区存储一部分消费组(Consumer Group)的 offset 信息。Kafka broker 会根据消费组 ID 和主题名来计算出一个哈希值,并将其映射到 consumer_offsets 主题的某个分区上。

consumer_offsets 主题是 Kafka 0.9.0 版本引入的新特性,之前的版本是将 offset 存储在 Zookeeper 中。但是 Zookeeper 不适合大量写入,因此后来改为存储在 Kafka 自身中,提高了性能和可靠性。

Offset 的提交和重置

提交 offset 是消费者在消费完一条消息后,将当前消费的 offset 值更新到 Kafka broker 中的操作。提交 offset 的目的是为了记录消费进度,以便在消费者发生故障或重启时,能够从上次消费的位置继续消费。

重置 offset 是消费者在启动或运行过程中,将当前消费的 offset 值修改为其他值的操作。重置 offset 的目的是为了调整消费位置,以便在需要重新消费或跳过某些消息时,能够实现这个需求。

提交 offset

消费者在消费 Kafka 消息时,需要维护

  • 当前消费的 offset 值,表示消费者正在消费的消息的位置,
  • 已提交的 offset 值,表示消费者已经跟Kafka确认了已消费过的消息的位置。

消费者在消费完一条消息后,需要提交 offset 来更新已提交的 offset 值。

提交 offset 的方式有两种:自动提交和手动提交。

  • 自动提交:Kafka 提供了一个配置参数 enable.auto.commit,默认为 true,表示开启自动提交功能。自动提交功能会在后台定期(由 auto.commit.interval.ms 参数控制)将当前消费的 offset 值提交给 Kafka broker。
  • 手动提交:如果 enable.auto.commit 设置为 false,则表示关闭自动提交功能,此时消费者需要手动调用 commitSync 或 commitAsync 方法来提交 offset。手动提交功能可以让消费者更灵活地控制何时以及如何提交 offset。

需要注意的是,无论是自动提交还是手动提交,都不保证提交成功。因为 Kafka broker 可能发生故障或网络延迟,导致提交失败或延迟。因此,消费者需要处理提交失败或延迟的情况。

  • 提交失败:如果提交失败,消费者可以选择重试或放弃。重试的话,可能会导致多次提交同一个 offset 值,但是不会影响正确性,因为 Kafka broker 会忽略重复的 offset 值。放弃的话,可能会导致下次启动时重新消费已经消费过的消息。
  • 提交延迟:如果提交延迟,消费者可以选择等待或继续。等待的话,可能会导致消费速度变慢,或者超过 session.timeout.ms 参数设置的时间而被认为已经死亡。继续的话,可能会导致下次启动时漏掉一些没有提交成功的消息。
重置 offset

重置 offset 的方式有两种:手动重置和自动重置。手动重置是指消费者主动调用 seek 或 seekToBeginning 或 seekToEnd 方法来修改当前消费的 offset 值。自动重置是指消费者在启动时根据 auto.offset.reset 参数来决定从哪个位置开始消费。

  • 手动重置:手动重置可以让消费者精确地控制从哪个位置开始消费。例如,如果想要重新消费某个分区的所有消息,可以调用 seekToBeginning 方法将 offset 设置为 0;如果想要跳过某个分区的所有消息,可以调用 seekToEnd 方法将 offset 设置为最大值;如果想要从某个具体的位置开始消费,可以调用 seek 方法将 offset 设置为任意值。
  • 自动重置:自动重置可以让消费者在启动时根据 auto.offset.reset 参数来决定从哪个位置开始消费。auto.offset.reset 参数有三个可选值:earliest, latest 和 none。earliest 表示从最早的可用消息开始消费;latest 表示从最新的可用消息开始消费;none 表示如果没有可用的 offset,则抛出异常。

消费者组

多个消费者,只要指定了相同的 group_id ,即属于同一个消费者组

同一个消费者组内的消费者可以共同消费一个Topic中的数据,

但是一个Topic中是有很多Partition的,消费者是怎么分配 Partition 的呢?

  • 例如有一个Topic,含有一个Partition,有一个消费者组(有消费者A,B),此时消费者A和消费者B中只有一个消费者可以消费到消息,另外一个消费者将不会消费到消息。这说明当一个消费者组内的消费者数量 > 某 Topic 的 Partition 数量时,多余的消费者是会空闲的
  • 例如有一个Topic,含有两个Partition,有一个消费者组(有消费者A,B),此时消费者A和消费者B会分别单独只消费某一个Partition,A和B不会交叉消费不同Partition。这说明当一个消费者组内的消费者数量 == 某 Topic 的 Partition 数量时,每个消费者对应一个Partition。
  • 例如有一个Topic,含有三个Partition,有一个消费者组(有消费者A,B),此时消费者A和消费者B会有一个消费者消费多个分区。这说明当一个消费者组内的消费者数量 < 某Topic的Partition数量时,部分消费者会消费多个Partition的消息。

不同消费者组也可以共同消费同一个Topic中的数据。

  • 假设有两个消费者组A和B,都订阅了同一个Topic,这时候Topic的某一条消息,消费者组A和消费者组B都可以拉取到。即消费者组A会消费一次,消费者组B也会消费一次。消费组内具体的消费逻辑同上单个消费者组组内消费的逻辑。
Partition和消费组内消费者数量

针对单个消费者组来说,

  • 若消费者数量大于partition数量,会造成闲置的消费者,产生浪费。
  • 若消费者数量小于partition数量,会导致均衡失效,其中的某个或某些消费者会消费更多的任务。

因此,一般消费组内消费者的数量应该等于Partition的数量;

但是如果需要消费的任务压力不大。也可以是第二种情况,即消费者的数量小于Partition数量。

单播消费
  • 让所有消费者处在同一个消费组里,消费组中的多个消费者只有一个消费者可以消费到Partition分区中的消息。一条消息只能被某一个消费者消费。
多播消费
  • 针对Kafka同一条消息只能被同一个消费组下的某一个消费者消费的特性,要实现多播只要保证这些消费者属于不同的消费组即可。
  • 假设有两个消费者组A和B,结果是A消费者组和B消费者组中各有一个消费者成功消费到消息。
  • 多播消费其实是一条消息能被多个消费者 (不同的消费者组) 消费的模式

小贴士:

  • Kafka是以消费者来 “拉”信息的模式工作的,而非“推”模式。消费者主动向 Broker 请求拉取消息。消费者通过 poll() 方法定期轮询 Broker,并根据自身消费能力控制拉取频率和批量大小
  • Kafka中存储的消息是以key、value形式存储的
  • 通常一个Topic中只会专门存储某一类的(消息)信息,并且在一个Topic中的消息是有固定结构的
  • 一个主题Topic中的所有Partition分区是分布在不同的Broker节点上的
  • 请注意Offset的定义和作用
  • 一般一个Topic被一个指定的消费者组消费,组中的消费者数量等于Partition数量。

消息不丢失

背景知识

三种消息语义:

  1. 最多传递一次——对应消息不重复 At most once 高并发情况,对于消息的丢失不是很敏感的场景
  2. 最少传递一次——对应消息不丢失 At Least once 对于消息重复传递不敏感的场景
  3. 仅有一次传递——消息既不丢失也不重复 Exactly once 对于消息可靠性要求高的场景

要保证消息不丢失,从三方面展开

1.生产者 发送消息不丢失

  • 使用带回调方法的Api,在回调函数内可以得知消息是否发送成功。如果发送失败,可以进行异常处理,例如把失败消息存储到本地磁盘或远程数据库。
  • 设置参数 acks=-1,是指当分区收到消息后,且同步到副本分区后,才返回响应。
  • 设置参数retries=3,生产者生产消息的重试次数

2.Broker 保存消息不丢失

  • 设置分区副本的个数>1,当leader副本挂了,还能让follow副本选举为leader副本,继续接收消息

3.消费者 不能少消费消息

  • 设置 enable.auto.commit=false,消费者是否自动提交offset消息偏移量。
  •  如果是自动提交,消费者的后台定时任务自动完成offset提交,业务线程可能没执行完毕此时系统挂了,而offset已经提交了,系统重启再次消费,由于offset已经提交就消费不到之前的消息。
  • 如果设置手动提交,则可以在业务线程完成任务后,手动提交offset,保证不少消费消息。

消息不重复消费

为了保证消息不重复消费

1. 启用 Kafka 生产者幂等性

Kafka 生产者可以通过启用幂等性来避免消息重复发送:

配置:设置 enable.idempotence=true,确保生产者发送的消息具有唯一性。

原理:Kafka 会为每条消息分配一个唯一的序列号(Sequence Number),并在 Broker 端进行去重,从源头避免消息重复。

2. 消费者端实现幂等性/去重

即使生产者启用了幂等性,消费者仍然可能因为某些原因(如消费者重启、偏移量提交失败等)重复消费消息。因此,消费者端需要实现幂等性:确保相同的消息被多次处理时,不会对系统状态产生影响。在消费者端维护一个已处理消息的标识(如消息 ID 或业务主键如订单ID并在处理消息前检查是否已经处理过,例如使用MySQL 存储已处理的消息 ID 或业务主键。

消息顺序消费

消息顺序消费指的是,多个消息需要按照先后顺序依次消费

多个消息 如果写入同一个 Topic 的不同 partition 分区,由于不同 partition 分区消息被消费的速度是不可预知的,所以此时无法保证消息被顺序消费,因此,多个消息,需要写入同一个 Topic 的 同一个 partition 分区,至少在单个分区内,消息是有序的,同时,需要保证每个 partition 分区由一个消费者消费,且是单线程消费,确保 单个分区内的消息按顺序处理。如此,即可确保消息顺序消费。

Rebalance 机制

背景知识:一个消费者组可以订阅多个Topic

Kafka中的Rebalance称之为再均衡

是Kafka中确保消费者组下所有消费者如何达成一致,分配(订阅的Topic的)分区的机制

Rebalance触发的时机有:

  • 消费者组中消费者的个数发生变化,例如有新的消费者加入消费者组,或者某个消费者停止了
  • 消费者组订阅的 Topic 的 Partition 个数发生变化,Partition的个数增加或减少
  • 消费者组订阅的 Topic 个数发生变化,订阅的Topic 个数增加或减少

Rebalance的不良影响

  • 发生Rebalance的时候,消费者组下的所有消费者都会协调在一起共同参与,Kafka使用分配策略,尽可能达到最公平的分配。
  • Rebalance的过程会对消费者组产生非常严重的影响,Rebalance的过程中所有的消费者都将停止工作,直到Rebalance完成。

自动提交offset

Offset:消息位移,它表示分区中每条消息的位置信息,是一个单调递增且不变的值

换句话说,offset可以用来唯一的标识分区中每一条记录

Kafka为了使我们能够专注于自己的业务逻辑,提供了自动提交offset的功能,这也是默认配置项。

当消费者配置 enable.auto.commit=true 时,消费者会在后台启动一个定时任务,每隔 auto.commit.interval.ms(默认 5 秒)自动提交一次 offset自动提交的 offset 存储在 Kafka 的内部系统主题consumer_offsets中

消息堆积

消费者端优化

  1. 消费者消费速度太慢,导致消息积压,可以优化消费者代码逻辑
  2. 当消费者数量少于partition分区数量时,可以增加消费者数量(但不能超过partition的数量)
  3. 当手动提交offset时,检查是否消费者未正确提交 offset,导致消费停滞

生产者端优化

  1. 控制生产速度:例如在生产者端设置限流机制,避免消息生产速度过快

Kafka集群优化

  1. 调整分区数量:根据消息生产和消费速度,合理调整主题的分区数量。如果消息堆积是由于分区数过少导致,可增加分区数。例如,将一个原本只有2个分区的主题,根据业务量增加到10个分区,以提高并行处理能力。但分区数过多也会增加管理开销,需谨慎评估。

消息队列的作用

使用消息队列的主要目的主要记住这三个关键词:解耦、异步、削峰填谷

  1. 解耦:在一个复杂的系统中,不同的模块或服务之间可能需要相互依赖,如果直接使用函数调用或者 API调用的方式,会造成模块之间的耦合,当其中一个模块发生改变时,需要同时修改调用方和被调用方的代码。而使用消息队列作为中间件,不同的模块可以将消息发送到消息队列中,不需要知道具体的接收方是谁,接收方可以独立地消费消息,实现了模块之间的解耦。
  2. 异步:有些操作比较耗时,例如发送邮件、生成报表等,如果使用同步的方式处理,会阻塞主线程或者进程,导致系统的性能下降。而使用消息队列,可以将这些操作封装成消息,放入消息队列中,异步地处理这些操作,不影响主流程的执行,提高了系统的性能和响应速度。
  3. 削峰填谷:削峰填谷是一种在高并发场景下平衡系统压力的技术,通常用于平衡系统在高峰期和低谷期的资源利用率,提高系统的吞吐量和响应速度。在削峰填谷的过程中,通常使用消息队列作为缓冲区,将请求放入消息队列中,然后在系统负载低的时候进行处理。这种方式可以将系统的峰值压力分散到较长的时间段内,减少瞬时压力对系统的影响,从而提高系统的稳定性和可靠性。

消息队列对比 && 技术选型

对比指标

RabbitMQ

RocktMQ

Kafka

优先级队列

支持

支持

不支持

延迟队列

支持延迟队列,可以通过插件或者消息TTL和死信交换来实现

直接支持延迟队列,可设定消息的延迟时间

不支持

死信队列

支持

支持

不支持

重试队列

可以实现重试机制,但需要通过消息属性和额外配置来手动设置

支持重试队列,可以自动或手动将消息重新发送

不确定 (面试时有的面试官说支持,有的面试官说不支持)

消费模式

采用推模式

支持推和拉两种模式

采用拉模式

事务消息

支持

支持

支持

技术选型

在选择消息队列技术时,需要根据实际业务需求和系统特点来选择,以下是一些参考因素:

  1. 性能和吞吐量:如果需要处理海量数据,需要高性能和高吞吐量,那么Kafka是一个不错的选择。
  2. 可靠性:如果需要保证消息传递的可靠性,包括数据不丢失和消息不重复投递,那么RocketMQ和RabbitMQ都提供了较好的可靠性保证。
  3. 消息传递模型:如果需要支持发布-订阅和点对点模型,那么RocketMQ和RabbitMQ是一个不错的选择。如果只需要发布-订阅模型,Kafka则是一个更好的选择。
  4. 消息持久化:如果需要更快地持久化消息,并且支持高效的消息査询,那么Kafka是一个不错的选择。如果需要更加传统的消息持久化方式,那么RocketMQ和RabbitMQ可以满足需求。
  5. 开发和部署复杂度:Kafka比较简单,易于使用和部署,但在实现一些高级功能时需要进行一些复杂的配置。RocketMQ和RabbitMQ提供了更多的功能和选项,也更加灵活,但相应地会增加开发和部署的复杂度。
  6. 社区和生态:Kafka、RocketMQ和RabbitMQ都拥有庞大的社区和完善的生态系统,但Kafka和RocketMQ目前的发展势头比较迅猛,社区活跃度也相对较高。
  7. 实现语言:Kafka是基于Scala和Java开发的,RocketMQ是基于Java语言的,RabbitMQ是基于erlang
  8. 支持功能性:上面列举过一些功能,例如延迟队列、死信队列、事务消息,我们在选型的时候需要看哪个可以满足我们的需求。
  9. 成本:如果是购买云服务,可能不同消息队列的成本不同,也是一个参考标准。

ISR 机制

ISR(In-Sync Replicas)机制定义 是Kafka保证数据一致性的核心机制,由Leader副本(负责读写)和Follower副本(负责备份)组成。‌

ISR集合的定义‌:ISR是指与Leader副本保持同步的Follower副本集合。这些副本已经复制了Leader副本的所有数据,并且它们的落后时间在一定范围内(由replica.lag.time.max.ms参数配置),因此被认为是可靠的、可以用于故障转移和数据恢复的副本。当Follower副本的延迟超过replica.lag.time.max.ms(默认10秒)时,会被移出ISR集合。‌

  • 选举保证节点容灾‌:当Leader副本出现故障时,Kafka会从ISR集合中选举一个新的Leader副本。由于ISR中的副本与之前的Leader副本保持同步,新的Leader副本能够继续提供服务,而不会丢失数据。这确实保证了节点的容灾能力。‌
  • Follower副本保证备份‌:ISR中的Follower副本不仅作为备份存在,它们还积极参与消息的复制过程。当消息被写入Leader副本时,Leader副本会将消息复制给ISR中的所有Follower副本。这样,即使Leader副本出现故障,ISR中的Follower副本也能提供完整的数据备份。‌
  • ISR的动态管理‌:Kafka会动态地管理ISR集合。如果某个Follower副本无法跟上Leader副本的更新速度(即落后时间超过replica.lag.time.max.ms),它将被移出ISR集合。一旦该副本重新追上Leader副本,它将被重新加入ISR集合。这种动态管理机制确保了ISR集合中的副本始终是可靠的。
  • 数据一致性保证:生产者在写入数据时,可以通过设置 acks 参数来控制数据的一致性级别。设置 acks=all(或acks=-1):当消息被写入 Kafka 的分区时,它首先会被写入 Leader,然后 Leader 将消息复制给 ISR 中的所有副本。只有当 ISR 中的所有副本都成功地接收到并确认了消息后,主副本才会认为消息已成功提交。这种机制确保了数据的可靠性和致性。acks 参数 仅支持以下三个值:

取值

等价写法

行为

0

-

不等待确认,直接发送下一条消息。

1

-

等待 Leader 副本确认。

all

-1

等待 ISR 中所有副本确认(需配置 min.insync.replicas 定义最小副本数)。

http://www.xdnf.cn/news/1196857.html

相关文章:

  • 哈希表应用(map,set共同作用)
  • 基于 KNN 算法的手写数字识别项目实践
  • DAY21-二叉树的遍历方式
  • vuhub jangow-01-1.0.1靶场攻略
  • 简易 BMI 身体质量指数计算器
  • C++算法竞赛篇(六)一维数组题型讲解
  • 用哈希表封装Myunordered_map和Myunordered_set
  • mac neo4j install verifcation
  • mac配置多版本jdk
  • Python 列表推导式与生成器表达式
  • 【成功经验分享】Github Education (Github学生认证)认证
  • 数据江湖的“三国演义”:数据仓库、数据湖与湖仓一体的全景对比
  • RAG vs 微调
  • 使用uni-app开发一个点餐收银台系统前端静态项目练习
  • C 语言第 10 天学习笔记:字符串基础操作与相关函数
  • 机器学习特征选择 explanation and illustration of ANOVA
  • java开闭原则 open-closed principle
  • 影刀RPA_初级课程_玩转影刀自动化_网页操作自动化
  • 【机器学习深度学习】NLP评价指标 BLEU 和 ROUGE
  • python优秀案例:基于python flask实现的小说文本数据分析与挖掘系统,包括K-means聚类算法和LDA主题分析
  • 用KNN实现手写数字识别:基于 OpenCV 和 scikit-learn 的实战教学 (超级超级超级简单)
  • Kafka——消费者组消费进度监控都怎么实现?
  • 牛客周赛101 D题 题解
  • 五、搭建springCloudAlibaba2021.1版本分布式微服务-gateway网关
  • 力扣热题100----------53最大子数组和
  • 零基础学习性能测试第五章:Tomcat的性能分析与调优-Tomcat原理,核心配置项,性能瓶颈分析,调优
  • RAG(检索增强生成)
  • 探秘CommonJS:Node.js模块化核心解析
  • redis主从复制、哨兵机制底层原理
  • XML Schema 指示器:全面解析与深度应用