图解 Kafka 网络层源码实现机制之收发消息全过程

  1. 针对 Java NIO 的 SocketChannel,kafka 是如何封装统一的传输层来实现最基础的网络连接以及读写操作的?

  2. 剖析 KafkaChannel 是如何对传输层、读写 buffer 操作进行封装的?

  3. 剖析工业级 NIO 实战:如何基于位运算来控制事件的监听以及拆包、粘包是如何实现的?

  4. 剖析 Kafka 是如何封装 Selector 多路复用器的?

  5. 剖析 Kafka 封装的 Selector 是如何初始化并与 Broker 进行连接以及网络读写的?

  6. 剖析 Kafka 网络发送消息和接收响应的整个过程是怎样的?

认真读完这篇文章,我相信你会对 Kafka 网络层源码有更加深刻的理解。

这篇文章干货很多,希望你可以耐心读完。

 

01 总体概述

通过场景驱动的方式,在网络请求封装和监听好后,我们来看看消息是如何进行网络收发的,都需要做哪些工作。

  1. 发送消息流程剖析

    • 消息预发送

    • 消息真正发送

  2. 接收响应流程剖析

    • 读取响应结果

    • 解析响应信息

    • 处理回调

为了方便大家理解,所有的源码只保留骨干。

02 发送消息流程剖析

02.1 消息预发送

这部分涉及的东西比较多,此处就简单的说明下,后续会有专门篇章进行剖析。

客户端先准备要发送的消息,流程如下:

  1. Sender 子线程会从 RecordAccumulator 缓冲区拉取要发送的消息集合,抽取到的数据会存放到下面几个地方:

    • 发送时会放入 inFlightRequests 集合和 KafkaChannel 的 send 对象,其中 inFlightRequests 后续篇章再进行剖析,这里简单说明下,该集合用来存储和操作待发送消息的缓存区,当请求准备网络发送时,会把请求从队头放入队列;当接收到响应后,会把请求从队尾删除。

    • 待发送完成后会放入 completedRequests 集合

  2. 对已经过期的数据进行处理。

  3. 封装客户端请求 ClientRequest,把 ClientRequest 类对象发送给 NetworkClient,它主要有以下2个工作要做:

    • 根据 ClientRequest 类对象构造 InFlightRequest 类对象

    • 根据 ClientRequest 类对象构造 NetworkSend 类对象,并放入到 KafkaChannel 的缓存里

  4. 此时消息预发送结束。

接下来我们依次看下 Selector 和 KafkaChannel 类的具体源码实现。

02.1.1 请求数据暂存内存中

 

 

github 源码地址如下:

https://github.com/apache/kafka/blob/2.7/clients/src/main/java/org/apache/kafka/common/network/Selector.java

https://github.com/apache/kafka/blob/2.7/clients/src/main/java/org/apache/kafka/common/network/KafkaChannel.java

/**
 * 消息预发送
 */
public void send(Send send) {
    // 1. 从服务端获取 connectionId
    String connectionId = send.destination();
    // 2. 从数据包中获取对应连接
    KafkaChannel channel = openOrClosingChannelOrFail(connectionId);
    // 3. 如果关闭连接集合中存在该连接
    if (closingChannels.containsKey(connectionId)) {
        // 把 connectionId 放入 failedSends 集合里
        this.failedSends.add(connectionId);
    } else {
        try {
            // 4. 暂存数据预发送,并没有真正的发送,一次只能发送一个
            channel.setSend(send);
        } catch (Exception e) {
            // 5. 更新 KafkaChannel 的状态为发送失败  
            channel.state(ChannelState.FAILED_SEND);
            // 6. 把 connectionId 放入 failedSends 集合里
            this.failedSends.add(connectionId);
            // 7. 关闭连接
            close(channel, CloseMode.DISCARD_NO_NOTIFY);
            ...
        }
    }
}

 从源码中可以看到调用了 KafkaChannel 类的 setSend() 方法。

 

public void setSend(Send send) {
  if (this.send != null)
      throw new IllegalState
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值