Kafka 重平衡
重平衡本质上是一种协议,规定了 消费者组下的所有消费者,按照什么策略消费 Topic
就是 给消费组 中的每一个消费者分配消费 任务的过程。
重平衡的发生在启动一个消费者组前,但是在某些情况下,会正在运行消费的时,再次发生,可能会导致整个集群的暂时性的瘫痪,影响kafka的高可用。
重平衡的发生时机
订阅主题数发生变化,这种一般发生在业务改变,数据一定变化
主题的分区发生变化, 启动集群前设置分区数, 之后调节,也是人为调节,可以在半夜
消费端消费组成员的变化, 这个原因产生较大影响,消费者处理消息超时,Kafka集群配置的 max.poll.interval.ms 的值,那么该消费者将会自动离组. 心跳超时,如果消费者在指定的session.timeout.ms时间内没有汇报心跳,
那么Kafka就会认为该消费已经dead了
消费者重平衡流程
例如: 一个消费者请求加入组
首先该消费者向 协调者 coordinate 发送 joinGroup 请求,包含自己要定阅的主题, 协调者 会将请求者的消息发送 给 leader 请求做出 分配。
然后leader 向协调者发出 SyncGroup 请求,将分配方案发送给协调者,其他节点都会向协调者发送SyncGroup请求,只不过没有分配方案。
最后协调者统一以 SyncGroup 的响应发送给所有成员。
Kafka 的 副本备份
kafka 采用的是同步和异步共同优点的备份策略,即将leader 的所有 follower 进行同步完毕才返回,ack. 只不过这个全部的副本是指的是 在 ISR 队列中的副本。
ISR 队列,是指 follower 副本存活且和 zookeeper 保持连接,同时其响应时间 较快。不满足条件的会被踢出去,满足的会被加入。
HW 高水位,表明 所有副本都同步到的 offset ,所有分区的最小offset ,那么 leader 也向 消费者提供的 HW.
LEO 每一个分区上的最新(大) offset
kafka采取同步和异步的共同优点,所以使用ISR的方法。把Follow中同步慢的节点从ISR中进行T除,从而保证了复制数据的速度。如果leader副本宕机,那么从ISR中选举出来新的leader副本。因为follow副本中都有记录HW。这样也会减少数据的丢失。Follow副本能够从leader中批量的读取数据并批量写入,从而减少了I/0的开销。
kafka 处理请求方案
kafka 处理请求 类似于 Reactor 模式。
网络线程负责接受 请求, 然后将请求放入共享的请求队列中。
broker 有个 IO线程池, 负责从共享队列中取出请求, 执行真正的处理, 如果是 produce ,将消息写入底层磁盘的日志中, 如果是 fetch ,则从磁盘读取消息。
当 IO线程 处理完请求,将生成的响应 发送到 网络 线程池的响应队列中
请求队列是所有网络线程共享的,而响应队列是每个网络线程专属的
Purgatory组件用于缓存延时请求
如acks=all的PRODUCE请求,必须等待ISR中所有副本都接收消息后才能返回
此时处理该请求的IO线程必须等待其他Broker的写入结果,当请求不能处理时,就会暂存在Purgatory中
等到条件满足后,IO线程会继续处理该请求,并将Response放入对应网络线程的响应队列中
Kafka将PRODUCE、FETCH这类请求称为数据类请求,把LeaderAndIsr、StopReplica这类请求称为控制类请求
在Kafka 2.3,正式实现了数据类请求和控制类请求的分离(完全拷贝一套组件,实现两类请求的分离)
kafka controller
早期的版本并没有采用 kafka Controller 对分区和副本进行管理,而是依赖于 zookeeper, 每一个 broker 都会在 zookeeper 上为分区和副本注册大量的监听器。这种严重依赖于zookeeper 会有脑裂和 羊群效应。
只有kafka 的 controller 监控 zookeeper , 其他的 node 再和 controller 通信,减少 zookeeper的 压力。
controller 的竞选
在任意时刻,集群中有且仅有一个控制器。
每个broker启动的时候会去尝试去读取zookeeper 中/controller节点的brokerid的值,如果读取到brokerid的值不为-1,则表示已经有其它broker节点成功竞选为控制器,所以当前broker就会放弃竞选;如果Zookeeper中不存在/controller这个节点,或者这个节点中的数据异常,那么就会尝试去创建/controller这个节点,当前broker去创建节点的时候,也有可能其他broker同时去尝试创建这个节点,只有创建成功的那个broker才会成为控制器,而创建失败的broker则表示竞选失败。每个broker都会在内存中保存当前控制器的brokerid值,这个值可以标识为activeControllerId。
controller 的责任
监听partition相关的变化。为Zookeeper中的/admin/reassign_partitions节点注册PartitionReassignmentListener,用来处理分区重分配的动作。为Zookeeper中的/isr_change_notification节点注册IsrChangeNotificetionListener,用来处理ISR集合变更的动作。为Zookeeper中的/admin/preferred-replica-election节点添加PreferredReplicaElectionListener,用来处理优先副本的选举动作。
监听topic相关的变化。为Zookeeper中的/brokers/topics节点添加TopicChangeListener,用来处理topic增减的变化;为Zookeeper中的/admin/delete_topics节点添加TopicDeletionListener,用来处理删除topic的动作。
监听broker相关的变化。为Zookeeper中的/brokers/ids/节点添加BrokerChangeListener,用来处理broker增减的变化。
从Zookeeper中读取获取当前所有与topic、partition以及broker有关的信息并进行相应的管理。对于所有topic所对应的Zookeeper中的/brokers/topics/[topic]节点添加PartitionModificationsListener,用来监听topic中的分区分配变化。
启动并管理分区状态机和副本状态机。
更新集群的元数据信息。
如果参数auto.leader.rebalance.enable设置为true,则还会开启一个名为“auto-leader-rebalance-task”的定时任务来负责维护分区的优先副本的均衡。
生产者压缩算法
kafka 的消息层次分为两层: 消息集合 以及 消息
一个消息集合中包含若干 日志项 , 日志项 才是封锁消息的地方。
kafka 通常不会直接操作具体的一条条消息,它总是在消息集合这个层面上进行写入操作。
压缩可以发生在两个地方,生产者端和broker端。
开启生产者压缩
props.put("compression.type", "gzip");
这样 Producer 启动后生产的每个消息集合都是经 GZIP 压缩过的,故而能很好地节省网络传输带宽以及 Kafka Broker 端的磁盘占用
broker 也可以进行压缩,但是 因为可能会发生预料之外的压缩 / 解压缩操作,通常表现为 Broker 端 CPU 使用率飙升。
Producer 端压缩、Broker 端保持[可能会重新解压/压缩]、Consumer 端解压缩。
Kafka 而言,它们的性能测试结果却出奇得一致,即在吞吐量方面:LZ4 > Snappy > zstd 和 GZIP;
而在压缩比方面,zstd > LZ4 > GZIP > Snappy。
生产者幂等性和事务
目的: 进行retry重试时,只会生成一个消息。
为了实现Producer的幂等性,Kafka引入了Producer ID(即PID)和Sequence Number。
PID。每个新的Producer在初始化的时候会被分配一个唯一的PID,这个PID对用户是不可见的。
Sequence Numbler。(对于每个PID,该Producer发送数据的每个<Topic, Partition>都对应一个从0开始单调递增的Sequence Number。不是offset
实现原理: broker 在缓存中保存 序列号, 对于接受的每一条消息,如果序列号 比 缓存中的大 1 则接受,否则丢弃。但是者只能保证单个生产者对分区的 exactly once 语义。
,kafka事务属性是指一系列的生产者生产消息和消费者提交偏移量的操作在一个事务,或者说是是一个原子操作),同时成功或者失败。
在事务属性之前先引入了生产者幂等性,它的作用为:
生产者多次发送消息可以封装成一个原子操作,要么都成功,要么失败
consumer-transform-producer模式下,因为消费者提交偏移量出现问题,导致在重复消费消息时,生产者重复生产消息。需要将这个模式下消费者提交偏移量操作和生成者一系列生成消息的操作封装成一个原子操作。
事务属性实现前提是幂等性,即在配置事务属性transaction id时,必须还得配置幂等性;但是幂等性是可以独立使用的,不需要依赖事务属性。
kafka 消费者组
消费者组是 kafka 提供的可以扩展且具有容错性的消费者机制。 一个分区,只能被消费者组中的一个消费者进行消费。
当消费者数量多于分区数量时,多于的消费者空闲。
当消费者数量少于分区数量时,一个消费者可能订阅多个分区。
发挥 consumer 最大的效果就是,consumer 数和topic 下的 partitions 数相等。