图解 Kafka 网络层实现机制之上篇

本文深入剖析Kafka如何封装Java NIO,包括TransportLayer、NetworkReceive、NetworkSend和KafkaChannel的实现。通过位运算控制事件监听,解决网络通信中的拆包、粘包问题。文章详细解释了网络连接、读写操作以及事件注册和取消的时机,帮助读者理解Kafka网络通信的内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇中,主要带大家深度剖析了「 生产者元数据 」的拉取、管理全流程,今天我们就来聊聊 Kafka 是如何对 Java NIO 进行封装的 ,本系列总共分为3篇,主要剖析以下几个问题:

  1. 针对 Java NIO 的 SocketChannel,kafka 是如何封装统一的传输层来实现最基础的网络连接以及读写操作的?
  2. 剖析 KafkaChannel 是如何对传输层、读写 buffer 操作进行封装的?
  3. 剖析工业级 NIO 实战:如何基于位运算来控制事件的监听以及拆包、粘包是如何实现的?
  4. 剖析 Kafka 是如何封装 Selector 多路复用器的?
  5. 剖析 Kafka 封装的 Selector 是如何初始化并与 Broker 进行连接以及网络读写的?
  6. 剖析 Kafka 网络发送消息和接收响应的整个过程是怎样的?

本篇只讨论前3个问题,剩余的放到后2篇中。

认真读完这篇文章,我相信你会对 Kafka 封装 Java NIO 源码有更加深刻的理解。

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

01 总体概述

上篇剖析了「 生产者元数据的拉取和管理的全过程 」,此时发送消息的时候就有了元数据,但是还没有进行网络通信,而网络通信是一个相对复杂的过程,对于 Java 系统来说网络通信一般会采用 NIO 库来实现,所以 Kafka 对 Java NIO 封装了统一的框架,来实现多路复用的网络 I/O 操作 。

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

02 Kafka 对 Java NIO 的封装

如果大家对 Java NIO 不了解的话,可以看下这个文档,这里就不过多介绍了。

https://pdai.tech/md/java/io/java-io-nio.html

我们来看看 Kafka 对 Java NIO 组件做了哪些封装? 这里先说下结果,后面会深度剖析。

  1. TransportLayer:它是一个接口,封装了底层 NIO 的 SocketChannel。
  2. NetworkReceive:封装了 NIO 的 ByteBuffer 中的读 Buffer, 对网络编程中的粘包、拆包经典实现 。
  3. NetworkSend:封装了 NIO 的 ByteBuffer 中的写 Buffer。
  4. KafkaChannel:对 TransportLayer、NetworkReceive、NetworkSend 进一步封装,屏蔽了底层的实现细节,对上层更友好。
  5. KafkaSelector:封装了 NIO 的 Selector 多路复用器组件。

接下来我们挨个对上面组件进行剖析。

02 TransportLayer 封装过程

TransportLayer 接口是对 NIO 中 「 SocketChannel 」 的封装。它的实现类总共有 2 个:

  1. PlaintextTransportLayer:明文网络传输实现。
  2. SslTransportLayer:SSL 加密网络传输实现。

本篇只剖析 PlaintextTransportLayer 的实现。

github 源码地址如下:

https://github.com/apache/kafka/blob/2.7/clients/src/main/java/org/apache/kafka/common/network/PlaintextTransportLayer.java
public class PlaintextTransportLayer implements TransportLayer {
    // java nio 中 SelectionKey 事件
    private final SelectionKey key;
    // java nio 中的SocketChannel
    private final SocketChannel socketChannel;
    // 安全相关
    private final Principal principal = KafkaPrincipal.ANONYMOUS;
    // 初始化
    public PlaintextTransportLayer(SelectionKey key) throws IOException {
        // 对 NIO 中 SelectionKey 类的对象引用
        this.key = key;
        // 对 NIO 中 SocketChannel 类的对象引用
        this.socketChannel = (SocketChannel) key.channel();
    }
}

从上面代码可以看出,该类就是 对底层 NIO 的 socketChannel 封装引用 。将构造函数的 SelectionKey 类对象赋值给 key,然后从 key 中取出对应的 SocketChannel 赋值给 socketChannel,这样就完成了初始化工作。

接下来,我们看看几个重要方法是如何使用这2个 NIO 组件的。

02.1 finishConnect()

@Override
// 判断网络连接是否完成
public boolean finishConnect() throws IOException {
    // 1. 调用socketChannel的finishConnect方法,返回该连接是否已经连接完成
    boolean connected = socketChannel.finishConnect();
    // 2. 如果网络连接完成以后就删除对OP_CONNECT事件的监听,同时添加对OP_READ事件的监听
    if (connected)
        // 事件操作
        key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
    // 3. 最后返回网络连接
    return connected;
}

该方法主要用来 判断网络连接是否完成 ,如果完成就关注 「 OP_READ 」 事件,并取消 「 OP_CONNECT 」 事件。

  1. 首先调用 socketChannel 通道的 finishConnect() 判断连接是否完成。
  2. 如果网络连接完成以后就删除对 OP_CONNECT 事件的监听,同时添加对 OP_READ 事件的监听,因为连接完成后就可能接收数据了。
  3. 最后返回网络连接 connected。

二进制位运算事件监听

这里通过「 二进制位运算 」巧妙的解决了网络事件的监听操作,实现非常经典。

通过 socketChannel 在 Selector 多路复用器注册事件返回 SelectionKey ,SelectionKey 的类型包括:

  1. OP_READ:可读事件,值为:1<<0 == 1 == 00000001。
  2. OP_WRITE:可写事件,值为:1<<2 == 4 == 00000100。
  3. OP_CONNECT:客户端连接服务端的事件,一般为创建 SocketChannel 客户端 channel,值为:1<<3 == 8 ==00001000。
  4. OP_ACCEPT:服务端接收客户端连接的事件,一般为创建 ServerSocketChannel 服务端 channel,值为:1<<4 == 16 == 00010000。
key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT | SelectionKey.OP_READ);

首先"~"符号代表按位取反,"&"代表按位取与,通过 key.interestOps() 获取当前的事

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值