Java-поток на модель подключения против NIO

Является ли неблокирующий Java NIO еще медленнее, чем ваш стандартный поток для асинхронного сокета на соединение?

Кроме того, если бы вы использовали потоки для каждого подключения, вы бы просто создали новые потоки или вы бы использовали очень большой пул потоков?

Я пишу сервер MMORPG на Java, который должен иметь возможность масштабировать 10000 клиентов, легко предоставляя достаточно мощное оборудование, хотя максимальное количество клиентов - 24000 (чего я считаю невозможным достичь для потока на модель подключения из-за ограничение на 15000 потоков в Java). Из трехлетней статьи я слышал, что блокирование ввода-вывода с нитью на модель подключения было на 25% быстрее, чем NIO (а именно, этот документ http://www.mailinator.com/tymaPaulMultithreaded.pdf), но может ли это быть достигнуто и в этот день? С тех пор Java сильно изменилась, и я слышал, что результаты были сомнительными при сравнении реальных сценариев, поскольку используемая VM не была Sun Java. Кроме того, поскольку это сервер MMORPG с несколькими параллельными пользователями, взаимодействующими друг с другом, будет ли использование синхронизации и методов обеспечения безопасности потоков снижать производительность до такой степени, когда один поточный селектор NIO, обслуживающий 10000 клиентов, будет быстрее? (вся работа не обязательно должна обрабатываться в потоке с помощью селектора, ее можно обрабатывать на рабочих потоках, например, как работает MINA/Netty).

Спасибо!

Ответ 1

Преимущества NIO следует брать с солью.

На HTTP-сервере большинство подключений - это соединения keep-alive, они неактивны в большинстве случаев. Было бы пустой тратой ресурсов, чтобы предварительно выделить поток для каждого.

Для MMORPG все очень по-другому. Я думаю, что соединения постоянно заняты получением инструкций от пользователей и отправлением пользователям последних системных состояний. Для подключения требуется большое количество времени.

Если вы используете NIO, вам придется постоянно перераспределять поток для подключения. Это может быть низкое решение для простого решения с фиксированным потоком за соединение.

Размер стека потоков по умолчанию довольно большой (1/4 МБ?), это основная причина, по которой ограниченные потоки могут быть ограничены. Попробуйте уменьшить его и посмотреть, поддерживает ли ваша система больше.

Однако, если ваша игра действительно очень "занята", это ваш процессор, который вам нужно больше всего волноваться. NIO или нет, очень сложно обрабатывать тысячи гиперактивных геймеров на машине.

Ответ 2

На самом деле существует 3 решения:

  • Несколько потоков
  • Один поток и NIO
  • Оба решения 1 и 2 в то же время время

Лучшее, что нужно сделать для производительности, - это иметь небольшое, ограниченное количество потоков и событий мультиплексной сети в этих потоках с помощью NIO, поскольку новые сообщения поступают через сеть.


Использование NIO с одним потоком - плохая идея по нескольким причинам:

  • Если у вас несколько процессоров или ядер, вы будете работать на холостом ходу, потому что вы можете использовать только одно ядро ​​за один раз, если у вас есть только один поток.
  • Если вам приходится блокировать по какой-либо причине (возможно, для доступа к диску), ваш CPU не работает, когда вы можете обрабатывать другое соединение, пока вы ждете диск.

Один поток на соединение - плохая идея, потому что он не масштабируется. Пусть говорят:

  • 10 000 соединений
  • 2 процессора с 2 ядрами каждый
  • только 100 потоков будут блокироваться в любой момент времени

Тогда вы можете решить, что вам нужны только 104 потока. Больше, и вы тратите ресурсы на управление дополнительными потоками, которые вам не нужны. Существует множество бухгалтерии под капотом, необходимых для управления 10 000 потоков. Это замедлит вас.


Вот почему вы объединяете два решения. Кроме того, убедитесь, что ваша виртуальная машина использует самые быстрые системные вызовы. Каждая ОС имеет свои уникальные системные вызовы для высокопроизводительного сетевого ввода-вывода. Убедитесь, что ваша виртуальная машина использует самую последнюю и самую последнюю. Я считаю, что это epoll() в Linux.

Кроме того, если вы будете использовать потоки за соединение, вы бы просто создавать новые темы или использовать очень большой пул потоков?

Это зависит от того, сколько времени вы хотите потратить на оптимизацию. Самое быстрое решение - создавать ресурсы, такие как потоки и строки, когда это необходимо. Затем пусть сбор мусора потребует их, когда вы закончите с ними. Вы можете повысить производительность, имея пул ресурсов. Вместо создания нового объекта вы запрашиваете пул для одного и возвращаете его в пул, когда закончите. Это добавляет сложности управления concurrency. Это может быть дополнительно оптимизировано с помощью алгоритмов concurrency, таких как неблокирующие алгоритмы. В новых версиях Java API есть несколько из них для вас. Вы можете потратить остаток своей жизни на эти оптимизации только на одну программу. Какое наилучшее решение для вашего конкретного приложения, вероятно, является вопросом, который заслуживает своего собственного сообщения.

Ответ 3

Если вы захотите потратить какую-то сумму на достаточно мощное оборудование, зачем ограничивать себя одним сервером. Google не использует один сервер, они даже не используют один сервер центров обработки данных.

Распространенное заблуждение состоит в том, что NIO позволяет не блокировать IO для своей единственной модели, сравнимой по стоимости. Если вы сравниваете блокировку NIO, вы можете получить ее на 30% быстрее, чем старый IO. т.е. если вы используете одну и ту же модель потоков и сравниваете только модели ввода-вывода.

Для сложной игры у вас гораздо больше шансов закончиться CPU, прежде чем вы нажмете 10K-соединений. Опять же проще иметь решение, которое масштабируется горизонтально. Тогда вам не нужно беспокоиться о том, сколько соединений вы можете получить.

Сколько пользователей может разумно взаимодействовать? 24? в этом случае у вас есть 1000 независимых групп, взаимодействующих. У вас не будет много ядер на одном сервере.

Сколько денег вы планируете потратить на пользователей на сервере? Вы можете купить 12-ядерный сервер с 64 ГБ памяти менее чем за 5000 фунтов стерлингов. Если вы разместите 2500 пользователей на этом сервере, вы потратили 2 фунта стерлингов на одного пользователя.

EDIT: У меня есть ссылка http://vanillajava.blogspot.com/2010/07/java-nio-is-faster-than-java-io-for.html, которая принадлежит мне.;) Я просмотрел это кем-то, кто является GURU Java Networking, и он в целом согласился с тем, что он нашел.

Ответ 4

Если у вас есть занятые соединения, что означает, что они постоянно отправляют вам данные и вы отправляете их обратно, вы можете использовать non-Blocking IO в сочетании с Akka.

Akka - это инструментарий с открытым исходным кодом и среда выполнения, упрощающая создание параллельных и распределенных приложений на JVM. Akka поддерживает несколько моделей программирования для concurrency, но это подчеркивает актерский concurrency, с вдохновением, взятым из Erlang. Языковые привязки существуют как для Java, так и для Scala.

Логика Akka не блокирует, поэтому она идеально подходит для асинхронного программирования. Используя Akka Actors, вы можете удалить Thread overhead.

Но если ваши потоки сокетов блокируются чаще, я предлагаю использовать Blocking IO в сочетании с Quasar

Quasar - это библиотека с открытым исходным кодом для простой и легкой JVM concurrency, которая реализует настоящие легкие потоки (волокна AKA) на JVM. Квазарные волокна ведут себя точно так же, как обычные потоки Java, за исключением того, что у них практически нет памяти и задач с переключением задач, поэтому вы можете легко создавать сотни тысяч волокон - или даже миллионы - в одной JVM. Quasar также предоставляет каналы для межволоконной связи, смоделированные после тех, которые предлагаются языком Go, в комплекте с селекторами каналов. Он также содержит полную реализацию модели актера, тщательно моделируемой после Erlang.

Квазарная логика блокируется, поэтому вы можете появиться, скажем, 24000 волокон, ожидающих разных соединений. Одним из положительных моментов в отношении квазара является то, что волокна могут легко взаимодействовать с обычными нитями. Также Quasar имеет интеграцию с популярными библиотеками, такими как Apache HTTP client или JDBC или Jersey и т.д., Поэтому вы можете использовать преимущества использования волокон во многих аспектах вашего проекта.
Вы можете увидеть хорошее сравнение этих двух фреймворков здесь.

Ответ 6

Как большинство из вас, ребята говорят, что сервер должен быть заблокирован в использовании ЦП до достижения 10k одновременных пользователей, я полагаю, что мне лучше использовать потоковый блокирующий (N) IO-подход, учитывая тот факт, что для этой конкретной MMORPG получение нескольких пакетов в секунду для каждого игрока не является чем-то необычным и может привести к потере селектора, если он будет использоваться.

Питер поднял интересный момент, что блокировка NIO быстрее, чем старые библиотеки, в то время как неопровержимые упоминания о том, что для занятого сервера MMORPG было бы лучше использовать потоки из-за того, сколько команд получено на игрока. Я не стал бы рассчитывать на то, что слишком много игроков простаивают в этой игре, поэтому для меня не должно быть проблем с нечистыми потоками. Я понял, что синхронизация по-прежнему требуется даже при использовании фреймворка на основе NIO, поскольку они используют несколько рабочих потоков, работающих одновременно, для обработки пакетов, полученных от клиентов. Переключение контекста может оказаться дорогостоящим, но я дам этому решению попробовать. Это относительно легко реорганизовать мой код, чтобы я мог использовать инфраструктуру NIO, если обнаружил, что есть узкое место.

Я считаю, что на мой вопрос был дан ответ. Я просто подожду еще немного, чтобы получить больше информации от большего количества людей. Спасибо за все ваши ответы!

EDIT: Я, наконец, выбрал свой курс действий. Я действительно был нерешительным и решил использовать JBoss Netty и разрешить пользователю переключаться между oio или nio с помощью классов

org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory;

Довольно приятно, что Netty поддерживает оба!