源码分析Kafka 消息拉取流程

本文详细分析了KafkaConsumer的`poll`方法,包括`updateAssignmentMetadataIfNeeded`和`updateFetchPositions`两个关键步骤,深入探讨了消息拉取流程。首先解释了拉取超时时间的计算,然后介绍了`ConsumerCoordinator#poll`方法,涉及心跳、元数据更新和消费进度提交。接着讲解了`updateFetchPositions`中如何更新拉取位置和处理缺失偏移量。最后,详细阐述了`pollForFetches`方法,包括拉取超时时间调整、消息拉取和重平衡检查。整个过程展示了Kafka消费者如何从broker拉取并处理消息的详细流程。
摘要由CSDN通过智能技术生成

}

// poll for new data until the timeout expires

do {                                       // @4

client.maybeTriggerWakeup(); //@5

if (includeMetadataInTimeout) {                     // @6

if (!updateAssignmentMetadataIfNeeded(timer)) {

return ConsumerRecords.empty();

}

} else {

while (!updateAssignmentMetadataIfNeeded(time.timer(Long.MAX_VALUE))) {

log.warn(“Still waiting for metadata”);

}

}

final Map<TopicPartition, List<ConsumerRecord<K, V>>> records = pollForFetches(timer); // @7

if (!records.isEmpty()) {

if (fetcher.sendFetches() > 0 || client.hasPendingRequests()) { // @8

client.pollNoWakeup();

}

return this.interceptors.onConsume(new ConsumerRecords<>(records)); // @9

}

} while (timer.notExpired());

return ConsumerRecords.empty();

} finally {

release();

}

}

代码@1:首先先对其参数含义进行讲解。

  • boolean includeMetadataInTimeout

拉取消息的超时时间是否包含更新元数据的时间,默认为true,即包含。

代码@2:检查是否可以拉取消息,其主要判断依据如下:

  • KafkaConsumer 是否有其他线程再执行,如果有,则抛出异常,因为 - KafkaConsumer 是线程不安全的,同一时间只能一个线程执行。

  • KafkaConsumer 没有被关闭。

代码@3:如果当前消费者未订阅任何主题或者没有指定队列,则抛出错误,结束本次消息拉取。

代码@4:使用 do while 结构循环拉取消息,直到超时或拉取到消息。

代码@5:避免在禁止禁用wakeup时,有请求想唤醒时则抛出异常,例如在下面的@8时,会禁用wakeup。

代码@6:更新相关元数据,为真正向 broker 发送消息拉取请求做好准备,该方法将在下面详细介绍,现在先简单介绍其核心实现点:

  • 如有必要,先向 broker 端拉取最新的订阅信息(包含消费组内的在线的消费客户端)。

  • 执行已完成(异步提交)的 offset 提交请求的回调函数。

  • 维护与 broker 端的心跳请求,确保不会被“踢出”消费组。

  • 更新元信息。

  • 如果是自动提交消费偏移量,则自动提交偏移量。

  • 更新各个分区下次待拉取的偏移量。

这里会有一个更新元数据是否占用消息拉取的超时时间,默认为 true。

代码@7:调用 pollForFetches 向broker拉取消息,该方法将在下文详细介绍。

代码@8:如果拉取到的消息集合不为空,再返回该批消息之前,如果还有挤压的拉取请求,可以继续发送拉取请求,但此时会禁用warkup,主要的目的是用户在处理消息时,KafkaConsumer 还可以继续向broker 拉取消息。

代码@9:执行消费拦截器。

接下来对上文提到的代码@6、@7进行详细介绍。

1.1 KafkaConsumer updateAssignmentMetadataIfNeeded 详解

KafkaConsumer#updateAssignmentMetadataIfNeeded

boolean updateAssignmentMetadataIfNeeded(final Timer timer) {

if (coordinator != null && !coordinator.poll(timer)) { // @1

return false;

}

return updateFetchPositions(timer); // @2

}

要理解这个方法实现的用途,我们就必须依次对 coordinator.poll 方法与 updateFetchPositions 方法。

1.1.1 ConsumerCoordinator#poll

public boolean poll(Timer timer) {

invokeCompletedOffsetCommitCallbacks(); // @1

if (subscriptions.partitionsAutoAssigned()) { // @2

pollHeartbeat(timer.currentTimeMs()); // @21

if (coordinatorUnknown() && !ensureCoordinatorReady(timer)) { //@22

return false;

}

if (rejoinNeededOrPending()) { // @23

if (subscriptions.hasPatternSubscription()) { // @231

if (this.metadata.timeToAllowUpdate(time.milliseconds()) == 0) {

this.metadata.requestUpdate();

}

if (!client.ensureFreshMetadata(timer)) {

return false;

}

}

if (!ensureActiveGroup(timer)) { // @232

return false;

}

}

} else { // @3

if (metadata.updateRequested() && !client.hasReadyNodes(timer.currentTimeMs())) {

client.awaitMetadataUpdate(timer);

}

}

maybeAutoCommitOffsetsAsync(timer.currentTimeMs()); // @4

return true;

}

代码@1:执行已完成的 offset (消费进度)提交请求的回调函数。

代码@2:队列负载算法为自动分配(即 Kafka 根据消费者个数与分区书动态负载分区)的相关的处理逻辑。其实现关键点如下:

  • 代码@21:更新发送心跳相关的时间,例如heartbeatTimer、sessionTimer、pollTimer 分别代表发送最新发送心跳的时间、会话最新活跃时间、最新拉取消息。

  • 代码@22:如果不存在协调器或协调器已断开连接,则返回 false,结束本次拉取。如果协调器就绪,则继续往下走。

  • 代码@23:判断是否需要触发重平衡,即消费组内的所有消费者重新分配topic中的分区信息,例如元数据发送变化,判断是否需要重新重平衡的关键点如下:

  • 如果队列负载是通过用户指定的,则返回 false,表示无需重平衡。

  • 如果队列是自动负载,topic 队列元数据发生了变化,则需要重平衡。

  • 如果队列是自动负载,订阅关系发生了变化,则需要重平衡。

如果需要重重平衡,则同步更新元数据,此过程会阻塞。详细的重平衡将单独重点介绍,这里暂时不深入展开。

代码@3:用户手动为消费组指定负载的队列的相关处理逻辑,其实现关键如下:

  • 如果需要更新元数据,并且还没有分区准备好,则同步阻塞等待元数据更新完毕。

代码@4:如果开启了自动提交消费进度,并且已到下一次提交时间,则提交。Kafka 消费者可以通过设置属性 enable.auto.commit 来开启自动提交,该参数默认为 true,则默认会每隔 5s 提交一次消费进度,提交间隔可以通过参数 auto.commit.interval.ms 设置。

接下来继续探讨 updateAssignmentMetadataIfNeeded (更新元数据)的第二个步骤,更新拉取位移。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值