文章目录
NetworkClient初始化
NetworkClient是一个网络通信组件,而底层最核心的建立连接、发起请求、处理网络I/O,是依靠Selector完成的。Selector是非阻塞的、基于多路复用的。
// Channel和Selector搭配使用
ChannelBuilder channelBuilder = ClientUtils.createChannelBuilder(config.values());
// 构造核心组件:网络通信NetworkClient,NetworkClient组件实现了KafkaClient接口
NetworkClient client = new NetworkClient(
// connections.max.idle.ms,默认:9min。跟Broker的一个网络连接最多空闲超过多长时间,就得被回收掉
// 跟Broker建立连接,它是最核心的组件(first参数:每个连接最多可以空闲几分钟)
// 针对多个Broker的网络连接,基于多路复用,执行非阻塞的I/O操作
new Selector(config.getLong(ProducerConfig.CONNECTIONS_MAX_IDLE_MS_CONFIG), this.metrics, time, "producer", channelBuilder),
this.metadata,
clientId,
// max.in.flight.requests.per.connection,默认:5个
// 对每个Broker,最多允许有5个request可以暂时收不到响应,放在in-flight集合中
config.getInt(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION),
// NetworkClient跟Broker建立网络连接如果失败了,间隔reconnect.backoff.ms(默认:50ms),就得重试
config.getLong(ProducerConfig.RECONNECT_BACKOFF_MS_CONFIG),
// Socket发送缓冲区的大小为:send.buffer.bytes,默认:128k
config.getInt(ProducerConfig.SEND_BUFFER_CONFIG),
// Socket接收缓冲区的大小为:receive.buffer.bytes,默认:32k
config.getInt(ProducerConfig.RECEIVE_BUFFER_CONFIG),
this.requestTimeoutMs, time);
1.基于Java NIO SocketChannel封装KafkaChannel
在构建网络通信组件NetworkClient时,会创建Selector实例。通过Kafka的Selector的构造方法可以看出,Kafka的网络通信底层就是基于Java NIO Selector实现的。
public Selector(int maxReceiveSize, long connectionMaxIdleMs, Metrics metrics, Time time, String metricGrpPrefix, Map<String, String> metricTags, boolean metricsPerConnection, ChannelBuilder channelBuilder) {
try {
// 初始化Java NIO Selector作为Kafka Selector的基础
// 这是一个多路复用组件,可以一个线程监听多个网络连接的请求、响应
this.nioSelector = java.nio.channels.Selector.open();
} catch (IOException e) {
throw new KafkaException(e);
}
// 最大能接收的数据大小
this.maxReceiveSize = maxReceiveSize;
// 一个连接最多能空闲多长时间,超过就要被回收
this.connectionsMaxIdleNanos = connectionMaxIdleMs * 1000 * 1000;
this.time = time;
this.metricGrpPrefix = metricGrpPrefix;
this.metricTags = metricTags;
// 映射关系为:“Broker ID:KafkaChannel”
this.channels = new HashMap<>();
// 已经成功发送出去的请求
this.completedSends = new ArrayList<>();
// 已经接收到的响应,而且已经被处理完了
this.completedReceives = new ArrayList<>();
// 每个Broker已收到、但尚未被处理的响应
this.stagedReceives = new HashMap<>();
this.immediatelyConnectedKeys = new HashSet<>();
// 已经成功建立连接的Broker列表
this.connected = new ArrayList<>();
// 尚未成功建立连接的Broker列表
this.disconnected = new ArrayList<>();
// 发送请求失败的Broker列表
this.failedSends = new ArrayList<>();
this.sensors = new SelectorMetrics(metrics);
this.channelBuilder = channelBuilder;
// initial capacity and load factor are default, we set them explicitly because we want to set accessOrder = true
this.lruConnections = new LinkedHashMap<>(16, .75F, true);
currentTimeNanos = time.nanoseconds();
nextIdleCloseCheckTime = currentTimeNanos + connectionsMaxIdleNanos;
this.metricsPerConnection = metricsPerConnection;
}
Kafka Selector内按照映射关系“Broker ID:KafkaChannel”存储了Broker对应的KafkaChannel,而KafkaChannel的底层实现–SocketChannel,最终会被包装到TransportLayer 中。
public class KafkaChannel {
// Broker ID
private final String id;
// TransportLayer接口内部封装有Java NIO的SocketChannel
private final TransportLayer transportLayer;
private final Authenticator authenticator;
private final int maxReceiveSize;
// 这个Channel最近(不断地)读取到的响应
private NetworkReceive receive;
// 由这个Channel发送出去的请求,发送出去一个之后发另一个
private Send send;
}
2.Kafka提供的Selector是如何初始化跟Broker之间的连接的
Sender线程在检查Broker是否ready时,会初始化一个新的连接。
private void initiateConnect(Node node, long now) {
// 拿到Broker ID
String nodeConnectionId = node.idString();
try {
log.debug("Initiating connection to node {} at {}:{}.", node.id(), node.host(), node.port());
// 将这个Broker ID的节点连接状态,修改为:CONNECTING
this.connectionStates.connecting(nodeConnectionId, now);
// 通过Java NIO Selector建立连接:会在底层初始化一个SocketChannel发起连接请求,将其注册到Selector上。
// 由Selector监听负责监听相应的事件,如果Broker返回响应说可以建立连接,Selector就会告诉你:可以通过一个API调用完成底层的网络连接
selector.connect(nodeConnectionId, // Broker ID
// 根据Broker的host和port,包装成Socket连接地址
new InetSocketAddress(node.host(), node.