publicclassNetworkClientimplementsKafkaClient {
// 状态枚举值privateenumState {
ACTIVE,
CLOSING,
CLOSED
}
/* the selector used to perform network i/o */// 用于执行网络 I/O 的选择器privatefinalSelectableselector;
// Metadata元信息的更新器, 它可以尝试更新元信息privatefinalMetadataUpdatermetadataUpdater;
/* the state of each node's connection */// 管理集群所有节点连接的状态privatefinalClusterConnectionStatesconnectionStates;
/* the set of requests currently being sent or awaiting a response */// 当前正在发送或等待响应的请求集合privatefinalInFlightRequestsinFlightRequests;
/* the socket send buffer size in bytes */// 套接字发送数据的缓冲区的大小(以字节为单位)privatefinalintsocketSendBuffer;
/* the socket receive size buffer in bytes */// 套接字接收数据的缓冲区的大小(以字节为单位)privatefinalintsocketReceiveBuffer;
/* the client id used to identify this client in requests to the server */// 表示客户端id,标识客户端身份privatefinalStringclientId;
/* the current correlation id to use when sending requests to servers */// 向服务器发送请求时使用的当前关联 IDprivateintcorrelation;
/* default timeout for individual requests to await acknowledgement from servers */// 单个请求等待服务器确认的默认超时privatefinalintdefaultRequestTimeoutMs;
/* time in ms to wait before retrying to create connection to a server */// 重连的退避时间privatefinallongreconnectBackoffMs;
/*** True if we should send an ApiVersionRequest when first connecting to a broker.* 是否需要与 Broker 端的版本协调,默认为 true* 如果为 true 当第一次连接到一个 broker 时,应当发送一个 version 的请求,用来得知 broker 的版本, 如果为 false 则不需要发送 version 的请求。*/privatefinalbooleandiscoverBrokerVersions;
// broker 端版本privatefinalApiVersionsapiVersions;
// 存储着要发送的版本请求,key 为 nodeId,value 为构建请求的 BuilderprivatefinalMap<String, ApiVersionsRequest.Builder>nodesNeedingApiVersionsFetch=newHashMap<>();
// 取消的请求集合privatefinalList<ClientResponse>abortedSends=newLinkedList<>();
/*** Begin connecting to the given node, return true if we are already connected and ready to send to that node.** @param node The node to check* @param now The current timestamp* @return True if we are ready to send to the given node*/@Overridepublicbooleanready(Nodenode, longnow) {
// 空节点if (node.isEmpty())
thrownewIllegalArgumentException("Cannot connect to empty node "+node);
// 1、判断节点是否准备好发送请求if (isReady(node, now))
returntrue;
// 2、判断节点连接状态if (connectionStates.canConnect(node.idString(), now))
// if we are interested in sending to a node and we don't have a connection to it, initiate one// 3、初始化连接,但此时不一定连接成功了initiateConnect(node, now);
returnfalse;
}
/*** Check if the node with the given id is ready to send more requests.* @param node The node* @param now The current time in ms* @return true if the node is ready*/@OverridepublicbooleanisReady(Nodenode, longnow) {
// if we need to update our metadata now declare all requests unready to make metadata requests first priority// 当发现正在更新元数据时,会禁止发送请求 && 当连接没有创建完毕或者当前发送的请求过多时,也会禁止发送请求return!metadataUpdater.isUpdateDue(now) &&canSendRequest(node.idString(), now);
}
/*** Are we connected and ready and able to send more requests to the given connection?* 检测连接状态、发送请求是否过多* @param node The node* @param now the current timestamp*/privatebooleancanSendRequest(Stringnode, longnow) {
// 三个条件必须都满足returnconnectionStates.isReady(node, now) &&selector.isChannelReady(node) &&inFlightRequests.canSendMore(node);
}
/*** 创建连接 * Initiate a connection to the given node* @param node the node to connect to* @param now current time in epoch milliseconds*/privatevoidinitiateConnect(Nodenode, longnow) {
StringnodeConnectionId=node.idString();
try {
// 1、更新连接状态为正在连接connectionStates.connecting(nodeConnectionId, now, node.host(), clientDnsLookup);
// 获取连接地址InetAddressaddress=connectionStates.currentAddress(nodeConnectionId);
log.debug("Initiating connection to node {} using address {}", node, address);
// 2、调用 selector 尝试异步进行连接,后续通过selector.poll进行监听事件就绪 selector.connect(nodeConnectionId,
newInetSocketAddress(address, node.port()),
this.socketSendBuffer,
this.socketReceiveBuffer);
} catch (IOExceptione) {
log.warn("Error connecting to node {}", node, e);
// Attempt failed, we'll try again after the backoffconnectionStates.disconnected(nodeConnectionId, now);
// Notify metadata updater of the connection failuremetadataUpdater.handleServerDisconnect(now, nodeConnectionId, Optional.empty());
}
}
/*** Do actual reads and writes to sockets.* @param timeout The maximum amount of time to wait (in ms) for responses if there are none immediately,* must be non-negative. The actual timeout will be the minimum of timeout, request timeout and* metadata timeout* @param now The current time in milliseconds* @return The list of responses received*/@OverridepublicList<ClientResponse>poll(longtimeout, longnow) {
// 确认是否活跃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=newArrayList<>();
handleAbortedSends(responses);
completeResponses(responses);
returnresponses;
}
// 1、尝试更新元数据longmetadataTimeout=metadataUpdater.maybeUpdate(now);
try {
// 2、执行网络 I/O 操作,真正读写发送的地方,如果客户端的请求被完整的处理过了,会加入到completeSends 或 complteReceives 集合中this.selector.poll(Utils.min(timeout, metadataTimeout, defaultRequestTimeoutMs));
} catch (IOExceptione) {
log.error("Unexpected error during I/O", e);
}
// process completed actionslongupdatedNow=this.time.milliseconds();
// 响应结果集合:真正的读写操作, 会生成responsesList<ClientResponse>responses=newArrayList<>();
// 3、完成发送的handler,处理 completedSends 集合handleCompletedSends(responses, updatedNow);
// 4、完成接收的handler,处理 completedReceives 队列handleCompletedReceives(responses, updatedNow);
// 5、断开连接的handler,处理 disconnected 列表handleDisconnections(responses, updatedNow);
// 6、处理连接的handler,处理 connected 列表handleConnections();
// 7、处理版本协调请求(获取api版本号) handlerhandleInitiateApiVersionRequests(updatedNow);
// 8、超时连接的handler,处理超时连接集合handleTimedOutConnections(responses, updatedNow);
// 9、超时请求的handler,处理超时请求集合handleTimedOutRequests(responses, updatedNow);
// 10、完成响应回调completeResponses(responses);
returnresponses;
}
/*** Handle any completed request send. In particular if no response is expected consider the request complete.* @param responses The list of responses to update* @param now The current time*/privatevoidhandleCompletedSends(List<ClientResponse>responses, longnow) {
// if no response is expected then when the send is completed, return it// 1、遍历 completedSends 发送完成的请求集合,通过调用 Selector 获取从上一次 poll 开始的请求for (Sendsend : this.selector.completedSends()) {
// 2、从 inFlightRequests 集合获取该 Send 关联对应 Node 的队列取出最新的请求,但并没有从队列中删除,取出后判断这个请求是否期望得到响应InFlightRequestrequest=this.inFlightRequests.lastSent(send.destination());
// 3、是否需要响应, 如果不需要响应,当Send请求完成时,就直接返回.还是有request.completed生成的ClientResponse对象if (!request.expectResponse) {
// 4、如果不需要响应就取出 inFlightRequests 中该 Sender 关联对应 Node 的 inFlightRequest,即提取最新的请求this.inFlightRequests.completeLastSent(send.destination());
// 5、调用 completed() 生成 ClientResponse,第一个参数为null,表示没有响应内容,把请求添加到 Responses 集合responses.add(request.completed(null, now));
}
}
}
/*** Choose the node with the fewest outstanding requests which is at least eligible for connection. This method will* prefer a node with an existing connection, but will potentially choose a node for which we don't yet have a* connection if all existing connections are in use. If no connection exists, this method will prefer a node* with least recent connection attempts. This method will never choose a node for which there is no* existing connection and from which we have disconnected within the reconnect backoff period, or an active* connection which is being throttled.** @return The node with the fewest in-flight requests.*/@OverridepublicNodeleastLoadedNode(longnow) {
// 从元数据中获取所有的节点List<Node>nodes=this.metadataUpdater.fetchNodes();
if (nodes.isEmpty())
thrownewIllegalStateException("There are no nodes in the Kafka cluster");
intinflight=Integer.MAX_VALUE;
NodefoundConnecting=null;
NodefoundCanConnect=null;
NodefoundReady=null;
intoffset=this.randOffset.nextInt(nodes.size());
for (inti=0; i<nodes.size(); i++) {
intidx= (offset+i) %nodes.size();
Nodenode=nodes.get(idx);
// 节点是否可以发送请求if (canSendRequest(node.idString(), now)) {
// 获取节点的队列大小intcurrInflight=this.inFlightRequests.count(node.idString());
// 如果为 0 则返回该节点,负载最小 if (currInflight==0) {
// if we find an established connection with no in-flight requests we can stop right awaylog.trace("Found least loaded node {} connected with no in-flight requests", node);
returnnode;
} elseif (currInflight<inflight) { // 如果队列大小小于最大值// otherwise if this is the best we have found so far, record thatinflight=currInflight;
foundReady=node;
}
} elseif (connectionStates.isPreparingConnection(node.idString())) {
foundConnecting=node;
} elseif (canConnect(node, now)) {
if (foundCanConnect==null||this.connectionStates.lastConnectAttemptMs(foundCanConnect.idString()) >this.connectionStates.lastConnectAttemptMs(node.idString())) {
foundCanConnect=node;
}
} else {
log.trace("Removing node {} from least loaded node selection since it is neither ready "+"for sending or connecting", node);
}
}
// We prefer established connections if possible. Otherwise, we will wait for connections// which are being established before connecting to new nodes.if (foundReady!=null) {
log.trace("Found least loaded node {} with {} inflight requests", foundReady, inflight);
returnfoundReady;
} elseif (foundConnecting!=null) {
log.trace("Found least loaded connecting node {}", foundConnecting);
returnfoundConnecting;
} elseif (foundCanConnect!=null) {
log.trace("Found least loaded node {} with no active connection", foundCanConnect);
returnfoundCanConnect;
} else {
log.trace("Least loaded node selection failed to find an available node");
returnnull;
}
}
/*** The set of requests which have been sent or are being sent but haven't yet received a response* 用来缓存已经发送出去或者正在发送但均还没有收到响应的 ClientRequest 请求集合*/finalclassInFlightRequests {
// 每个连接最大执行中的请求数privatefinalintmaxInFlightRequestsPerConnection;
// 节点 Node 至客户端请求双端队列 Deque<NetworkClient.InFlightRequest> 的映射集合,key为 NodeId, value 是请求队列privatefinalMap<String, Deque<NetworkClient.InFlightRequest>>requests=newHashMap<>();
/** Thread safe total number of in flight requests. */// 线程安全的 inFlightRequestCount privatefinalAtomicIntegerinFlightRequestCount=newAtomicInteger(0);
// 设置每个连接最大执行中的请求数publicInFlightRequests(intmaxInFlightRequestsPerConnection) {
this.maxInFlightRequestsPerConnection=maxInFlightRequestsPerConnection;
}
/*** Can we send more requests to this node?* @param node Node in question* @return true iff we have no requests still being sent to the given node* 判断该连接是否还能发送请求*/publicbooleancanSendMore(Stringnode) {
// 获取节点对应的双端队列Deque<NetworkClient.InFlightRequest>queue=requests.get(node);
// 判断条件 队列为空 || (队首已经发送完成 && 队列中没有堆积更多的请求)returnqueue==null||queue.isEmpty() ||
(queue.peekFirst().send.completed() &&queue.size() <this.maxInFlightRequestsPerConnection);
}
/*** Add the given request to the queue for the connection it was directed to* 将请求添加到队列首部*/publicvoidadd(NetworkClient.InFlightRequestrequest) {
// 这个请求要发送到哪个 Broker 节点上Stringdestination=request.destination;
// 从 requests 集合中根据给定请求的目标 Node 节点获取对应 Deque<ClientRequest> 双端队列 reqsDeque<NetworkClient.InFlightRequest>reqs=this.requests.get(destination);
// 如果双端队列reqs为nullif (reqs==null) {
// 构造一个双端队列 ArrayDeque 类型的 reqsreqs=newArrayDeque<>();
// 将请求目标 Node 节点至 reqs 的映射关系添加到 requests 集合this.requests.put(destination, reqs);
}
// 将请求 request 添加到 reqs 队首reqs.addFirst(request);
// 增加计数inFlightRequestCount.incrementAndGet();
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
3、completeNext() 出队最老请求
/*** Get the oldest request (the one that will be completed next) for the given node* 取出该连接对应的队列中最老的请求*/publicNetworkClient.InFlightRequestcompleteNext(Stringnode) {
// 根据给定 Node 节点获取客户端请求双端队列 reqs,并从队尾出队NetworkClient.InFlightRequestinFlightRequest=requestQueue(node).pollLast();
// 递减计数器inFlightRequestCount.decrementAndGet();
returninFlightRequest;
}
/*** Get the last request we sent to the given node (but don't remove it from the queue)* @param node The node id*/publicNetworkClient.InFlightRequestlastSent(Stringnode) {
returnrequestQueue(node).peekFirst();
}
1.
2.
3.
4.
5.
6.
7.
5、completeLastSent() 出队最新请求
/*** Complete the last request that was sent to a particular node.* @param node The node the request was sent to* @return The request* 取出该连接对应的队列中最新的请求*/publicNetworkClient.InFlightRequestcompleteLastSent(Stringnode) {
// 根据给定 Node 节点获取客户端请求双端队列 reqs,并从队首出队NetworkClient.InFlightRequestinFlightRequest=requestQueue(node).pollFirst();
// 递减计数器inFlightRequestCount.decrementAndGet();
returninFlightRequest;
}