1:NetworkClient类
位置:org.apache.kafka.clients;
作用:用于异步请求/响应网络i/o的网络客户端。这是一个内部类,用于实现面向用户的生产者和消费者客户端,也就是说我们的生产请求和消费请求最后都会交给NetworkClient类处理,调用NetworkClient后交由Selector处理nio请求。
1.1:属性
/* 用于执行网络 io 的选择器 */
private final Selectable selector;
/* Metadata元信息的更新器, 他可以尝试更新元信息 */
private final MetadataUpdater metadataUpdater;
/* 每个节点的连接状态 */
private final ClusterConnectionStates connectionStates;
/* 当前正在发送或等待响应的一组请求,clientRequest请求的暂存区 */
private final InFlightRequests inFlightRequests;
/* 套接字发送缓冲区大小(以字节为单位) */
private final int socketSendBuffer;
/* 套接字接收大小缓冲区(以字节为单位) */
private final int socketReceiveBuffer;
/* 用于在对服务器的请求中识别此客户端的客户端 ID */
private final String clientId;
/* 向服务器发送请求时使用的当前关联 ID*/
private int correlation;
/* 单个请求等待服务器确认的默认超时*/
private final int defaultRequestTimeoutMs;
1.2:方法
1:负责selector初始化,和broker建立建立,用于后续的请求处理
@Override
public boolean ready(Node node, long now) {
if (node.isEmpty())
throw new IllegalArgumentException("Cannot connect to empty node " + node);
if (isReady(node, now))
return true;
if (connectionStates.canConnect(node.idString(), now))
// 用于初始化连接Selector,建立对node连接状态
initiateConnect(node, now);
return false;
}
2:在sender线程中run时调用。用于发送准备好的ClientRequest 请求,producer和consumer都会调用
@Override
public void send(ClientRequest request, long now) {
doSend(request, false, now);
}
private void sendInternalMetadataRequest(MetadataRequest.Builder builder,
String nodeConnectionId, long now) {
ClientRequest clientRequest = newClientRequest(nodeConnectionId, builder, now, true);
doSend(clientRequest, true, now);
}
private void doSend(ClientRequest clientRequest, boolean isInternalRequest, long now) {
/*获取要发送的node id*/
String nodeId = clientRequest.destination();
if (!isInternalRequest) {
// If this request came from outside the NetworkClient, validate
// that we can send data. If the request is internal, we trust
// that internal code has done this validation. Validation
// will be slightly different for some internal requests (for
// example, ApiVersionsRequests can be sent prior to being in
// READY state.)
if (!canSendRequest(nodeId))
throw new IllegalStateException("Attempt to send a request to node " + nodeId + " which is not ready.");
}
/*获取请求体*/
AbstractRequest.Builder<?> builder = clientRequest.requestBuilder();
try {
//做一些版本的管理
NodeApiVersions versionInfo = apiVersions.get(nodeId);
short version;
// Note: if versionInfo is null, we have no server version information. This would be
// the case when sending the initial ApiVersionRequest which fetches the version
// information itself. It is also the case when discoverBrokerVersions is set to false.
if (versionInfo == null) {
version = builder.latestAllowedVersion();
if (discoverBrokerVersions && log.isTraceEnabled())
log.trace("No version information found when sending {} with correlation id {} to node {}. " +
"Assuming version {}.", clientRequest.apiKey(), clientRequest.correlationId(), nodeId, version);
} else {
version = versionInfo.latestUsableVersion(clientRequest.apiKey(), builder.oldestAllowedVersion(),
builder.latestAllowedVersion());
}
// The call to build may also throw UnsupportedVersionException, if there are essential
// fields that cannot be represented in the chosen version.
//发送请求
doSend(clientRequest, isInternalRequest, now, builder.build(version));
} catch (UnsupportedVersionException e) {
//版本异常时处理方式。加到abortedSends
// If the version is not supported, skip sending the request over the wire.
// Instead, simply add it to the local queue of aborted requests.
log.debug("Version mismatch when attempting to send {} with correlation id {} to {}", builder,
clientRequest.correlationId(), clientRequest.destination(), e);
ClientResponse clientResponse = new ClientResponse(clientRequest.makeHeader(builder.latestAllowedVersion()),
clientRequest.callback(), clientRequest.destination(), now, now,
false, e, null);
abortedSends.add(clientResponse);
}
}
3:向selector注册请求,注册 OP_WRITE 事件
private void doSend(ClientRequest clientRequest, boolean isInternalRequest, long now, AbstractRequest request) {
//发送的node
String nodeId = clientRequest.destination();
//生成请求头
RequestHeader header = clientRequest.makeHeader(request.version());
if (log.isDebugEnabled()) {
int latestClientVersion = clientRequest.apiKey().latestVersion();
if (header.apiVersion() == latestClientVersion) {
log.trace("Sending {} {} with correlation id {} to node {}", clientRequest.apiKey(), request,
clientRequest.correlationId(), nodeId);
} else {
log.debug("Using older server API v{} to send {} {} with correlation id {} to node {}",
header.apiVersion(), clientRequest.apiKey(), request, clientRequest.correlationId(), nodeId);
}
}
//将destination以及序列化后的要发送的内容封装到NetworkSend(ByteBufferSend)中。
Send send = request.toSend(nodeId, header);
/*InFlightRequest,请求已经发送,但是还没有得到响应。这样的请求称之为InFlight。*/
InFlightRequest inFlightRequest = new InFlightRequest(
header,
clientRequest.createdTimeMs(),
clientRequest.destination(),
clientRequest.callback(),
clientRequest.expectResponse(),
isInternalRequest,
request,
send,
now);
//将请求添加到inFlightRequests队列中,同时会为 SelectionKey 注册 OP_WRITE 事件,在poll时
this.inFlightRequests.add(inFlightRequest);
//异步发送请求等待相应,发送目的地为Selector,Selector将send和kafkaChannel绑定,开启socket进行写请求
//客户端的请求 Send 会被设置到 KafkaChannel 中,org.apache.kafka.common.network.KafkaChannel 的 TransportLayer
// 会为 SelectionKey 注册 OP_WRITE 事件。此时 Channel 的 SelectionKey 就有了 OP_CONNECT、OP_WRITE 事件,
// 在 Kselector 的轮询过程中当发现这些事件准备就绪后,就开始执行真正的操作。
selector.send(inFlightRequest.send);
}
上面的发送请求的方法,现在就开始处理请求。
@Override
public List<ClientResponse> poll(long timeout, long now) {
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;
}
//元数据更新
long metadataTimeout = metadataUpdater.maybeUpdate(now);
try {
//调用selector,处理真正的读写,如果客户端请求被处理过了,会加到completedReceives集合中
this.selector.poll(Utils.min(timeout, metadataTimeout, requestTimeoutMs));
} catch (IOException e) {
log.error("Unexpected error during I/O", e);
}
// process completed actions
long updatedNow = this.time.milliseconds();
List<ClientResponse> responses = new ArrayList<>();
handleCompletedSends(responses, updatedNow);
handleCompletedReceives(responses, updatedNow);
handleDisconnections(responses, updatedNow);
handleConnections();
handleInitiateApiVersionRequests(updatedNow);
handleTimedOutRequests(responses, updatedNow);
completeResponses(responses);
return responses;
}
private void completeResponses(List<ClientResponse> responses) {
for (ClientResponse response : responses) {
try {
response.onComplete();
} catch (Exception e) {
log.error("Uncaught error in request completion:", e);
}
}
}
NetworkClient最终调用Selector处理 (4.6)client源码——Kafka 网络层实现机制之 Selector 多路复用器