我編寫了一個多線程的遊戲服務器應用程序,它可以使用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