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

一、总体概述

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

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

  1. 提供网络连接以及读写操作
  2. 对准备好的事件进行收集并进行网络操作

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

二、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<String, KafkaChannel> channels; 
    // 发送完成的Send集合
    private final List<Send> completedSends;
    // 已经接收完毕的请求集合
    private final LinkedHashMap<String, NetworkReceive> completedReceives;
    // 立即连接的集合
    private final Set<SelectionKey> immediatelyConnectedKeys;
    // 关闭连接的 channel 集合
    private final Map<String, KafkaChannel> closingChannels;
    // 断开连接的节点集合
    private final Map<String, ChannelState> disconnected;
    // 连接成功的节点集合
    private final List<String> connected;
    // 发送失败的请求集合
    private final List<String> 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<String, String> 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<String, DelayedAuthenticationFailureClose>() : null;
    }
}

重要字段如下所示:

  1. nioSelector: 在 Java NIO 中用来监听网络I/O事件。
  2. channels: 用来进行管理客户端到各个Node节点的网络连接,Map 集合类型 <Node节点id, KafkaChannel>
  3. completedSends: 已经发送完成的请求对象 Send 集合,List 集合类型。
  4. completedReceives: 已经接收完毕的网络请求集合,LinkedHashMap 集合类型 <ChannelId, NetworkReceive>,其中 value 都是已经接收完毕的 NetworkReceive 类对象。
  5. immediatelyConnectedKeys: 立即连接key集合。
  6. closingChannels: 关闭连接的 channel 集合。
  7. disconnected: 断开连接的集合。Map 集合类型 <ChannelId, ChannelState>,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。

如下图所示:

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.如果立即连接成功了
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值