源码分析Kafka 消息拉取流程,javakafka教程

public ConsumerRecords<K, V> poll(final Duration timeout) { // @1

return poll(time.timer(timeout), true); // @2

}

代码@1:参数为超时时间,使用 java 的 Duration 来定义。

代码@2:调用内部的 poll 方法。

KafkaConsumer#poll

private ConsumerRecords<K, V> poll(final Timer timer, final boolean includeMetadataInTimeout) { // @1

acquireAndEnsureOpen(); // @2

try {

if (this.subscriptions.hasNoSubscriptionOrUserAssignment()) { // @3

throw new IllegalStateException(“Consumer is not subscribed to any topics or assigned any partitions”);

}

// 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 分别代表发送最新发送心跳的时间、

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值