从前面我们看到,生产者在发送数据时,首先是更新元数据,这次分析一下更新元数据的流程
,首先生产者是怎么保存元数据的
元数据结构
Metadata对象
public final class Metadata implements Closeable {
private static final Logger log = LoggerFactory.getLogger(Metadata.class);
public static final long TOPIC_EXPIRY_MS = 5 * 60 * 1000;
private static final long TOPIC_EXPIRY_NEEDS_UPDATE = -1L;
//元数据更新最小的时间间隔,默认100ms,减少网络压力
private final long refreshBackoffMs;
//元数据过期时间,5min
private final long metadataExpireMs;
//元数据版本号,每个元数据都有版本号
private int version;
//上一次更新元数据的时间
private long lastRefreshMs;
//上一次成功更新元数据的时间
private long lastSuccessfulRefreshMs;
private AuthenticationException authenticationException;
//kafka的集群信息
private Cluster cluster;
//是否需要更新元数据
private boolean needUpdate;
/* Topics with expiry time */
private final Map<String, Long> topics;
private final List<Listener> listeners;
private final ClusterResourceListeners clusterResourceListeners;
private boolean needMetadataForAllTopics;
private final boolean allowAutoTopicCreation;
private final boolean topicExpiryEnabled;
private boolean isClosed;
}
Cluster对象
```java
ublic final class Cluster {
private final boolean isBootstrapConfigured;
//kafka集群的节点信息
private final List<Node> nodes;
//未授权的topic
private final Set<String> unauthorizedTopics;
private final Set<String> internalTopics;
private final Node controller;
//topic分区信息
private final Map<TopicPartition, PartitionInfo> partitionsByTopicPartition;
//topic的副本信息
private final Map<String, List<PartitionInfo>> partitionsByTopic;
//topic 可用的Partition信息
private final Map<String, List<PartitionInfo>> availablePartitionsByTopic;
//服务器上有哪些副本
private final Map<Integer, List<PartitionInfo>> partitionsByNode;
//服务器编号与服务器对应的关系
private final Map<Integer, Node> nodesById;
private final ClusterResource clusterResource;
}
Node对象
public class Node {
private static final Node NO_NODE = new Node(-1, "", -1);
//id编号,配置参数执行
private final int id;
private final String idString;
//主机名
private final String host;
//端口号,默认9092
private final int port;
//机加
private final String rack;
}
PartitionInfo对象
public class PartitionInfo {
//主题
private final String topic;
//分区编号
private final int partition;
//leader所在的节点
private final Node leader;
//副本所在的节点
private final Node[] replicas;
//ISR列表
private final Node[] inSyncReplicas;
//离线的副本
private final Node[] offlineReplicas;
}
元数据更新流程
private ClusterAndWaitTime waitOnMetadata(String topic, Integer partition, long maxWaitMs) throws InterruptedException {
// add topic to metadata topic list if it is not there already and reset expiry
metadata.add(topic);
//在元数据中获取集群信息
Cluster cluster = metadata.fetch();
Integer partitionsCount = cluster.partitionCountForTopic(topic);
// Return cached metadata if we have it, and if the record's partition is either undefined
// or within the known partition range
//集群中已经存在分区信息,则直接返回
if (partitionsCount != null && (partition == null || partition < partitionsCount))
return new ClusterAndWaitTime(cluster, 0);
long begin = time.milliseconds();
long remainingWaitMs = maxWaitMs;
long elapsed;
// Issue metadata requests until we have metadata for the topic or maxWaitTimeMs is exceeded.
// In case we already have cached metadata for the topic, but the requested partition is greater
// than expected, issue an update request only once. This is necessary in case the metadata
// is stale and the number of partitions for this topic has increased in the meantime.
do {
log.trace("Requesting metadata update for topic {}.", topic);
metadata.add(topic);
/**
* 是否需要更新元数据的标志更改为true
* 获取当前的元数据版本
*/
int version = metadata.requestUpdate();
//唤醒sender线程拉取元数据
sender.wakeup();
try {
// 等待元数据更新
metadata.awaitUpdate(version, remainin