深入Kafka

深入Kafka

集群成员关系

每个broker都有一个唯一标识符,在broker启动时,通过创建临时节点把自己的ID注册到Zookeeper。Kafka组件订阅Zookeeper的/brokers/ids路径,当有broker加入集群或退出集群时,这些组件可以获得通知。

在broker停机,出现网络分区或长时间垃圾回收停顿时,broker会在Zookeeper上断开连接,此时临时节点自动移除,其他broker能够感知到。在关闭broker时,它对应的节点会消失,不过它的ID会继续存在于其他数据结构中。在完全关闭一个broker之后,如果使用相同的ID启动另一个全新的broker,它会立即加入集群,并拥有与旧broker相同的分区和主题。

控制器

控制器其实就是一个broker,除了一般功能,还负责首领的选举。集群里第一个启动的broker通过在Zookeeper里创建一个临时节点/controller让自己成为控制器。其他broker则会对这个节点创建Zookeeper watch对象,以确保集群里一次只有一个控制器存在。controller epoch可以防止集群脑裂。

当控制器发现一个broker已经离开集群,它就知道,那些失去首领的分区需要一个新首领。控制器遍历这些分区,并确定谁应该成为新首领,然后向所有包含新首领或现有跟随者的broker发送请求。该请求消息包含了谁是新首领以及谁是分区跟随者的信息。随后,新首领开始处理来自生产者和消费者的请求,而跟随者开始从新首领那复制消息。当控制器发现一个broker加入集群时,它会使用brokerID来检查新加入的broker是否含现有分区的副本。如果有,控制器就把变更通知发送给新加入的broker和其他的broker,新broker上的副本开始从首领那复制消息。

复制

首领副本:为了保证一致性,所有生产者请求和消费者请求都会经过这个副本。

跟随者副本:首领以外的副本都是跟随者副本,一个备用的角色。

首领的另一个任务是搞清楚哪个跟随者的状态与自己是一致的。

replica.lag.time.max.mx

参数来配置不活跃时间,当ISR中的follower副本滞后leader副本时间超过此时间则判定同步失败。

首选首领

除了当前首领之外,每个分区都有一个首选首领。auto.leader.rebalance.enable设为true,它会检查首选首领是不是当前首领,如果不是,并且该副本是同步的,那么会触发首领选举,让首选首领成为当前首领。

处理请求

broker的大部分工作是处理客户端、分区副本和控制器发送给分区首领的请求。Kafka提供一个二进制协议,指定了请求消息的格式以及broker如何对请求作出响应。

生产请求:生产者发送的请求,它包含客户端要写入broker的消息。

获取请求:在消费者和跟随者副本需要从broker读取消息时发送的请求。

生产请求和获取请求都必须发送给分区的首领副本。如果broker收到一个针对特定分区的请求,而该分区的首领在另一个broker上,那么发送请求的客户端会收到一个“非分区首领”的错误响应。Kafka客户端要自己负责把生产请求和获取请求发送到正确的broker上。

**客户端如何知道该往哪里发送请求?**元数据请求。请求包含了客户端感兴趣的主题列表,服务端响应消息里指明了这些主题所包含的分区,每个分区都有哪些副本,以及哪个副本是首领。元数据请求可以发送给任意一个broker,因为所有broker都缓存了这些信息。

一般情况下,客户端会把这些信息缓存起来,并直接往目标broker上发送生产请求和获取请求。客户端需要配置metadata.max.age.ms参数来实现定时刷新元数据信息。

生产请求:broker会检查是否有权限,ack是否有效,ack是否达成。

获取请求:客户端指定要读哪个分区从哪个偏移量s开始读,如果偏移量存在,broker将按照客户端指定的数量上限从分区读取消息,再把消息使用零拷贝技术向客户端发送消息。注意,要等到所有同步副本复制了这些消息,才允许消费者读取它们。这意味着,消息到达消费者的时间取决于副本复制完全的时间,可以通过设置replica.lag.time.max.ms来配置副本复制最大允许时间。

其他请求:Kafka最为常见的几种请求类型:元数据请求,生产请求和获取请求。其他的请求比如,把偏移量保存在特定的Kafka主题上,OffsetCommitRequest、OffsetFetchRequest和ListOffsetsRequest。现在,应用程序调用commitOffset()方法时,客户端不再把偏移量写入Zookeeper,而是往Kafka发送OffsetCommitRequest请求。ApiVersionRequest,向broker询问版本的请求。

物理存储

Kafka的基本存储单元是分区。分区无法在多个broker间进行再细分,也无法在同一个broker的多个磁盘再细分。分区的大小受到挂载点可用空间的限制。log.dirs指定了存储分区的目录清单。

分区分配

创建主题时,Kafka首先会决定如何在broker间分配分区

  • 在broker间平均地分布分区副本。
  • 确保每个分区的每个副本分布在不同的broker上。
  • 如果为broker指定了机架信息,那么尽可能把每个分区的副本分配到不同的机架的broker上。
  • 选好合适的broker后,接下来要决定这些分区应该使用哪个目录。我们单独为每个分区分配目录,规则很简单:计算每个目录里的分区数量,新的分区总是被添加到数量最小的那个目录里。
文件管理

Kafka管理员为每个主题配置了数据保留期限,规定数据被删除之前可以保留多长时间,或者清理数据之前可以保留的数据量大小。Kafka将分区分成若干个片段,默认情况下,每个片段包含1GB或一周的数据,以较小的那个为准。在broker往分区写入数据时,如果达到片段上限,就关闭当前文件,打开一个新的文件。当前正在写入数据的片段叫做活跃片段。活动片段永远不会被删除。

文件格式

Kafka的消息和便宜量保存在文件里。保存在磁盘上的数据格式与从生产者发送过来或者发送给消费者的消息格式是一样的。因为格式一致,所以Kafka可以使用零拷贝技术。

除了键、值和偏移量之外,消息里还包含了消息的大小、校验和、消息格式版本号、压缩算法和时间戳。

索引

Kafka为每个分区维护一个索引,索引把偏移量映射到片段文件和偏移量在文件里的位置。

清理

清理时的compact策略,每个日志片段可以分为以下两个部分,干净的部分和污浊的部分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TPegTtJc-1604823643763)(C:\myboot\boot\note\images\kafka日志片段.png)]

每个broker会启动一个清理管理器线程和多个清理线程,它们负责执行清理任务。这些线程会选择污浊率较高的分区进行清理。为了清理分区,清理线程会读取分区的污浊部分,并在内存里创建一个map。map里的每个元素包含了消息键的散列值和消息的偏移量,键的散列值是16B,加上偏移量总共是24B。管理员在配置Kafka可以对所有线程的map使用的总内存大小进行配置,Kafka并不要求分区的的整个污浊部分来适应这个map的大小,但要求至少有一个完整的片段必须符合。如果不符合,那么Kafka就会报错。清理线程在创建好偏移量map后,开始从干净片段读取消息,从最旧的消息开始,把它们的内容与map里的内容进行对比。它会检查消息的键是否存在于map中,如果不存在,那么说明消息的值是最新的,就把消息复制到替换片段上。如果键已存在,消息会被忽略。复制完所有的消息后,我们就将替换片段与原始片段进行交换,然后开始清理下一个片段。

删除事件

为了彻底把一个键从系统里删除,应用程序必须发送一个包含该键且值为null的消息。清理线程发现该消息时,会先进行常规的清理,只保留值为null的消息。该消息(称为墓碑消息)会被保留一段时间,时间长短是可配置的。消费者在这段时间可以看到这个墓碑消息,从而可以去数据库删除这个键(往往是一个用户)的相关数据。一段时间后,清理线程会移除这个墓碑消息,这个键也将从Kafka分区里消失。

何时会清理主题

compact策略也不会对当前片段进行清理,Kafka在包含脏记录的主题数量达到50%时进行清理,这样避免太过频繁的清理,同时避免存在太多的脏记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值