加载中...

深入理解Kafka的Broker

深入理解Kafka的Broker

Kafka是一种高吞吐量的分布式发布订阅消息系统,它从设计原理方面进行了详尽的考虑,主要表现在以下几个方面。

  1. 高吞吐量,支持大量数据的事件流。
  2. 支持消息数据的可靠传送,能够处理积压的大量数据
  3. 支持低延迟的消息传递。
  4. 支持系统的自动容错。
  5. 通过 Topic的分区,支持消息的分布式处理。

1. 主题与分区

1.1 主题与分区的关系

在Kafka中要实现消息的发送与订阅,必须首先创建Topic。因为Topic是Kafka进行消息归类的基本单元。Topic接收消息生产者发布的消息,并将消息转发给消费者。

Topic 其实是一个逻辑概念,由分区组成。分区则是一个物理概念,一个Topic可以包含多个分区,而一个分区只能属于一个Topic,正因为Kafka采用这样的数据模型,从而实现了消息的分布式管理。

消息被发送到Topic 的时候,实际上是发送给了Topic 中的某一个分区,并且被添加到分区的最后,通过一个偏移量来指定消息的位置。同一个Topic的不同分区中的消息数据是不同的

同时,在创建 Topic 时,还可以指定分区的副本数,通过增加副本数量可以提升容错能力,Kafka通过多副本机制实现了故障的自动转移。

在同一个分区的多个副本中,存在一个 Leader 副本和多个 Follower 副本,它们保存的是相同的消息。其中 Leader 副本负责处理读写请求;Follower 副本只负责与 Leader 副本的消息同步。不同的 Follower 副本可能处于不同的节点上,当 Leader 副本出现了故障,Kafka 会从 Follower 副本中选举一个新的 Leader 副本。

1.2 Topic的管理

对主题进行管理与操作,主要会用到kafka-topics.sh和 kafka-confgs.sh 这两个脚本。

但是有些时候需要将某些管理查看的功能集成到外部系统中。为了支持这样的功能,Kafka也提供了相应的API来直接操作Kafka。我们可以直接使用KafkaAdminClient来进行topic的管理。

2. 消息的持久化

消息由Kafka Broker接收后会进行持久化的操作,以便在消费者不可用的时候保存消息。Apache Kafka的底层依然是基于Java实现的,而Java的磁盘IO操作又存在以下两点问题。

  • 存储缓存对象严重影响性能。
  • 堆内存数据的增加导致Java垃圾回收的速度越来越慢。

虽然传统的磁盘读写操作会很慢,但是磁盘线性写入的性能远远大于随机写入的性能。因为底层的操作系统对磁盘的线性写入进行了大量优化,在某些情况下,磁盘线性甚至比随机的内存读写更快。所以Kafka在进行消息持久化操作时,写日志文件采用的就是磁盘的线性写入方式,从而解决了传统磁盘写操作慢的问题。这里的持久化需要从读和写两个方面来考虑。

  1. 写操作:将数据顺序追加到日志文件中。
  2. 读操作:根据偏移量从日志文件中读取。

Kafka 具有以下优点。

  • 实现了读写分离,数据大小不会对性能产生影响。
  • 硬盘空间相对于内存空间容量限制更小。
  • 访问磁盘采用线性的方式,速度更快、更稳定。

2.1 持久化原理

一个 Topic 被分成多个 Partition,每个 Partition 在存储层面是一个 append-only 日志文件,属于一个 Partition 的消息都会被直接追加到日志文件的尾部,每条消息在文件中的位置称为偏移量。

Kafka日志分为index与log,这两个文件总数成对出现。但是它们存储的信息是不一样的。

  • index文件存储元数据,即索引文件。
  • log文件存储消息,即数据文件。

索引文件指向对应数据文件中消息的偏移量。例如:1,128指的是数据文件的第1条数据,偏移地址为128;而物理地址(在索引文件中指定)+偏移量可以定位消息。

2.2 持久化的读写流程

写流程:

日志文件允许串行追加,并且总是附加到最后一个日志文件的后面。每个日志文件的大小可以由参数log.segment.bytes来指定,其默认值是1GB。当日志文件的大小达到或超过这个值的时候,就会产生一个新的日志文件。每个生成的日志文件称为一个Segment File。日志文件有两个配置参数:M和S。M表示 强制操作系统将日志文件刷新到磁盘之前写入的消息数;S表示 强制操作系统将日志文件刷新到磁盘之前的时间,单位是秒。在系统溃的情况下,最多会丢失M条消息或S秒的数据。

读流程:

Kafka在读取持久化数据的时候,依据给出的消息偏移量和数据块大小来读取数据,并且将读取的数据放入一个缓存区中。如果读取的数据非常大,可以多次读取,每次都将缓冲区加倍,直到成功读取消息为止。在读取数据的时候,需要先确定存储数据的日志文件,即Segment File,再得到数据在Segment File 中的偏移量,然后从此位置开始读取。

2.3 为什么要建立分段和索引

Kafka 为了解决在读取数据时快速定位数据,采用了将数据文件分段并建立索引的形式。每段放在一个单独的数据文件里面,数据文件以该段中最小的 offset 命名。这样在查找指定 offset 的消息数据时,用二分查找法就可以定位到该数据消息在哪个段中,即快速定位数据文件。

为了在一个段内快速定位数据,进一步提高查找的效率,Kafka为每个分段后的数据文件建立了索引文件,文件名与数据文件名是一样的,只是文件扩展名为.index。索引文件中的每条索引表示数据文件中一条消息数据的位置信息。这里需要注意的是,在索引文件中并没有为每条消息数据建立索引,而是每隔一定字节的数据建立一条索引。这样避免索引文件占用过多的空间,从而可以将索引文件保留在内存中。

3. 消息的可靠传输

3.1 生产者的ack机制

当生产者向服务器端分区的Leader副本发送数据时,可以通过acks参数来设置生产者数据可靠性的级别。ask 参数有下面三个取值。

  • 1(默认)。

    这种参数设置意味着生产者在服务器端分区的Leader副本已成功接收到数据并得到确认后发送下一条数据。即只要Leader 副本的分区成功接收到数据,就算生产者成功发送了数据,在这种方式下存在数据丢失的可能性。如果分区Leader副本所在的节点出现了宕机,而分区的Follower 副本还没有完成数据同步,则会丢失数据。

  • (2)0。
    这种参数设置意味着生产者不用等待来自服务器端Broker 的确认,继续发送下一条消息。在这种情况下数据传输效率最高,但是数据可靠性是最低的。因为生产者发送数据后,并不知道服务器端是否成功接收到数据消息。

  • (3)all或者-1。
    这种参数设置意味着生产者发送完消息数据后,需要等待服务器端 Topic 分区的所有副本都完成与 Leader 副本的数据同步后,才算数据消息成功发送。很明显,在这种方式下,Kafka的性能最差,但是其可靠性最高。

3.2 消费者与水位线

生产者将消息成功发送到服务器端后。消费者在消费消息的时候,能够读取到分区中的哪些消息数据呢?

首先,介绍一个名词LEO,它是Log End Offset的缩写,表示Topic分区的每个副本日志中最后一条消息的位置。高水位线(High Watermark)等于Topic 分区中每副本对应的最小的 LEO 值。

假设分区有三个副本。leader副本的leo是6,follower1的leo是4,follower2的leo是3,那么分区的hw就是3,即消费者可以消费的最大位移。

通过这样的机制,当生产者写入新的消息后,消费者是不能够立即消费的。Leader副本会等待该消息被所有副本都同步后,再去更新高水位线的位置,这样消费者才能消费生产者新写入的消息。这样就保证了,如果 Leader 副本所在的 Broker 出现了宕机的情况,Kafka选举出新的 Leader 副本后,该消息仍然可以在重新选举的Leader 副本中获取。

4. leader选举

如果分区存在多个副本,其中一个是 Leader 副本,其他都是 Follower 副本,如果某个分区的 Leader 副本宕机了,那么 Kafka 会自动从其他 Follower 副本中选举一个新的 Leader 副本。之后所有读写就会转移到这个新的 Leader 副本上。

Kafka 不是采用常见的多数选举的方式进行 Leader 副本的选举,而是在ZooKeeper 上针对每个 Topic 维护一个ISR(已同步的副本)列表集合。如果不在 ISR 列表中的副本表示还没有完成与 Leader 副本的同步,也就没有被选举的资格。换句话说,只有这个ISR列表中的副本才有资格成为Leader 副本。

在进行 Leader 副本选举时,首先选举 ISR 中的第一个,如果第一个选举不成功,接着选举第二个,依次类推。因为ISR 中的是同步副本,消息最完整且各个节点都是一样的。Kafka 的选举机制相对比较简单,就是采用ISR 列表的顺序选举的。假设某个 Topic的分区有 N个副本,Kafka 可以容忍 N-1 个 Leader 副本宕机或不可用。还有一点需要说明的是,如果 ISR 列表中的副本都不可用,Kafka 则会从不在ISR 列表的副本中选举一个Leader 副本,这时候就可能导致数据不一致的问题。

5. 配额管理

配额(Quota)是对 Kafka 某种资源的限制。Apache Kafka 配额所能管理和配置的对象有3种。

  • 用户级别:user。
  • 客户端级别:clientid。
  • 用户级别+客户端级别:user+clientid。

这三种配额的管理都是对接入Kafka的生产者和消费者身份的认定方式。其中,客户端级别是每个接入Kafka集群的生产者或消费者的一个身份标志;用户级别只有在开启身份认证的Kafka集群中才有。如果Kaka集群没有开启身份认证,则只能使用客户端级别的方式进行限流。可以使用 kafka-configs.sh 脚本命令来为 Kafka 设定配额。Kafka 配额可配置的选项以及它们的含义如下。

(1)producer_byte_rate :生产者单位时间内可以发布到Kafka集群中单台Broker的字节数(位:秒)。

(2)consumer_byte_rate :消费者在单位时间内可以从Kafka集群中单台Broker拉取的字节数(单
位:秒)。

6. 日志清理

Apache Kafka 是一个基于日志的消息处理系统。一个Topic 可以有若干个 Partition,而分区是数据管理的基本单元。一个分区的数据文件可以存储在若干个独立磁盘的目录中。每个Partition的日志文件存储时又会被分成多个Segment。Segment 是日志清理的基本单元,需要注意的是,当前正在使用的Segment是不会被清理的。

以Segment为单位,每一个Partition分区的日志都会被分为两部分:一部分是已清理的部分;另一部分是未清理的部分。同时未清理的部分又分为可以清理的部分和不可清理的部分。

Kafka通过日志的压缩提供保留较为细粒度的日志记录,这种日志的压缩方式有别于于粗粒度的基于时间的保留。

6.1 日志的删除

Kafka中的每一条数据都包含Key和Value,并且Kafka 通过日志将数据存储在磁盘中。在一般情况下,日志并不会永久保留,否则会占用大量的磁盘空间。在数据达到一定范围或超过一定的时间后,最早写入的数据将会被删除,这就是日志删除策略。这种日志删除策略按照一定的保留策略来直接删除不符合条件的日志分段。

6.2 日志的压缩

日志压缩是另一种清理的方式。它在默认的删除规则之外提供了一种删除过时数据的方式,就是当Key相同,而数据不同时,只保留最后一条数据,前面的数据在合适的情况下被删除。换句话说,这种日志清理的策略是针对每个消息的key进行整合的,对于有相同Key的不同 Value 值,只保留最后一个版本。

6.3 清理的细节

通过服务器端的参数 log.cleanup.policy来设置Kafka 的日志清理策略,此参数默认值“delete”,即采用日志删除的清理策略。如果要采用日志压缩的清理策略,就需要将log.cleanup.policy 设置为compact。需要注意的是,采用日志压缩的清理策略参log.cleaner.enable 需要设定为 true。通过将参数 log.cleanup.policy 设置为 delete和compact还可以同时支持日志删除和日志压缩两种策略。

日志清理的粒度可以控制到 Topic 级别,与参数log.cleanup.policy 对应的主题级别的参数为 cleanup.policy。

当Kafka日志信息需要被清理的时候,Kafka日志管理器中会有一个专门的周期性日志删除任务来检测不符合保留条件的日志文件,并执行相应的删除或压缩操作从而达到日志清理的目的。通过设置服务器端参数log.retention.check.interval.ms来设置这个周期性任务的检查间隔,其默认值为300s,即5min。既然 Kafka要周期性地检査日志的保留条件,那么什么样的日志可以保留下来呢?目
前Kafka支持三种保留策略。下面分别来介绍它们。

  1. 基于时间的保留策略。
    周期性执行的日志删除任务会检查当前日志文件中是否有保留时间超过设定阈值的数据。可以通过服务器端参数log.retention.hours、log.retention.minutes及log.retention.ms 来配置。其中 log.retention.ms 的优先级最高,log.retention.minutes 次之,log.retention.hours 的优先级最低。

  2. 基于日志大小的保留策略

    在该策略下,周期性执行的日志检查任务会检查当前日志的大小是否超过设定的阈值,从而寻找Kafka中可被执行删除的日志分段的文件集合。日志保留的大小阀值可以通过服务器端参数 log.retention.bytes 来设置,其默认值为-1,表示无穷大。参数 log.retention.bytes 配置的是日志文件的总大小,而不是单个的日志分段的大小,一个日志文件包含多个日志分段。

  3. 基于日志起始偏移量的保留策略。
    在一般情况下,日志文件的起始偏移量logStartOffset 等于第一个日志分段的baseOffset,但这并不是绝对的,日志文件的起始偏移量logStartOfset 的值可以通过DeleteRecordsRequest 请求及日志的清理和截断等操作修改。某日志分段的下一个日志分
    的起始偏移量 baseOfset 如果小于等于logStartOfset,则可以删除此日志分段,

7. ZooKeeper的角色定位

在Kaka 的体系架构中需要 ZooKeeper 的支持。Kafka 通过 ZooKeeper 管理集群配置,选举Leader,以及在Consumer Group 发生变化时进行重平衡。

ZooKeeper 用于分布式系统的协调,Kaka使用ZooKeeper 也是基于相同的原因。ZooKeeper 主要用来协调 Kafka 的各个 Broker,不但可以实现 Broker 的负载均衡,而且当增加 Broker 或某个 Broker 故障时,ZooKeeper 会通知生产者和消费者,这样可以保证整个系统正常运转。ZooKeeper 在Kafka 集群中的作用主要体现在以下几个方面。

(1)存储Kafka的元数据

Kafka可以在不更改生产者和消费者配置的条件下,通过使用 ZooKeeper 来实现动态的集群扩展。Kafka Broker 在启动过程中,会把所有元信息(例如,Topic 的信息、分区的信息等)在 ZooKeeper 注册并保持相关的元数据更新。同时,由于 ZooKeeper 提供了监听机制,生产者或消费者程序会在 ZooKeeper 上注册相关的监听器,一旦 ZooKeeper 中记录的元信息发生变化,生产者或消费者能及时感知并进行相应调整。这样就保证了在实现集群的动态扩容或缩容的过程中,各个Broker 间仍能自动实现负载均衡,并能感知集群的变化。同时,ZooKeeper利用监听的机制监听Broker 和Leader 副本的存活性。

(2)管理 Broker。

在 Kafka Broker 启动成功后,会向 ZooKeeper 注册 Broker 的信息,从而实现在服务器正常运行下的水平拓展。同时,当我们成功创建Topic后,ZooKeeper 也会维护 Topic 与Broker 之间的对应关系,这是通过/brokers/topics/topic.name 节点来记录的。

(3)管理消费者。

消费者可以使用 Consumer Group 的形式消费 Kafka 集群中的消息数据。消费者在启动的过程中需要指定一个 Consumer Group 的 ID,这个 ID 会被 ZooKeeper 记录和维护,以保证同一份数据可以被同一个 Consumer Group 的不同消费者多次消费。

同时 ZooKeeper 管理消费者的偏移量用于跟踪当前消费者消费的位置。

(4)ZooKeeper对生产者的意义。

生产者在启动过程中,会向ZooKeeper中注册监听器,从而帮助生产者了解Topic中的分区信息,包括分区的增加、减少、副本的选举等。同时生产者通过动态了解运行情况实现负载均衡。

这里需要说明的是,ZooKeeper并不直接对消息的生产者进行管理。生产者通过监器感知 ZooKeeper 数据的变化。

7.1 具体存储的元数据

存储的数据主要包含以下几个部分。

  • Topic 的注册信息。
  • Topic 配置。
  • 分区的状态信息。
  • Broker 的注册信息。
  • Consumer 的注册信息
  • Consumer owner.
  • Consumer offset.

下面重点介绍其中几个比较主要的目录及其存储的数据:

  • /brokers/topics/[topic].
    该目录用于存储Topic在Broker 上的注册信息。
  • /brokers/topics/[topic]/partitions/[0...N],其中[0...N]表示 partition 索引号
    该目录用于存储 Topic 分区的状态信息。
  • /brokers/ids/[0...N]。
    什么样
    该目录用于存储 Broker 的注册信息。在server.properties中,每个 Broker 都需要指定一个数字类型的id号,这个id号不能重复。这种目录节点其实是ZooKeeper 的临时节点类型。如果Broker出现了宕机,这个目录节点将会自动被删除。
  • /consumers/[groupId]/ids/[consumerld]。
    每创建一个消费者实例,就会在该目录下创建一个consumerld节点,用于跟踪这个消费者实例。
  • /consumers/[groupld]/offsets/[topic]/[partitionld] -> long (offset).
    该目录用来跟踪每个消费者所消费的分区数据中最大的offset。
  • /config/topics/[topic name]。
    该目录用于存储在 Topic中的配置信息。
posted @   strind  阅读(91)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示