一、总体概述
大家都知道在 Java NIO 有个三剑客,即「SocketChannel通道」、「Buffer读写」、「Selector多路复用器」,上篇已经讲解了前2个角色,今天我们来聊聊最后一个重要的角色。
Kafka Selector 是对 Java NIO Selector 的二次封装,主要功能如下:
- 提供网络连接以及读写操作
- 对准备好的事件进行收集并进行网络操作
为了方便大家理解,所有的源码只保留骨干。
二、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;
}
}
重要字段如下所示:
- nioSelector: 在 Java NIO 中用来监听网络I/O事件。
- channels: 用来进行管理客户端到各个Node节点的网络连接,Map 集合类型 <Node节点id, KafkaChannel>
- completedSends: 已经发送完成的请求对象 Send 集合,List 集合类型。
- completedReceives: 已经接收完毕的网络请求集合,LinkedHashMap 集合类型 <ChannelId, NetworkReceive>,其中 value 都是已经接收完毕的 NetworkReceive 类对象。
- immediatelyConnectedKeys: 立即连接key集合。
- closingChannels: 关闭连接的 channel 集合。
- disconnected: 断开连接的集合。Map 集合类型 <ChannelId, ChannelState>,value 是 KafkaChannel 的状态,可以在使用的时候可以通过这个 ChannelState 状态来判断处理逻辑。
- connected: 成功连接的集合,List 集合类型,存储成功请求的 ChannelId。
- failedSends: 发送失败的请求集合,List 集合类型, 存储失败请求的 ChannelId。
- channelBuilder: 用来构建 KafkaChannel 的工具类。
- maxReceiveSize: 最大可以接收的数据量大小。
- idleExpiryManager: 空闲超时到期连接管理器。
- 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.如果立即连接成功了
if (connected) {
...

本文深入探讨了Kafka网络层的Selector组件,它是基于Java NIO的二次封装,用于网络连接和读写操作。文章详细介绍了Selector的封装过程、连接管理、空闲超时到期连接管理和网络连接的完整流程,强调了Selector在Kafka中的重要性和工作原理。
最低0.47元/天 解锁文章
4万+

被折叠的 条评论
为什么被折叠?



