对于KafkaConsumer,我们最后来看一下消费者是符合从服务端获取消息的,它是用Fetcher来实现此功能。Fetcher的主要工作就是发送FetchRequest请求,获取指定的消息集合,处理FetchResponse,更新消费位置。
我们先来看一下它的核心字段:
client:负责网络通信
minBytes:在服务端收到FetchRequest之后并不是立即响应,而是当可返回的消息数据积累到至少minBytes个字节时才进行响应。
maxWaitMs:等待FetchResponse的最长时间,服务端根据此时间决定何时进行响应
fetchSize:每次fetch操作的最大字节数
maxPollRecords:每次获取Record的最大数量
metadata:记录了Kafka集群的元数据
subscriptions:记录每个TopicPartition的消费情况
completedFetches:每个FetchResponse首先会转换成CompletedFetch对象进入此队列缓存,此时并未解析消息。
nextInLineRecords:保存了FetchResponse解析之后的结果集
下面我们先来看创建FetchRequest请求的方法
private Map<Node, FetchRequest> createFetchRequests() {
// 获取集群元数据
Cluster cluster = metadata.fetch();
Map<Node, Map<TopicPartition, FetchRequest.PartitionData>> fetchable = new HashMap<>();
for (TopicPartition partition : fetchablePartitions()) {
//查找当前分区所在的节点
Node node = cluster.leaderFor(partition);
//如果节点为空,需要更i性能元数据
if (node == null) {
metadata.requestUpdate();
//如果没有pend的请求
} else if (this.client.pendingRequestCount(node) == 0) {
Map<TopicPartition, FetchRequest.PartitionData> fetch = fetchable.get(node);
if (fetch == null) {
fetch = new HashMap<>();
fetchable.put(node, fetch);
}
//记录需要fetch的消息的offset
long position = this.subscriptions.position(partition);
fetch.put(partition, new FetchRequest.PartitionData(position, this.fetchSize));
log.trace("Added fetch request for partition {} at offset {}", partition, position);
}
}
Map<Node, FetchRequest> requests = new HashMap<>();
for (Map.Entry<Node, Map<TopicPartition, FetchRequest.PartitionData>> entry : fetchable.entrySet()) {
//创建Request
Node node = entry.getKey();
FetchRequest fetch = new FetchRequest(this.maxWaitMs, this.minBytes, entry.getValue());
requests.put(node, fetch);
}
return requests;
}
总结一下上面这个方法的流程:
1、首先按条件查找fetchable分区
(1)首先是分配个当前消费者的分区
(