java - 使用 NIO 避免高 CPU 使用率

标签 java network-programming cpu-usage

我编写了一个多线程游戏服务器应用程序,它使用 NIO 处理多个同时连接。 .不幸的是,一旦第一个用户连接,即使该用户实际上没有发送或接收任何数据,该服务器也会在一个内核上产生全部 CPU 负载。

下面是我的网络处理线程的代码(缩写为可读性的基本部分)。类(class)ClientHandler是我自己的类,它为游戏机制进行网络抽象。以下示例中的所有其他类均来自 java.nio .

如您所见,它使用 while(true)环形。我的理论是,当 key 可写时,selector.select()将立即返回和clientHandler.writeToChannel()叫做。但是当处理程序返回而不写入任何内容时, key 将保持可写。然后立即再次调用 select 并立即返回。所以我忙得不可开交。

有没有办法设计网络处理循环,只要 clientHandlers 没有要发送的数据,它就会 hibernate ? 请注意,低延迟对我的用例至关重要,因此当没有处理程序有数据时,我不能让它 hibernate 任意数量的毫秒。

ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(port));
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
// wait for connections

while(true)
{
     // Wait for next set of client connections
    selector.select();
    Set<SelectionKey> keys = selector.selectedKeys();
    Iterator<SelectionKey> i = keys.iterator();
    while (i.hasNext()) {
        SelectionKey key = i.next();
        i.remove();

        if (key.isAcceptable()) {
            SocketChannel clientChannel = server.accept();
            clientChannel.configureBlocking(false);
            clientChannel.socket().setTcpNoDelay(true);
            clientChannel.socket().setTrafficClass(IPTOS_LOWDELAY);
            SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
            ClientHandler clientHanlder = new ClientHandler(clientChannel);
            clientKey.attach(clientHandler);
        }
        if (key.isReadable()) {
            // get connection handler for this key and tell it to process data 
            ClientHandler clientHandler = (ClientHandler) key.attachment();
            clientHandler.readFromChannel();
        }
        if (key.isWritable()) {
            // get connection handler and tell it to send any data it has cached 
            ClientHandler clientHandler = (ClientHandler) key.attachment();
            clientHandler.writeToChannel();
        }
        if (!key.isValid()) {
            ClientHandler clientHandler = (ClientHandler) key.attachment();
            clientHandler.disconnect();
        }
    }
}

最佳答案

SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

问题就在这里。除非套接字发送缓冲区已满,否则 SocketChannel 几乎总是可写的。因此,他们通常不应注册 OP_WRITE:否则你的选择器循环会旋转。只有在以下情况下才应如此注册:
  • 有事要写,
  • 之前 write()已返回零。
  • 关于java - 使用 NIO 避免高 CPU 使用率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14881294/

    相关文章:

    java - 在 Eclipse 中方便地将一个类 move 到不同的包中,而不需要 svn

    java - 字符串 url 编码两次,我无法获取初始字符串

    java - 使用 Hibernate,拦截器接收与当前值和先前值相同的集合对象,如何获取真实的当前值和先前值?

    ios - 当我运行 for-in 循环时 CPU 使用率飙升 : OS-x app

    java - Hibernate:使用 setFirstResult 和 setMaxResult 进行分页

    python - 如何在 Python 中使用套接字响应 SSDP 搜索?

    ios - GCDAsyncUdpSocket, "Cannot bind socket more than once"

    javascript - 我如何防止玩家发送垃圾邮件移动消息 (Node.js)?

    php - 如何在没有 exec 的情况下获取 CPU 使用率和 RAM 使用率?

    kubernetes - CPU 资源单位 (millicore/millicpu) 是如何计算的?