Темы бездействуют = плохо?

Я хочу поддерживать около 10 000 одновременных HTTP-клиентов на небольшом кластере машин (как можно меньше). Я хотел бы поддерживать соединение с каждым клиентом, пока пользователь использует приложение, чтобы сервер мог отправлять обновления.

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


person ᴇʟᴇvᴀтᴇ    schedule 04.03.2014    source источник
comment
Проблема действительно заключается в разветвлении самих 10-тысячных потоков. Для этого потребуется тонна пространства стека. Если у вас есть память, это может сработать для вас, но в остальном решение NIO лучше, хотя и сложнее.   -  person Gray    schedule 05.03.2014
comment
stackoverflow.com/a/17771219/1305501   -  person nosid    schedule 05.03.2014
comment
Это тоже 10 тысяч разных HTTP-серверов? Если нет, вы можете рассмотреть возможность использования HTTP keepalive и повторного использования существующих соединений.   -  person fge    schedule 05.03.2014
comment
@Gray У вас есть оценка фактического использования стека типичным потоком? Максимальный размер стека не имеет значения, поскольку полный размер не привязан к физической оперативной памяти.   -  person Marko Topolnik    schedule 05.03.2014
comment
@CodeChimp Linux, правильно настроенный как машина серверного класса, может регулярно поддерживать миллионы подключений.   -  person Marko Topolnik    schedule 05.03.2014
comment
Значение по умолчанию зависит от ОС и JVM. См. здесь: onkarjoshi.com/blog/209/   -  person Gray    schedule 05.03.2014
comment
Я считаю, что максимальное пространство — это все, что имеет значение @MarkoTopolnik — по крайней мере, с точки зрения нехватки. Конечно, часть его не будет резидентом. Зависит от приложения, я думаю.   -  person Gray    schedule 05.03.2014
comment
@Gray Переключатель -Xss устанавливает максимальный размер на поток, я не знаю максимального общего пространства для стеков. Я знаю, что это во многом зависит, но вдохновленный недавним вопросом, я начал задаваться вопросом, что именно является приблизительной цифрой для типичной установки, скажем, JBoss и Spring. Моя интуиция подсказывает, что пиковое использование менее 64 КБ, и незанятый поток занимает, вероятно, минимум, который он может - одну страницу памяти (4 КБ, 8 КБ, что-то в этом роде).   -  person Marko Topolnik    schedule 05.03.2014
comment
Да, невозможно контролировать общее пространство, используемое всеми потоками, без контроля количества потоков и их пространства стека для каждого потока @MarkoTopolnik. Бьюсь об заклад, один поток иногда занимает намного больше, чем 4k. Сильно зависит от приложения, но я видел несколько трассировок стека monster, особенно в цепочках веб-обработчиков.   -  person Gray    schedule 05.03.2014
comment
@Gray Конечно, мы все знаем об этом ... скажем, он достигает 200 кадров стека, это четыре экрана трассировки стека. Каждый кадр может быть... в среднем 128 байт (многие методы очень малы и просто делегируют другим методам), что все еще меньше, чем общий размер стека 32 КБ.   -  person Marko Topolnik    schedule 05.03.2014
comment
Мои комментарии для потомков @MarkoTopolnik. Я знаю, что ты знаешь этот материал. По умолчанию это 64k или 128k или что-то, что может случиться в зависимости от кадров вызова, я думаю.   -  person Gray    schedule 05.03.2014
comment
@Gray Нет, я серьезно задаюсь этим вопросом, потому что поймал себя на том, что не принимаю все это во внимание. Если размер оказывается таким тривиальным, то классическая блокировка ввода-вывода в Java совсем не кажется чем-то плохим, вопреки моему нынешнему убеждению. И блокирующий ввод-вывод гораздо проще кодировать.   -  person Marko Topolnik    schedule 05.03.2014
comment
давайте продолжим это обсуждение в чате   -  person Gray    schedule 05.03.2014
comment
Вот соответствующая ссылка для ОП: usenix.org /legacy/events/hotos03/tech/full_papers/vonbehren/   -  person Marko Topolnik    schedule 05.03.2014
comment
@Marko: обратите внимание, что статье больше 10 лет.   -  person nosid    schedule 05.03.2014
comment
@nosid Я действительно заметил это, и если вы действительно прочитаете это, вы увидите, что сегодня аргументы применимы только более, чем десять лет назад.   -  person Marko Topolnik    schedule 05.03.2014
comment
@Марко, не могли бы вы обобщить свои мысли в полноценный ответ?   -  person ᴇʟᴇvᴀтᴇ    schedule 06.03.2014


Ответы (3)


Асинхронный ввод-вывод в основном означает, что ваше приложение выполняет большую часть планирования потоков. Вместо того, чтобы позволять ОС случайным образом приостанавливать ваш поток и планировать другой, вы имеете столько потоков, сколько ядер ЦП, и переходите к другим задачам в наиболее подходящие моменты, когда поток достигает операции ввода-вывода, что займет некоторое время.

Вышеприведенное кажется явным выигрышем с точки зрения производительности, но модель асинхронного программирования намного сложнее в нескольких отношениях:

  1. это не может быть выражено как вызов одной функции, поэтому рабочий процесс не очевиден, особенно когда рассматривается передача потока управления из-за исключений;
  2. без целенаправленной поддержки со стороны языка программирования идиомы очень беспорядочны: спагетти-код и/или чрезвычайно слабое отношение сигнал/шум являются нормой;
  3. в основном из-за 1. вышеуказанной отладки намного сложнее, так как трассировка стека не отражает прогресс в рамках единицы работы в целом;
  4. выполнение переходит от потока к потоку внутри пула (или даже нескольких пулов, где каждый уровень абстракции имеет свой собственный), поэтому профилирование и мониторинг с помощью обычных инструментов оказываются практически бесполезными.

С другой стороны, в современных ОС произошло множество благоприятных улучшений и оптимизаций, которые в основном устраняют недостатки производительности синхронного программирования ввода-вывода:

  • адресное пространство огромно, так что место, зарезервированное для стеков, не проблема;
  • фактическая физическая загрузка ОЗУ стеками вызовов не очень велика, поскольку только часть стека, фактически используемая потоком, выделяется в ОЗУ, а стек вызовов обычно не превышает 64 КБ;
  • переключение контекста, которое раньше было непомерно дорогим для большого количества потоков, было улучшено до такой степени, что его накладные расходы для всех практических целей незначительны.

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

https://www.usenix.org/legacy/events/hotos03/tech/full_papers/vonbehren/vonbehren_html/index.html

person Marko Topolnik    schedule 06.03.2014

В комментариях к вашему вопросу уже есть несколько хороших указателей.

Причина отказа от использования потоков 10 КБ заключается в том, что это требует ресурсов памяти, а память требует энергии. Модель программирования не является аргументом, потому что поток, сидящий на клиентском соединении, не должен быть тем же, который хочет опубликовать событие.

Пожалуйста, ознакомьтесь со стандартом веб-сокетов и моделью обработки асинхронных запросов в стандарте Servlet 3.0. Все последние серверы веб-приложений Java реализуют его сейчас (например, Glassfish и Tomcat), и это решение вашей проблемы.

На сам вопрос нельзя ответить, поскольку используемая ОС, JVM и сервер приложений отсутствуют. Однако вы можете довольно быстро протестировать его самостоятельно, просто создав сервлет или JSP с Thread.sleep(9999999) и выполнив на нем siege -c 10000 ....

person cruftex    schedule 04.03.2014
comment
память стоит энергии? Что это обозначает? - person Gray; 05.03.2014
comment
Как я уже сказал, чем больше памяти вы вставляете и чем быстрее память, тем выше потребляемая мощность вашего сервера. - person cruftex; 05.03.2014
comment
Еще одно предложение: вы смотрели на асинхронную обработку запросов в Servlet 3.0? - person cruftex; 05.03.2014
comment
1-2 ватта (Ddr3) на флешку, если ею никто не пользуется из-за парковки - person AnatolyG; 05.03.2014
comment
Можете ли вы предоставить некоторые ссылки на то, что память составляет значительный % мощности системы? - person Gray; 05.03.2014

10 000 одновременных HTTP-клиентов... в чем проблема бездействия потоков?

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

Очевидно, вы будете время от времени пробуждать каждый из ваших n-сот потоков, верно? И это момент, когда вы оплачиваете стоимость переключения контекста, которая может быть не такой уж и маленькой (время вызова системного планировщика, больше кэш-промахов и т. д.). См., например: http://www.cs.rochester.edu/u/cli/research/switch.pdf

И вам придется очень осторожно закреплять свои темы, чтобы не повлиять на системные. В результате архитектура «поток на соединение» (при блокировке ввода-вывода) может увеличить задержку системы по сравнению с асинхронным вводом-выводом. Но это все еще может работать для вашего случая, если большую часть времени почти все потоки припаркованы.

И последнее слово. Мы не знаем, сколько раз ваши потоки будут блокироваться на read() и сколько работы им нужно сделать для обработки полученных данных. Какое оборудование, ОС и сетевые интерфейсы будут использоваться... Итак, протестируйте прототип вашей системы.

person AnatolyG    schedule 04.03.2014
comment
@cruftex, но ты собираешься разбудить ветку? :) если нет, то просто прерви его - person AnatolyG; 05.03.2014