Kafka入门教程其二 生产与消费详解 Rebalance过程 leader选举过程

本文深入讲解Kafka的生产与消费过程,包括partition分配、Leader选举、多副本同步、消息投递语义等内容,帮助理解Kafka的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 概述

接着上一篇博客,本篇主要介绍Kafka的生产与消费的过程。Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉去指定Topic的消息。

图中有两个topic,topic 0有两个partition,topic 1有一个partition,三副本备份。

2. 生产


创建一条记录,记录中一个要指定对应的topicvaluekeypartition可选。 先序列化,然后按照topic和partition,放进对应的发送队列中。kafka produce都是批量请求,会积攒一批,然后一起发送,不是调send()就进行立刻进行网络发包。

如果partition没填,那么情况会是这样的:

  • key有填
    按照key进行哈希,相同key去一个partition。(如果扩展了partition的数量那么就不能保证了)
  • key没填
    round-robin来选partition

这些要发往同一个partition的请求按照配置,攒一批然后由一个单独的线程一次性发过去。

2.1 partition分配与Leader选举

当存在多副本的情况下,会尽量把多个副本,分配到不同的broker上。kafka会为partition选出一个leader,之后所有该partition的请求,实际操作的都是leader,然后再同步到其他的follower。当一个broker歇菜后,所有leader在该broker上的partition都会重新选举,选出一个leader。(这里不像分布式文件存储系统那样会自动进行复制保持副本数)

然后这里就涉及两个细节:怎么分配partition,怎么选leader。

关于partition的分配,还有leader的选举,总得有个执行者。在kafka中,这个执行者就叫controller。kafka使用zk在broker中选出一个controller,用于partition分配和leader选举。

2.1.1 partition分配
  1. 将所有Broker(假设共n个Broker)和待分配的Partition排序
  2. 将第i个Partition分配到第(i mod n)个Broker上 (这个就是leader)
  3. 将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上
2.1.2 Leader选举

controller会在Zookeeper的/brokers/ids节点上注册Watch,一旦有broker宕机,它就能知道。当broker宕机后,controller就会给受到影响的partition选出新leader。controller从zk 的/brokers/topics/[topic]/partitions/[partition]/state中,读取对应partition的ISR(in-sync replica已同步的副本)列表,选一个出来做leader。

选出leader后,更新zk,然后发送LeaderAndISRRequest给受影响的broker。这里不是使用zk通知,而是直接给broker发送rpc请求。

如果ISR列表是空,那么会根据配置,随便选一个replica做leader,或者干脆这个partition就是歇菜。如果ISR列表的有机器,但是也歇菜了,那么还可以等ISR的机器活过来。

2.2 多副本同步

这里的策略,服务端这边的处理是follower从leader批量拉取数据来同步。但是具体的可靠性,是由生产者来决定的。

生产者生产消息的时候,通过request.required.acks参数来设置数据的可靠性。

ackswhat happen
0which means that the producer never waits for an acknowledgement from the broker.发过去就完事了,不关心broker是否处理成功,可能丢数据。
1which means that the producer gets an acknowledgement after the leader replica has received the data. 当写Leader成功后就返回,其他的replica都是通过fetcher去同步的,所以kafka是异步写,主备切换可能丢数据。
-1which means that the producer gets an acknowledgement after all in-sync replicas have received the data. 要等到isr里所有机器同步成功,才能返回成功,延时取决于最慢的机器。强一致,不会丢数据。

在acks=-1的时候,如果ISR少于min.insync.replicas指定的数目,那么就会返回不可用。

这里ISR列表中的机器是会变化的,根据配置 replica.lag.time.max.ms,多久没同步,就会从ISR列表中剔除。以前还有根据落后多少条消息就踢出ISR,在1.0版本后就去掉了,因为这个值很难取,在高峰的时候很容易出现节点不断的进出ISR列表。

从ISA中选出leader后,follower会从把自己日志中上一个高水位后面的记录去掉,然后去和leader拿新的数据。因为新的leader选出来后,follower上面的数据,可能比新leader多,所以要截取。这里高水位的意思,对于partition和leader,就是所有ISR中都有的最新一条记录。消费者最多只能读到高水位;

从leader的角度来说高水位的更新会延迟一轮,例如写入了一条新消息,ISR中的broker都fetch到了,但是ISR中的broker只有在下一轮的fetch中才能告诉leader。

也正是由于这个高水位延迟一轮,在一些情况下,kafka会出现丢数据和主备数据不一致的情况,0.11开始,使用leader epoch来代替高水位。

3. 消费

订阅topic是以一个消费组来订阅的,一个消费组里面可以有多个消费者。同一个消费组中的两个消费者,不会同时消费一个partition。换句话来说,就是一个partition,只能被消费组里的一个消费者消费,但是可以同时被多个消费组消费。因此,如果消费组内的消费者如果比partition多的话,那么就会有个别消费者一直空闲。

3.1 offset保存

一个消费组消费partition,需要保存offset记录消费到哪,以前保存在zk中,由于zk的写性能不好,以前的解决方法都是consumer每隔一分钟上报一次。这里zk的性能严重影响了消费的速度,而且很容易出现重复消费。

0.10版本后,kafka把这个offset的保存,从zk总剥离,保存在一个名叫__consumeroffsets topic的topic中。写进消息的key由groupid、topic、partition组成,value是偏移量offset。topic配置的清理策略是compact。总是保留最新的key,其余删掉。一般情况下,每个key的offset都是缓存在内存中,查询的时候不用遍历partition,如果没有缓存,第一次就会遍历partition建立缓存,然后查询返回。

确定consumer group位移信息写入__consumers_offsets的哪个partition,具体计算公式:

__consumers_offsets partition =
	           Math.abs(groupId.hashCode() % groupMetadataTopicPartitionCount)   
	//groupMetadataTopicPartitionCount由offsets.topic.num.partitions指定,默认是50个分区。
3.2 分配partition–reblance

生产过程中broker要分配partition,消费过程这里,也要分配partition给消费者。类似broker中选了一个controller出来,消费也要从broker中选一个coordinator,用于分配partition。

下面从顶向下,分别阐述一下

  1. 怎么选coordinator
  2. 交互流程
  3. reblance的流程
3.2.1 选coordinator
  1. 看offset保存在那个partition
  2. 该partition leader所在的broker就是被选定的coordinator

这里我们可以看到,consumer group的coordinator,和保存consumer group offset的partition leader是同一台机器。

3.2.2 交互流程

把coordinator选出来之后,就是要分配了
整个流程是这样的:

  1. consumer启动、或者coordinator宕机了,consumer会任意请求一个broker,发送ConsumerMetadataRequest请求,broker会按照上面说的方法,选出这个consumer对应coordinator的地址。
  2. consumer 发送heartbeat请求给coordinator,返回IllegalGeneration的话,就说明consumer的信息是旧的了,需要重新加入进来,进行reblance。返回成功,那么consumer就从上次分配的partition中继续执行。
3.2.3 reblance流程

Rebalance 发生时,Group 下所有 consumer 实例都会协调在一起共同参与,kafka 能够保证尽量达到最公平的分配。但是 Rebalance 过程对 consumer group 会造成比较严重的影响。在 Rebalance 的过程中 consumer group 下的所有消费者实例都会停止工作,等待 Rebalance 过程完成。

列举一下会reblance的情况:

  • 增加partition
  • 增加消费者
  • 消费者主动关闭
  • 消费者宕机
  • coordinator自己也宕机

Rebalance 过程分为两步:Join 和 Sync。

Join 顾名思义就是加入组。这一步中,所有成员都向coordinator发送JoinGroup请求,请求加入消费组。一旦所有成员都发送了JoinGroup请求,coordinator会从中选择一个consumer担任leader的角色,并把组成员信息以及订阅信息发给leader——注意leader和coordinator不是一个概念。leader负责消费分配方案的制定。

Sync,这一步leader开始分配消费方案,即哪个consumer负责消费哪些topic的哪些partition。一旦完成分配,leader会将这个方案封装进SyncGroup请求中发给coordinator,非leader也会发SyncGroup请求,只是内容为空。coordinator接收到分配方案之后会把方案塞进SyncGroup的response中发给各个consumer。这样组内的所有成员就都知道自己应该消费哪些分区了。

综合来讲:

  1. consumer给coordinator发送JoinGroupRequest请求。

  2. 这时其他consumer发heartbeat请求过来时,coordinator会告诉他们,要reblance了。

  3. 其他consumer发送JoinGroupRequest请求。

  4. 所有记录在册的consumer都发了JoinGroupRequest请求之后,coordinator就会在这里consumer中随便选一个leader。然后回JoinGroupRespone,这会告诉consumer你是follower还是leader,对于leader,还会把follower的信息带给它,让它根据这些信息去分配partition

  5. consumer向coordinator发送SyncGroupRequest,其中leader的SyncGroupRequest会包含分配的情况。

  6. coordinator回包,把分配的情况告诉consumer,包括leader。
    当partition或者消费者的数量发生变化时,都得进行reblance。

4. 消息投递语义

kafka支持3种消息投递语义

  • At most once:最多一次,消息可能会丢失,但不会重复
  • At least once:最少一次,消息不会丢失,可能会重复
  • Exactly once:只且一次,消息不丢失不重复,只且消费一次(0.11中实现,仅限于下游也是kafka)

在业务中,常常都是使用At least once的模型,如果需要可重入的话,往往是业务自己实现。

4.1 At least once

先获取数据,再进行业务处理,业务处理成功后commit offset。

  1. 生产者生产消息异常,消息是否成功写入不确定,重做,可能写入重复的消息
  2. 消费者处理消息,业务处理成功后,更新offset失败,消费者重启的话,会重复消费
4.2 At most once

先获取数据,再commit offset,最后进行业务处理。

  1. 生产者生产消息异常,不管,生产下一个消息,消息就丢了
  2. 消费者处理消息,先更新offset,再做业务处理,做业务处理失败,消费者重启,消息就丢了
4.3 Exactly once

思路是这样的,首先要保证消息不丢,再去保证不重复。所以盯着At least once的原因来搞。 首先想出来的:

生产者重做导致重复写入消息----生产保证幂等性
消费者重复消费—消灭重复消费,或者业务接口保证幂等性重复消费也没问题

由于业务接口是否幂等,不是kafka能保证的,所以kafka这里提供的exactly once是有限制的,消费者的下游也必须是kafka。所以一下讨论的,没特殊说明,消费者的下游系统都是kafka(注:使用kafka conector,它对部分系统做了适配,实现了exactly once)。
生产者幂等性好做,没啥问题。
解决重复消费有两个方法:

  1. 下游系统保证幂等性,重复消费也不会导致多条记录。
  2. 把commit offset和业务处理绑定成一个事务。

本来exactly once实现第1点就ok了。
但是在一些使用场景下,我们的数据源可能是多个topic,处理后输出到多个topic,这时我们会希望输出时要么全部成功,要么全部失败。这就需要实现事务性。既然要做事务,那么干脆把重复消费的问题从根源上解决,把commit offset和输出到其他topic绑定成一个事务。

Ref

  1. https://www.jianshu.com/p/d3e963ff8b70
  2. https://www.cnblogs.com/yoke/p/11405397.html
<think>我们有两个问题需要回答:1.KafkaRebalance机制详解2.消费者如何从上次消费的offset继续消费消费流程offset恢复)根据用户提供的引用,我们可以整合信息来回答。首先,关于Rebalance机制:引用[1][2][3][4][5]都提到了Rebalance机制,尤其是引用[3]详细列举了触发Rebalance的条件。其次,关于offset恢复:在之前的回答中,我们已经解释了offset存储在`__consumer_offsets`主题中,消费者重启后会从该主题获取上次提交的offset继续消费。而Rebalance过程中,分区的分配会发生变化,但offset的存储消费者组绑定,因此新的消费者会从该组最后提交的offset开始消费。因此,我们可以将回答分为两部分:第一部分:Rebalance机制详解-定义:Rebalance(再平衡)是Kafka消费者组内部分区重新分配的过程。-触发条件(根据引用[3]):1.消费者加入或离开消费者组2.消费者崩溃或失去连接3.主题的分区数量发生变化4.消费者组协调器变更5.消费者组成员发送心跳失败-过程(需要结合引用[4][5]):-当触发条件发生时,消费者组会进入Rebalance状态。-所有消费者停止消费,等待重新分配分区。-组协调器(GroupCoordinator)负责管理再平衡过程选举出组领导者(LeaderConsumer),然后由领导者根据分配策略(如RangeAssignor、RoundRobinAssignor等)为所有成员分配分区,并将分配结果发送给协调器,协调器再分发给所有消费者。-消费者收到新的分区分配后,开始从分配到的分区的offset继续消费。-影响(引用[1][2]):-重复消费:在Rebalance期间,消费者停止消费,但可能已经处理了部分消息但未提交offset,重新分配后新消费者可能从之前的offset开始消费,导致重复消费。-延迟增加:Rebalance期间整个消费者组停止工作,导致消费延迟。-优化(引用[1][2]):-避免频繁的Rebalance:增加`session.timeout.ms`(消费者被认为失效的时间)`heartbeat.interval.ms`(发送心跳的时间间隔)的配置,让消费者有更多时间发送心跳。-减少Rebalance的影响:使用增量RebalanceKafka2.4+支持,即CooperativeStickyAssignor),允许消费者Rebalance期间继续消费未受影响的分区。-确保快速完成Rebalance:减少一次性处理的消息数量(减少`max.poll.records`),避免消费者处理消息时间过长导致心跳超时。第二部分:消费者从上次消费的offset继续消费的流程-在正常情况下(无Rebalance),消费者通过提交offset到`__consumer_offsets`主题来记录消费位置。当消费者重启时,它会从该主题读取该消费者组在该分区的最后提交的offset,然后从这个offset开始消费。-在Rebalance过程中,分区被重新分配给消费者,新的消费者会从该消费者组在该分区最后提交的offset开始消费(即offset恢复)。注意,这里的offset是消费者组级别的,所以即使消费者换了,只要在同一个组,就会从同一个offset继续。因此,整个消费流程包括:1.消费者启动,加入消费者组(可能触发Rebalance)。2.消费者被分配分区(可能是Rebalance的结果)。3.消费者从分配到的分区的最后提交的offset开始拉取消息。4.消费者处理消息,并定期提交offset(自动或手动)。5.如果消费者故障退出,触发Rebalance,其他消费者(或重启后的该消费者)重新分配分区,并重复步骤3。注意:在Rebalance期间,如果消费者处理了消息但还没来得及提交offset,那么这些消息可能会被重新消费(因为offset没有提交,新消费者会从旧的已提交offset开始)。因此,建议在消息处理完成后立即手动提交offset,以减少重复消费。根据以上分析,我们可以组织回答。</think>### Kafka Rebalance 机制详解消费者 Offset 恢复流程 #### 一、Rebalance(再平衡)机制详解 RebalanceKafka 消费者组实现负载均衡的核心机制,当消费者组状态变化时,会触发分区重新分配。以下是关键要点: 1. **触发条件**(引用[3]) - ✅ 消费者加入/离开组(如新增 B2 消费者) - ✅ 消费者崩溃或失联(如 A1 宕机) - ✅ 主题分区数变更(如 topic 分区从 3→5) - ✅ 协调器(Coordinator)变更 - ✅ 心跳超时(默认 10 秒未发送心跳) ```mermaid graph LR A[消费者A1宕机] --> B[协调器检测心跳超时] B --> C[标记消费者失效] C --> D[触发Rebalance] ``` 2. **执行流程**(引用[2][4]) - **阶段1**:所有消费者停止消费,向协调器发送 JoinGroup 请求 - **阶段2**:选举 Leader 消费者,执行分区分配策略(Range/RoundRobin) - **阶段3**:协调器同步分配方案(SyncGroup) - **阶段4**:消费者获取新分配的分区并恢复消费 3. **性能问题优化**(引用[1]) | 问题 | 原因 | 解决方案 | |---|---|---| | 重复消费 | Rebalance 期间 offset 未提交 | 减小 `max.poll.interval.ms` | | 消费延迟 | 全组暂停消费 | 使用增量 RebalanceKafka 2.4+) | | 频繁触发 | 心跳超时敏感 | 调整 `session.timeout.ms=45s` | #### 二、消费者 Offset 恢复机制 当消费者 A1 故障恢复后,其消费流程如下: 1. **Offset 存储原理** - Offset 存储在 Kafka 内部主题 `__consumer_offsets` - 提交方式: - **自动提交**:`enable.auto.commit=true`(默认每 5 秒) - **手动提交**:`consumer.commitSync()`(更可靠) 2. **恢复流程**(以 A1 重启为例) ```mermaid sequenceDiagram A1->>Coordinator: 发送 JoinGroup 请求 Coordinator->>A1: 分配分区 P0 A1->>__consumer_offsets: 查询最后提交的 offset __consumer_offsets-->>A1: 返回 offset=102 A1->>Broker: 从 offset=103 开始拉取消息 ``` 3. **关键配置** ```java // 消费者配置示例 props.put("group.id", "groupA"); props.put("auto.offset.reset", "latest"); // 无offset时策略 props.put("enable.auto.commit", "false"); // 推荐手动提交 ``` 4. **异常处理** - 当 offset 不存在时(如首次启动): - `earliest`:从分区首条消息开始 - `latest`:从最新消息开始(可能丢数据) - 重复消费防护:在业务逻辑中实现幂等性处理 #### 三、完整消费流程 Rebalance 关联 1. **正常消费流程** `拉取消息 → 业务处理 → 提交offset → 周期性心跳` 2. **故障场景联动** - A1 宕机 → 心跳超时 → 触发 Rebalance - B1 接管 A1 的分区 → 从 __consumer_offsets 读取 A 组 offset - A1 恢复后 → 新 Rebalance → A1 重新分配分区并恢复 offset > **最佳实践**:使用手动提交 offset + 合理心跳配置,可减少 70% 的异常 Rebalance(引用[1][4])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值