Я написал многопоточное приложение для серверных игр, которое обрабатывает несколько одновременных подключений, используя NIO. К сожалению, этот сервер генерирует полную загрузку ЦП на одном ядре, как только первый пользователь подключается, даже когда этот пользователь фактически не отправляет или не получает никаких данных.Избегайте использования большого количества процессоров с помощью NIO
Ниже приведен код моей сетевой обработки (сокращенно важные части для удобства чтения). Класс ClientHandler
- это мой собственный класс, который выполняет сетевую абстракцию для игровой механики. Все остальные классы в приведенном ниже примере - от java.nio
.
Как вы можете видеть, он использует петлю while(true)
. Моя теория заключается в том, что когда ключ доступен для записи, selector.select()
будет немедленно возвращаться и вызывается clientHandler.writeToChannel()
. Но когда обработчик вернется, ничего не написав, ключ останется доступным для записи. Затем select вызывается снова немедленно и немедленно возвращается. Поэтому я занялся спиной.
Есть ли способ сконструировать цикл обработки сети таким образом, чтобы он спал, пока нет данных для отправки клиентомHandlers? Обратите внимание, что низкая латентность имеет решающее значение для моего случая использования, поэтому я не могу просто позволить ей спятить произвольное количество мс, когда у обработчиков нет данных.
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 - это все, что полезно ждать, когда каналы записываются * - сетевой буфер OS должен быть в состоянии позаботиться об этом. Если ваше узкое место в том, доступны ли данные для записи, вам следует подождать. (Например, на вашем «ClientHandler», я полагаю.) – millimoose
Я бы подумал об использовании фреймворка для поддержки NIO, такого как netty или mina. Большинство из них уже решены. Если у вас менее 1000 соединений, вы можете использовать блокировку ввода-вывода или NIO. –