Java NIO从一个套接字读取并写入另一个套接字的固有缺陷?

时间:2018-10-30 10:55:15

标签: java performance sockets nio

我致力于一个基本的最小服务器概念,即使用NIO和套接字通道将数据从一个端口流到另一个目的地。

事物在全速,快速链接等情况下都能很好地工作。当一方比另一方快时,事情将彻底失败并消耗大量CPU。一切正常,只是效率很低。

示例:

Iterator keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
    SelectionKey key = (SelectionKey) keys.next();
    keys.remove();
    try {
        if (!key.isValid()) continue;
        if (key.isReadable()) read(key);
    }
    catch (Exception e) {
        e.printStackTrace();
        key.cancel();
    }
}

只要套接字具有可读取的数据,就会发生read调用。但是,如果由于客户端具有高延迟或无法快速读取数据而无法写入此套接字的可写部分怎么办?我们最终以疯狂的速度循环,什么也没做,直到释放出更多可写缓冲区为止:

public void read(SelectionKey key) throws IOException {
    ByteBuffer b = (ByteBuffer) buffers.get(readable); //prior buffer that may or may not have been fully used from the prior write attempt
    int bytes_read = readable.read(b);
    b.flip();
    if (bytes_read > 0 || b.position() > 0) writeable.write(b);
    b.compact();
}

所以说我们可以从千兆位的套接字中读取数据,但是接收器只能从100kbit的可写套接字中读取数据……我们可能在每一个可以写入客户端的小数据之间循环一百万次因为它们只是没有像我们想要的那样快速使用套接字缓冲区中的数据。

如果我使用线程执行此操作,将不会有任何问题,因为我们将阻塞写调用。但是使用NIO,您应该怎么做才能使其跳过读取通知?

1 个答案:

答案 0 :(得分:1)

我知道了。我仍然没有找到针对我的文档,但是经过更多考虑之后,我意识到这就是OP_WRITEABLE场景的目的。

因此,当可写对象返回它接受的0个字节时,我在可读通道上取消注册OP_READ,并在可写通道上注册OP_WRITEABLE。通知我其可写后,我会换回读/写通道,包括为立即删除OP_WRITEABLE而注册的通道。

因此,当无法完成写入时,请取消注册触发您尝试对其进行写入的读取,然后注册有关其的写入通知。一旦知道了,就换回注册。

现在一切正常。