目录
2.生产者创建 KafkaProducer 实例时更新元数据
1.Kafka集群元数据信息
// 集群元数据信息
public final class Cluster {
private final boolean isBootstrapConfigured;
// kafka集群的Broker节点列表
private final List<Node> nodes;
// 未授权的topic集合
private final Set<String> unauthorizedTopics;
// 无效的topic集合
private final Set<String> invalidTopics;
// 内部主题集合
private final Set<String> internalTopics;
// Kafka集群的控制节点
private final Node controller;
// partition 的详细信息
private final Map<TopicPartition, PartitionInfo> partitionsByTopicPartition;
// 一个topic下面有多少个partition
private final Map<String, List<PartitionInfo>> partitionsByTopic;
// 一个topic下面有多少个可用的partition
private final Map<String, List<PartitionInfo>> availablePartitionsByTopic;
// 一个Node节点上面的partition列表
private final Map<Integer, List<PartitionInfo>> partitionsByNode;
// node 与 id 的对应关系
private final Map<Integer, Node> nodesById;
private final ClusterResource clusterResource;
}
// TopicPartition: 包含 topic、partition、leader、replicas、isr
public class PartitionInfo {
// 主题
private final String topic;
// 分区编号
private final int partition;
// leader副本,每个分区有且只有一个leader副本
private final Node leader;
// 单个分区的全部副本集合
private final Node[] replicas;
// 单个分区的ISR列表
private final Node[] inSyncReplicas;
// 单个分区的OSR列表
private final Node[] offlineReplicas;
}
2.生产者创建 KafkaProducer 实例时更新元数据
-
初始化 KafkaProducer 对象:当使用
new KafkaProducer<String, String>(props)
创建 KafkaProducer 实例时,构造方法中会调用this.metadata.bootstrap(addresses)
来从 Broker 端拉取元数据到本地。 -
设置核心参数:构造函数中会设置以下参数:
refreshBackoffMs
:更新失败时的重试间隔,默认为 100 毫秒。metadataExpireMs
:本地元数据的过期时间,默认为 5 分钟(300000 毫秒)。metadataIdleMs
:闲置主题的元数据过期时间,默认也为 5 分钟。ClusterResourceListeners
:注册监听器,用于在集群元数据变更时通知客户端。
-
集群元数据信息:
Cluster
类中保存了 Kafka 集群的详细信息,包括 Broker 节点列表、未授权和无效的主题集合、控制节点、分区信息等。 -
PartitionInfo 类:包含主题、分区编号、Leader 副本、所有副本集合、ISR 列表和 OSR 列表。
-
MetadataCache 更新:在 KafkaProducer 对象创建时,会强制更新元数据信息,将目标主题的元数据信息拉取到本地缓存。
3.生产者调用 doSend()
方法发送消息时更新元数据
-
调用
doSend()
方法:在 KafkaProducer 调用send()
方法后,最终会执行doSend()
。 -
检查元数据可用性:通过
waitOnMetadata()
方法判断目标主题的元数据是否可用。如果本地缓存包含且未过期,则直接返回。 -
更新元数据:如果元数据不可用或过期,将进行更新。如果是首次启动或元数据过期,将全量更新;否则,只更新部分需要的
TopicPartition
元数据。 -
阻塞等待:主线程将同步阻塞等待拉取最新元数据,最大等待时间为 60 秒(由
max.block.ms
控制)。 -
唤醒 Sender 子线程:调用
sender.wakeup()
唤醒 Sender 子线程,进而唤醒NetworkClient
线程。 -
拉取最新元数据:通过
NetworkClient
从 Broker 端拉取最新元数据。 -
更新 MetadataCache:Broker 返回最新元数据后,更新本地缓存,包括版本号、最后刷新时间等,并根据需要更新全量或部分元数据信息。
-
通知主线程:拉取成功后,通知主线程,主线程根据最新元数据信息发送数据。
这个过程确保了 KafkaProducer 在发送消息时始终使用最新的元数据,从而保证了消息的正确路由和集群的高可用性。
4.消费者更新元数据的时机
-
初始化时拉取元数据:当 KafkaConsumer 被实例化时,它会在构造方法中调用
this.metadata.bootstrap(addresses)
,向 Broker 端发送请求以拉取当前的元数据信息到本地。 -
订阅主题匹配新主题时:使用正则表达式订阅主题时(例如
consumer.subscribe(Pattern.compile("test-.*"))
),如果新创建的主题与正则表达式匹配,KafkaConsumer 会触发元数据的更新,以包含新主题的相关信息。 -
拉取数据前检查元数据:在 ConsumerCoordinator 调用
poll()
方法进行数据拉取之前,需要检查是否需要刷新最新的元数据。这是因为 TopicPartition 的分区副本可能会经历重新选举 Leader 的过程,新的 Leader 可能位于不同的 Broker 节点上。 -
元数据过期时更新:当订阅的 Topic 的元数据达到过期时间(由
metadataExpireMs
参数控制),KafkaConsumer 会自动拉取最新的元数据信息,以确保消费者能够获取到最新的分区和副本信息。