Kafka怎样完成建立和Broker之间的连接?

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.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值