Java Kafka源代码-理解Selector.poll()的语义

Java Kafka源代码-理解Selector.poll()的语义,java,apache-kafka,polling,Java,Apache Kafka,Polling,我正在研究卡夫卡的网络层代码,我有一些关于选择器的问题 类,特别是poll方法的实现方式。投票方法如下所示: void poll(int timeout){ .... /* check ready keys */ long startSelect = time.nanoseconds(); int readyKeys = select(timeout); long endSelect = time.nanoseconds(); this.sensors.s

我正在研究卡夫卡的网络层代码,我有一些关于选择器的问题 类,特别是poll方法的实现方式。投票方法如下所示:

void poll(int timeout){
....
    /* check ready keys */
    long startSelect = time.nanoseconds();
    int readyKeys = select(timeout);
    long endSelect = time.nanoseconds();
    this.sensors.selectTime.record(endSelect - startSelect, time.milliseconds());

    if (readyKeys > 0 || !immediatelyConnectedKeys.isEmpty()) {
        pollSelectionKeys(this.nioSelector.selectedKeys(), false, endSelect);
        pollSelectionKeys(immediatelyConnectedKeys, true, endSelect);
    }

...
}
我们首先调用pollSelectionKeys方法的原因是什么 对于select方法返回的键,然后是直接连接的键?是 为了清楚起见,我们单独执行这些操作,还是有一些特定的操作 涉及的要求

其次,在pollSelectionKeys方法中,我们有:

void pollSelectionKeys(Iterable<SelectionKey> selectionKeys,
                                       boolean isImmediatelyConnected,
                                       long currentTimeNanos){
...
    /* if channel is ready write to any sockets that have space in their buffer and for which
    we have data */
    if (channel.ready() && key.isWritable()) {
        Send send = channel.write();
        if (send != null) {
            this.completedSends.add(send);
            this.sensors.recordBytesSent(channel.id(), send.size());
        }
    }
...
}
据我所知,我们只在卡夫卡察尼属于任何一方时才给它写信 我们从前面对select方法的调用中获得的键集,或者如果KafkaChannel 与其中一个立即连接的密钥关联。我的问题是,我们为什么要做这件事 这样写信给卡夫卡察尼家族的事?更具体地说,我们不需要迭代 在所有已连接的卡夫卡通道上,如果他们有发送,请写信给他们 与它们关联的对象?这样我们就可以尽快给卡夫卡察尼写信,
不必等待它属于立即连接的密钥或ReadyKey。

在I/O连接完成之前,TCP连接不可用。

答案在于下面类相关部分的连接方法

 connected = socketChannel.connect(address);
..............................
................................

 SelectionKey key = socketChannel.register(nioSelector, SelectionKey.OP_CONNECT);
根据NIO connect的文档说明

如果此通道处于非阻塞模式,则调用 方法启动非阻塞连接操作。如果 立即建立连接,就像本地服务器可能发生的那样 连接,则此方法返回true。否则,这种方法 返回false,连接操作必须稍后由完成 调用finishConnect方法

下面很好地解释了一个典型的交互工作流程

如果以非阻塞模式连接,则应:

注册OP_CONNECT的通道 当它触发时,请调用finishConnect 如果返回true,则取消注册OP_CONNECT并注册OP_READ或OP_WRITE,具体取决于接下来要执行的操作 如果返回false,则不执行任何操作,继续选择 如果connect或finishConnect引发异常,请关闭通道并重试,或者忘记它,或者告诉用户或 只要合适。 如果在通道连接之前不想执行任何操作,请执行以下操作: 在阻塞模式下连接,并在以下情况下进入非阻塞模式: 连接成功

在本地连接的情况下,这种连接方法可能会立即连接,并且可能不会触发为该连接注册的OP_connect事件。在连接调用后,socketChannel会运行几行,因此,当使用典型的java NIO注册代码时,我们可能会错过它。我们最终需要在这些渠道上调用finishConnect,请参阅工作流中的第二个要点。因此,我们将这样的通道密钥添加到另一组立即连接的密钥中,这样它们就可以被处理得太晚,否则我们将完全丢失它们

 if (readyKeys > 0 || !immediatelyConnectedKeys.isEmpty()) {
            pollSelectionKeys(this.nioSelector.selectedKeys(), false, endSelect);
            pollSelectionKeys(immediatelyConnectedKeys, true, endSelect);
        }
稍后在pollSelectionKeys方法中,请注意finishConnect的使用,finishConnect是对底层

总之,卡夫卡的代码看起来像是标准的NIO代码,除非卡夫卡团队能够解释更多。关于这个主题的更多好的阅读可以找到。我们可以发现一个有趣的误解,它与这个归档和JDK团队最终的拒绝有关

对于问题的第二部分,您可能会询问以下代码。为什么要两个不同的键

if (readyKeys > 0 || !immediatelyConnectedKeys.isEmpty()) {
            pollSelectionKeys(this.nioSelector.selectedKeys(), false, endSelect);
            pollSelectionKeys(immediatelyConnectedKeys, true, endSelect);
        }
请看,我们现在维护了两组键。虽然有一个整体键视图是由selector.keys提供的,但该键集不可直接修改,因此它是一种只读视图。此密钥集中的密钥只有在被取消且其频道被取消注册后才会被删除。因此,通常使用selector.selectedKeys访问就绪频道。另外,selector.selectedKeys显然不会立即从连接的按键返回按键。对从selector.selectedKeys获得的这些键进行处理的通常模式是在集合上迭代,测试由以下表达式表示的通道可接受、可连接、可读写的事件 密钥已准备就绪,请执行您的操作,然后将其从集合中移除。此删除部分非常必要。选择器不会从所选密钥集本身删除SelectionKey实例。处理完通道后,必须执行此操作。
下一次通道准备就绪时,选择器将再次将其添加到选定的关键点集。因此,这就是处理这两个问题的原因,pollSelectionKeys方法设计用于处理这两个问题。

这就更有意义了。你对上面的第二个问题有什么回答吗?我补充了我对第二个问题的看法。
if (readyKeys > 0 || !immediatelyConnectedKeys.isEmpty()) {
            pollSelectionKeys(this.nioSelector.selectedKeys(), false, endSelect);
            pollSelectionKeys(immediatelyConnectedKeys, true, endSelect);
        }