图解 Kafka 网络层实现机制之Selector 多路复用器

01 总体概述

大家都知道在 Java NIO 有个三剑客,即「 SocketChannel通道 」、「 Buffer读写 」、「 Selector多路复用器 」,上篇已经讲解了前2个角色,今天我们来聊聊最后一个重要的角色。

Kafka Selector 是对 Java NIO Selector 的二次封装,主要功能如下:

  1. 提供网络连接以及读写操作

  2. 对准备好的事件进行收集并进行网络操作

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

02 Selector 封装过程

github 源码地址如下:

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

org.apache.kafka.common.network.Selector,该类是 Kafka 网络层最重要最核心的实现,也是非常经典的工业级通信框架实现,为了简化,这里称为 Kselector, 接下来我们先来看看该类的重要属性字段:

public class Selector implements Selectable, AutoCloseable {
    // 在 Java NIO 中用来监听网络I/O事件
    private final java.nio.channels.Selector nioSelector;
    // channels 管理
    private final Map channels; 
    // 发送完成的Send集合
    private final List completedSends;
    // 已经接收完毕的请求集合
    private final LinkedHashMap completedReceives;
    // 立即连接的集合
    private final Set immediatelyConnectedKeys;
    // 关闭连接的 channel 集合
    private final Map closingChannels;
    // 断开连接的节点集合
    private final Map disconnected;
    // 连接成功的节点集合
    private final List connected;
    // 发送失败的请求集合
    private final List failedSends;
    // 用来构建 KafkaChannel 的工具类
    private final ChannelBuilder channelBuilder;
    // 最大可以接收的数据量大小
    private final int maxReceiveSize;
    // 空闲超时到期连接管理器
    private final IdleExpiryManager idleExpiryManager;
    // 用来管理 ByteBuffer 的内存池
    private final MemoryPool memoryPool;
    // 初始化 Selector
    public Selector(int maxReceiveSize,
            long connectionMaxIdleMs,
            int failedAuthenticationDelayMs,
            Metrics metrics,
            Time time,
            String metricGrpPrefix,
            Map metricTags,
            boolean metricsPerConnection,
            boolean recordTimePerConnection,
            ChannelBuilder channelBuilder,
            MemoryPool memoryPool,
            LogContext logContext) {
        try {
            this.nioSelector = java.nio.channels.Selector.open();
        } catch (IOException e) {
            throw new KafkaException(e);
        }
        this.maxReceiveSize = maxReceiveSize;
        this.time = time;
        this.channels = new HashMap<>();
        this.explicitlyMutedChannels = new HashSet<>();
        this.outOfMemory = false;
        this.completedSends = new ArrayList<>();
        this.completedReceives = new LinkedHashMap<>();
        this.immediatelyConnectedKeys = new HashSet<>();
        this.closingChannels = new HashMap<>();
        this.keysWithBufferedRead = new HashSet<>();
        this.connected = new ArrayList<>();
        this.disconnected = new HashMap<>();
        this.failedSends = new ArrayList<>();
        this.log = logContext.logger(Selector.class);
        this.sensors = new SelectorMetrics(metrics, metricGrpPrefix, metricTags, metricsPerConnection);
        this.channelBuilder = channelBuilder;
        this.recordTimePerConnection = recordTimePerConnection;
        this.idleExpiryManager = connectionMaxIdleMs < 0 ? null : new IdleExpiryManager(time, connectionMaxIdleMs);
        this.memoryPool = memoryPool;
        this.lowMemThreshold = (long) (0.1 * this.memoryPool.size());
        this.failedAuthenticationDelayMs = failedAuthenticationDelayMs;
        this.delayedClosingChannels = (failedAuthenticationDelayMs > NO_FAILED_AUTHENTICATION_DELAY) ? new LinkedHashMap() : null;
    }
}

重要字段如下所示:

  1. nioSelector:在 Java NIO 中用来监听网络I/O事件。

  2. channels:用来进行管理客户端到各个Node节点的网络连接,Map 集合类型

  3. completedSends:已经发送完成的请求对象 Send 集合,List 集合类型。

  4. completedReceives:已经接收完毕的网络请求集合,LinkedHashMap 集合类型 ,其中 value 都是已经接收完毕的 NetworkReceive 类对象。

  5. immediatelyConnectedKeys:立即连接key集合。

  6. closingChannels:关闭连接的 channel 集合。

  7. disconnected:断开连接的集合。Map 集合类型 ,value 是 KafkaChannel 的状态,可以在使用的时候可以通过这个 ChannelState 状态来判断处理逻辑。

  8. connected:成功连接的集合,List 集合类型,存储成功请求的 ChannelId。

  9. failedSends:发送失败的请求集合,List 集合类型, 存储失败请求的 ChannelId。

  10. channelBuilder:用来构建 KafkaChannel 的工具类。

  11. maxReceiveSize:最大可以接收的数据量大小。

  12. idleExpiryManager:空闲超时到期连接管理器。

  13. memoryPool:用来管理 ByteBuffer 的内存池,分配以及回收。

介绍完字段后,我们来看看该类的方法。方法比较多,这里深度剖析下其中几个重要方法,通过学习这些方法的不仅可以复习下 Java NIO 底层组件,另外还可以学到 Kafka 封装这些底层组件的实现思想。

NetworkClient 的请求一般都是交给 Kselector 去处理并完成的。而 Kselector 使用 NIO 异步非阻塞模式负责具体的连接、读写事件等操作。

我们先看下连接过程,客户端在和节点连接的时候,会创建和服务端的 SocketChannel 连接通道。Kselector 维护了每个目标节点对应的 KafkaChannel。

如下图所示:

图解 Kafka 网络层实现机制之Selector 多路复用器_第1张图片

02.1 connect()

@Override
public void connect(String id, InetSocketAddress address, int sendBufferSize, int receiveBufferSize) throws IOException {
    // 1.先确认是否已经被连接过  
    ensureNotRegistered(id);
    // 2.打开一个 SocketChannel
    SocketChannel socketChannel = SocketChannel.open();
    SelectionKey key = null;
    try {
        // 3.设置 socketChannel 信息 
        configureSocketChannel(socketChannel, sendBufferSize, receiveBufferSize);
        // 4.尝试发起连接
        boolean connected = doConnect(socketChannel, address);
        // 5. 将该 socketChannel 注册到 nioSelector 上,并关注 OP_CONNECT 事件
        key = registerChannel(id, socketChannel, SelectionKey.OP_CONNECT);
        // 6.如果立即连接成功了
        if (connected) {
            ...
            // 先将 key 放入 immediatelyConnectedKeys 集合
            immediatelyConnectedKeys.add(key);
            // 并取消对 OP_CONNECT 的监听
            key.interestOps(0);
        }
    } catch (IOExcepti

你可能感兴趣的:(程序员,Java,计算机,kafka,java,大数据)