【网络核心层篇】NetworkClient—检查连接

Sender 线程在发送消息的的sendProducerData()方法中,会对Kafka的的每个node 进行检测是否可以发送消息,将没有就绪的node节点移除,这个时候就会调用NetworkClient的ready方法对指定的node 进行检测。

1.流程图:

【网络核心层篇】NetworkClient—检查连接_第1张图片

2.源码分析:
1.检测node的连接状态是否就绪

可用则为true,不可用则返回false,并会初始化一个连接

/**
 * Begin connecting to the given node, return true if we are already connected and ready to send to that node.
 *
 * 检测指定node是否可以发送请求
 * 如果可以发送,那么就返回true,否则就初始化一个连接,
 * @param node The node to check
 * @param now  The current timestamp
 * @return True if we are ready to send to the given node
 */
@Override
public boolean ready(Node node, long now) {
    if (node.isEmpty())
        throw new IllegalArgumentException("Cannot connect to empty node " + node);

    //是否已经准备好发送请求 [2]
    if (isReady(node, now))
        return true;

    // 检测该节点现在是否能够连接
    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
      //初始化一个连接
        initiateConnect(node, now);

    return false;
}

是否能够建立连接:

public boolean canConnect(String id, long now) {
    //该连接没有建立,或者连接断开,并且距离最近一次的连接时间超过了重试周期reconnect.backoff.max.ms 配置
    NodeConnectionState state = nodeState.get(id);
    if (state == null)
        return true;
    else
        return state.state.isDisconnected() &&
                now - state.lastConnectAttemptMs >= state.reconnectBackoffMs;
}
2.isReady()
@Override
public boolean isReady(Node node, long now) {
    // if we need to update our metadata now declare all requests unready to make metadata requests first
    // priority
    /**是否我们现在需要更新元数据,如果需要更新数据,该方法返回false,
     *
     * !metadataUpdater.isUpdateDue(now)  现在不需要更新
     */
    // 以及该node是否已经可以发送请求
    return !metadataUpdater.isUpdateDue(now) && canSendRequest(node.idString(), now);
}

isReady判断:

当前元数据不需要进行更新,并且可以发送消息则 该node 连接就绪。

3.当前元数据更新是否需要更新的判断依据:
public boolean isUpdateDue(long now) {
    //没有处于正在更新的过程中,metadata应该更新
    return !this.metadataFetchInProgress && this.metadata.timeToNextUpdate(now) == 0;
}
/**
 * The next time to update the cluster info is the maximum of the time the current info will expire and the time the
 * current info can be updated (i.e. backoff time has elapsed); If an update has been request then the expiry time
 * is now
 * 计算下次更新metadata时间,这里也就是metadata更新策略:
 *
 *   1: 当needUpdate=true时,当前时间 - 上一次更新时间 > refreshBackoffMs 进行更新。
 *      否则,继续等待到更新周期refreshBackoffMs进行跟新,
 *
 *   2:如果needUpdate=false时候,检查metadata是否过期,如果过期了。则对更新周期进行判断。
 *
 *   3: refreshBackoffMs使用的是 retry.backoff.ms配置。默认更新周期100毫秒。
 *
 *   总结:metadata的更新周期是100ms,过期时间是metadata.max.age.ms,默认60s。
 *   如果当needUpdate=true,当更新开关打开,并且与上一次更新时间间隔达到100ms以上进行更新,
 *   在更新开关没有打开的情况下,上一次更新是一个成功的操作,该metadata会缓存metadata.max.age.ms,
 *   然后数据过期发生更新操作。
 *
 *   timeToNextUpdate(nowMs)的返回值就是还剩多久需要进行更新操作。
 *
 */

public synchronized long timeToNextUpdate(long nowMs) {

    long timeToExpire = needUpdate ? 0 : Math.max(this.lastSuccessfulRefreshMs + this.metadataExpireMs - nowMs, 0);

    long timeToAllowUpdate = this.lastRefreshMs + this.refreshBackoffMs - nowMs;
    return Math.max(timeToExpire, timeToAllowUpdate);
}

总结:当前元数据是否需要更新:

1:元数据正在更新过程中,也就是客户端发起了更新元数据请求,但是服务端还没响应,此时需要等待元数据更新,

2:没有处于更新过程中,那么就会判断是否需要更新,需要更新则进行更新,注意更新周期。

3:如果不需要更新,会进进一步检测,数据是否过期,没有超过过期时间则不需要更新。否则也需要进行更新。

4.可以发送消息:canSendRequest
/**
 * Are we connected and ready and able to send more requests to the given connection?
 * 是否与指定的kafka节点已经建立好连接
 * @param node The node
 * @param now  the current timestamp
 */
private boolean canSendRequest(String node, long now) {
//node的状态被标记为就绪, channel is ready 并且,请求队列可以发送请求
    return connectionStates.isReady(node, now) && selector.isChannelReady(node)
            && inFlightRequests.canSendMore(node);
}

三个条件:

1:如果在connectionStates中该节点的状态被标记为就绪,

2:该node关联的KafkaChannel 就绪。

3:请求队列inFlightRequests的状态可以发送消息 canSendMore

5:canSendMore
    /**
     * 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
     *
     *         重点条件:queue.peekFirst().request().completed,
     *         即如果发给这个节点的最早的请求还没有发送完成,是不能再往这个节点发送请求的。
     * 
     *         从canSendMore方法中也可以看出:
     
     *         只要没有超过maxInFlightRequestsPerConnection,
               一个node可以有多个in-flight request的
     *
     * maxInFlightRequestsPerConnection -->  max.in.flight.requests.per.connection                      
     *
     *
     */
    public boolean canSendMore(String node) {
        Deque<NetworkClient.InFlightRequest> queue = requests.get(node);
        return queue == null || queue.isEmpty() ||
                (queue.peekFirst().send.completed() && queue.size() < this.maxInFlightRequestsPerConnection);
    }

InFlightRequest保存了所有已发送,但还没收到响应的请求。

如果InFlightRequest为空,也就是没有还在等待响应的请求,

InFlightRequest中第一个,也就是最早的那个请求已经完成,并且容量有余,

InFlightRequest的最大最大size 对应的是max.in.flight.requests.per.connection
也就是 配置限制客户端在单个连接上能够发送的未响应请求的个数
InFlightRequest为空,也就是没有还在等待响应的请求,

InFlightRequest中第一个,也就是最早的那个请求已经完成,并且容量有余,

```java
InFlightRequest的最大最大size 对应的是max.in.flight.requests.per.connection
也就是 配置限制客户端在单个连接上能够发送的未响应请求的个数

你可能感兴趣的:(Kafka,kafka2.0,源码分析)