我编写了一个多线程的游戏服务器应用程序,它可以使用NIO处理多个同时连接。不幸的是,当第一个用户连接时,即使该用户没有真正发送或接收任何数据,该服务器也会在一个内核上生成全部CPU负载。使用NIO避免CPU使用率过高
下面是我的网络处理线程的代码(缩写为可读性的基本部分)。类ClientHandler
是我自己的类,为游戏机制做网络抽象。以下示例中的所有其他课程均为java.nio
。
正如你所看到的,它使用了一个while(true)
循环。我的理论是,当一个密钥可写时,selector.select()
将立即返回,并调用clientHandler.writeToChannel()
。但是,当处理程序返回而没有写入任何内容时,密钥将保持可写。然后选择立即再次调用并立即返回。所以我忙得不可开交。
有没有办法设计网络处理循环的方式,只要没有数据要发送clientHandlers睡觉?请注意,低延迟对我的使用情况至关重要,所以我不能让它在没有处理程序有数据时让其睡眠任意数量的ms。
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();
}
}
}
我不确定在通道是*可写的时候,select()/ NIO是否有用于等待 - 操作系统网络缓冲区应该能够处理这个问题。如果你的瓶颈是数据是否可以写入,你应该等待。 (也就是我想你的'ClientHandler's) – millimoose 2013-02-14 18:24:58
我会考虑使用一个框架来支持像netty或mina这样的NIO。这些大部分的错误已经解决了。如果连接数少于1000,则可以使用阻塞IO或NIO。 – 2013-02-14 18:42:46