NetworkClient#poll 方法执行具体的网络请求和响应。下面来看一下 NetworkClient#poll 方法的具体实现:
public List<ClientResponse> poll(long timeout, long now) {
ensureActive();
if (!abortedSends.isEmpty()) {
// If there are aborted sends because of unsupported version exceptions or disconnects,
// handle them immediately without waiting for Selector#poll.
List<ClientResponse> responses = new ArrayList<>();
handleAbortedSends(responses);
completeResponses(responses);
return responses;
}
/*
* 如果距离上次更新超过指定时间,且存在负载小的目标节点,
* 则创建 MetadataRequest 请求更新本地缓存的集群元数据信息,并在下次执行 poll 操作时一并送出
*/
long metadataTimeout = metadataUpdater.maybeUpdate(now);
try {
/* 发送网络请求 */
this.selector.poll(Utils.min(timeout, metadataTimeout, defaultRequestTimeoutMs));
} catch (IOException e) {
log.error("Unexpected error during I/O", e);
}
// process completed actions
/* 处理服务端响应 */
long updatedNow = this.time.milliseconds();
// 响应队列
List<ClientResponse> responses = new ArrayList<>();
// 对于发送成功且不期望服务端响应的请求,创建本地的响应对象添加到 responses 队列中
handleCompletedSends(responses, updatedNow);
/*
* 获取并解析服务端响应
* - 如果是更新集群元数据对应的响应,则更新本地缓存的集群元数据信息
* - 如果是更新 API 版本的响应,则更新本地缓存的目标节点支持的 API 版本信息
* - 否则,获取 ClientResponse 添加到 responses 队列中
*/
handleCompletedReceives(responses, updatedNow);
// 处理连接断开的请求,构建对应的 ClientResponse 添加到 responses 列表中,并标记需要更新集群元数据信息
handleDisconnections(responses, updatedNow);
// 处理 connections 列表,更新相应节点的连接状态
handleConnections();
// 如果需要更新本地的 API 版本信息,则创建对应的 ApiVersionsRequest 请求,并在下次执行 poll 操作时一并送出
handleInitiateApiVersionRequests(updatedNow);
// 遍历获取 inFlightRequests 中的超时请求,构建对应的 ClientResponse 添加到 responses 列表中,并标记需要更新集群元数据信息
handleTimedOutRequests(responses, updatedNow);
// 遍历处理响应对应的 onComplete 方法
// 本质上就是在调用注册的 RequestCompletionHandler#onComplete 方法
completeResponses(responses);
return responses;
}
更新集群元数据
首先来看更新本地缓存的集群元数据信息的过程( 步骤 1 ),前面曾多次提及到更新集群元数据的场景,而这些更新操作实际上都是标记集群元数据需要更新,真正执行更新的操作则发生在这里。实现位于 DefaultMetadataUpdater#maybeUpdate 方法中:
public long maybeUpdate(long now) {
// should we update our metadata?
// 获取下次更新集群信息的时间戳
long timeToNextMetadataUpdate = metadata.timeToNextUpdate(now);
// 检查是否已经发送了 MetadataRequest 请求
long waitForMetadataFetch = hasFetchInProgress() ? defaultRequestTimeoutMs : 0;
// 计算当前距离下次发送 MetadataRequest 请求的时间差
long metadataTimeout = Math.max(timeToNextMetadataUpdate, waitForMetadataFetch);
if (metadataTimeout > 0) {
// 如果时间还未到,则暂时不更新
return metadataTimeout;
}
// Beware that the behavior of this method and the computation of timeouts for poll() are
// highly dependent on the behavior of leastLoadedNode.
// 寻找负载最小的可用节点,如果没有可用的节点则返回 null
Node node = leastLoadedNode(now);
if (node == null) {
log.debug("Give up sending metadata request since no node is available");
return reconnectBackoffMs;
}
// 检查是否允许向目标节点发送请求,如果允许则创建 MetadataRequest 请求,并在下次执行 poll 操作时一并送出
return maybeUpdate(now, node);
}
private long maybeUpdate(long now, Node node) {
String nodeConnectionId = node.idString();
// 如果允许向该节点发送请求
if (canSendRequest(nodeConnectionId, now)) {
Metadata.MetadataRequestAndVersion requestAndVersion = metadata.newMetadataRequestAndVersion();
// 创建集群元数据请求 MetadataRequest 对象
MetadataRequest.Builder metadataRequest = requestAndVersion.requestBuilder;
log.debug("Sending metadata request {} to node {}", metadataRequest, node);
// 将 MetadataRequest 包装成 ClientRequest 进行发送,在下次执行 poll 操作时一并发送
sendInternalMetadataRequest(metadataRequest, nodeConnectionId, now);
this.inProgressRequestVersion = requestAndVersion.requestVersion;
return defaultRequestTimeoutMs;
}
/* 不允许向目标节点发送请求的场景 *