Лучше использовать select или многопоточность (или оба) при создании сервера, который будет одновременно отправлять / получать задачи для 200+ клиентов - PullRequest
0 голосов
/ 05 июня 2019

Я создаю сервер, который будет отправлять и получать задачи от более чем 200 клиентов одновременно (потенциально больше клиентов в будущем). На клиентах также будут работать фоновые механизмы, которые будут выполнять задачи и отправлять ответы на сервер без предварительного запроса. Я ожидаю, что большой объем информации будет передаваться в обе стороны. Я занимался исследованиями многопоточности и использования функции select, и мне интересно, учитывая некоторые параметры проекта, какая опция (или комбинация) будет наиболее эффективным масштабируемым решением, основанным на объеме трафика, который может произойти.

Любые предложения будут с благодарностью. Я был бы рад ответить на любые вопросы, чтобы дать больше ясности.

Ответы [ 2 ]

2 голосов
/ 05 июня 2019

Любой подход будет работать;насколько «лучше», это будет во многом зависеть от того, как вы определите слово «лучше».

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

  • С другой стороны, однопоточный подход ограничивает вас использованием одного ядра;он не сможет воспользоваться преимуществами современного многоядерного процессора, чтобы ускорить параллелизм.

  • С третьей стороны, многопоточный подход может стать проблематичным (ипотерять свой потенциал ускорения), если различным потокам / соединениям часто требуется доступ к любым разделяемым / изменяемым структурам данных.В этом сценарии «узкого места совместно используемых данных» потоки могут тратить много времени на блокировку мьютекса, и тогда вы все равно в основном вернетесь к использованию одного ядра.Если каждое соединение работает независимо от других (например, как часть простого веб-сервера) и не требует взаимодействия с другими потоками, это не должно вызывать беспокойства.

  • Многопоточность позволяет вам использовать блокирующий ввод / вывод (который проще реализовать, чем неблокирующий ввод / вывод), но блокирующий ввод / вывод ограничивает ваш контроль над потоками (например, как вы получаете поток для чистого выхода иликакое-либо другое не инициированное клиентом действие, если оно заблокировано на неопределенный срок в вызове recv()? У этой проблемы нет хороших решений, только плохие)

  • Single-многопоточность требует, чтобы вы использовали неблокирующий ввод-вывод (в противном случае один не отвечающий клиент может остановить обслуживание всех других клиентов, пока сервер заблокирован во время вызова send() или recv()), а неблокирующий ввод-выводсложно сделать правильно, так как вы должны изящно обрабатывать частичные чтения и частичные записи.

  • Если вашей программе когда-либо нужно выполнять нетривиальные вычисленияили файл ввода / вывода, обратите внимание, что однопоточный дизайн заставит всех клиентов ждать, пока вычисление (или ввод / вывод) для любого клиента завершится.В многопоточной конструкции, OTOH, клиенты B – Z могут продолжать обслуживаться на других ядрах / потоках, пока клиенты A заняты чтением с диска или сокращением чисел.

  • Затраты на порождениеи поддержание потоков будет варьироваться от одной ОС к другой.Если вы собираетесь запускать сотни потоков одновременно, вы можете сначала проверить, сможет ли ваша целевая ОС (и оборудование) справиться с этой нагрузкой эффективно.(Вы можете уменьшить накладные расходы на порождение и получение потоков через пул потоков за счет увеличения использования ОЗУ)

Я лично предпочитаю однопоточный / неблокирующий-IПодход / O, потому что блокировка ввода / вывода проблематична, если вы хотите, чтобы ваша программа могла корректно и надежно завершать работу (что вам и нужно, если только так вы можете выполнить, например, тестирование утечки памяти в valgrind).Если одноядерная производительность оказывается недостаточной, часто довольно просто расширить дизайн handle-N-sockets-on-1-thread на более мощную конструкцию handle-N-sockets-on-of-of-M-thread,и затем вы можете поиграть с различными значениями N и M, пока не найдете тот, который дает вам наилучшую производительность (например, установив M на количество ядер на хост-машине и передав новые принятые сокеты тому потоку, которыйв настоящее время обрабатывает наименьшее количество сокетов)

0 голосов
/ 05 июня 2019

Однажды я создал программу на Java, приложение для чата, чтобы каждое соединение с сервером, которое было установлено, представляло новый поток на сервере для управления данным клиентом.

Внутри класса Server была статическая переменная, управляющая подключением клиентов.

Я не знаю, является ли рекомендуемый подход к различным технологиям правильным способом ответа на ваш вопрос, но яПодумайте, что для вашего случая было бы неплохо взглянуть на платформу Erlang / Elixir , исходя из того, что она способна одновременно удерживать множество клиентов.

В настоящее время крупные компании, такие как Whatsapp, используют Erlang и Discord Elixir.

Я надеюсь, что мой ответ был полезным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...