Подтвердить что ты не робот

Один поток на клиента. Выполнимо?

Я пишу Java-сервер, который использует простые сокеты для приема соединений от клиентов. Я использую довольно простую модель, где каждое соединение имеет свой собственный поток, считывающий из него в режиме блокировки. Псевдокод:

handshake();

while(!closed) {
  length = readHeader(); // this usually blocks a few seconds
  readMessage(length);
}

cleanup();

(Темы создаются из Executors.newCachedThreadPool(), поэтому при их запуске не должно быть значительных накладных расходов)

Я знаю, что это немного наивная настройка, и она не будет хорошо масштабироваться для многих подключений, если потоки были выделены для ОС. Тем не менее, я слышал, что несколько потоков в Java могут совместно использовать один аппаратный поток. Это правда?

Зная, что я буду использовать Hotspot VM на Linux, на сервере с 8 ядрами и 12 ГБ оперативной памяти, вы думаете, что эта настройка будет хорошо работать для тысяч подключений? Если нет, то каковы альтернативы?

4b9b3361

Ответ 1

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

Общей альтернативой является использование селекторов и неблокирующих операций ввода-вывода, найденных в пакете java.nio.

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

Ответ 2

Это будет хорошо масштабироваться до сотен подключений, а не до тысяч. Одна из проблем заключается в том, что поток Java занимает довольно много стека (например, 256 КБ), и у ОС возникнут проблемы с расписанием всех ваших потоков.

Посмотрите на Java NIO или фреймворки, которые помогут вам начать делать сложные вещи более легко (например, Apache Mina)

Ответ 3

Чтобы иметь хорошую производительность при работе со многими сокетами, вы обычно используете подход select, так как Unix API обрабатывает однопоточные многопроцессорные приложения, которым требуется много ресурсов.

Это можно сделать с помощью пакета java.nio, который имеет класс Selector, который в основном способен проходить через все открытые сокеты и уведомлять вас о появлении новых данных.

Вы регистрируете все открытые потоки внутри одного Selector, а затем можете обрабатывать все из них только из одного потока.

Вы можете получить дополнительную информацию с учебником здесь

Ответ 4

JVM для Linux является используя одно к одному сопоставление потоков. Это означает, что каждый поток Java сопоставляется с одним потоком собственной ОС.

Таким образом, создание тысячи потоков или больше не является хорошей идеей, потому что это повлияет на вашу производительность (контекстное переключение, cache flushes/misses, synchronization latency и т.д.). Это также не имеет никакого смысла, если у вас меньше тысячи процессоров.

Единственное адекватное решение для обслуживания многих клиентов параллельно - использовать асинхронный ввод-вывод. Подробнее см. Java NIO.

См. также:

Ответ 5

Попробуйте Netty.

модель "один поток для каждого запроса" - это способ написания большинства серверов приложений Java. Ваша реализация может масштабироваться так же хорошо, как и они.

Ответ 6

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

Пакет java.nio решает это, предоставляя мультиплексирование сокетов/неблокирующее IO, которое позволяет связывать несколько соединений с одним Selector. Однако это решение намного сложнее добиться, чем простой подход блокировки из-за многопоточности и неблокирующего аспекта.

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

Ответ 7

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

ОБНОВЛЕНИЕ: Этот пост от Matthew Schmidt утверждает, что соединитель на основе NIO (написанный Филиппом Хаником) в Tomcat 6 достиг 16 000 одновременных соединений.

Если вы хотите написать свой собственный коннектор, посмотрите MINA, чтобы помочь с NIO абстракции. MINA также имеет функции управления, которые могут устранить необходимость в другом контейнере (если вы беспокоитесь о развертывании многих блоков и их эксплуатации и т.д.)

Ответ 8

Я бы предположил, что это зависит от того, что еще делает сервер, когда обрабатывает сообщения. Если это относительно легкий вес, то спецификация вашей машины должна ЛЕГКО справиться с просто обработкой соединений тысяч таких процессов. Возможно, есть еще десятки тысяч, но вам нужно всего две машины в одной сети, чтобы на самом деле эмпирически проверить ее и получить определенный ответ.

Ответ 9

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

Конечно, я думаю, что асинхронный ввод-вывод сделает все лучше и быстрее, но поможет вам с проблемами сокета и сетей. Только. Когда ваши потоки блокируются из-за ввода-вывода, JVM переведет его в режим сна и изменит его на другой поток до тех пор, пока не вернется блокирующий ввод-вывод. Но это заблокирует только поток. Ваш процессор продолжит работу и начнет выполнять другой поток. Таким образом, минус время для создания потока, то, как вы используете I/O, не влияет на вашу модель. Если вы не создаете потоки (используя пул), ваша проблема решена.

Ответ 10

Зачем откатываться? Вы можете использовать контейнер сервлета с сервлетами, очередью сообщений или ZeroMQ.