(3.3)client源码——kafka网络通信客户端-NetworkClient类

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 多路复用器

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
kafka是一个分布式的流处理平台,它可以处理和存储大规模的实时数据流。kafka-run-class是kafka提供的一个脚本工具,用于在命令行中执行指定的kafka.tools.GetOffsetShell是用于获取指定topic在指定时间点的偏移量信息的工具。 具体来说,kafka.tools.GetOffsetShell通过指定topic、时间点和broker列表来获取该topic在指定时间点的偏移量。它的用法似于以下命令: ``` bin/kafka-run-class.sh kafka.tools.GetOffsetShell --topic <topic> --time <timestamp> --broker-list <broker-list> --partitions <partitions> ``` 其中,<topic>是要查询的topic名称,<timestamp>是要查询的时间点,<broker-list>是broker的地址列表,<partitions>是要查询的分区编号。该命令会返回指定topic在指定时间点的偏移量信息。 另外,kafka.tools.ConsumerOffsetChecker是kafka提供的另一个工具,用于检查消费者的偏移量信息。它可以通过指定zookeeper连接、消费者组和topic来获取消费者的偏移量信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [kafka监控命令kafka-run-class.sh查看消费了多少条数据](https://blog.csdn.net/m0_37739193/article/details/73849523)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [kafka_2.9.2-0.8.2.1.tgz](https://download.csdn.net/download/shy_snow/9372309)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值