Страницы

Поиск по вопросам

понедельник, 4 февраля 2019 г.

Многопоточный веб сервер на C++

Здравствуйте. Интересует правильная реализация многопоточности веб сервера на C++. Проблема в следующем: Раньше я думал что для того чтобы сервер был многопоточным, достаточно лишь после принятия запроса от клиента создать для него новый поток, с помощью CreateThread, и так для каждого клиента.
Но позже узнал, что нет смысла создавать потоков больше чем количество логических процессоров в системе, так как будут потери при переключении между потоками, и программа будет работать медленнее.
Так как же быть? С одной стороны, на всяких хабрах и блогах пишут что достаточно создать поток функцией CreateThread. С другой стороны некий гуру пишет, что 100-200 потоков одновременно очень сильно снижают производительность на обычном компьютере.


Ответ

В целом, вы все верно пишете, но я бы сместил акценты. Машину действительно можно загрузить двумя сотнями потоков, но это не значит, что они ее обязательно загрузят или что это решение будет грузить меньше других - у любого сервиса есть предел, после которого он превращается в тыкву. В принципе, у вас три основных варианта: синхронная обработка запросов, которая будет очень медленной, многопоточная обработка запросов, и, наконец, асинхронная обработка запросов в том или ином виде, под которой я прячу всякие модели типа event loop и всё остальное. Последний вариант наиболее производительный, но очень сложный, и нужный только в таких монстрах, как nginx - вам для ваших целей наверняка хватит простой многопоточной обработки. Скорее всего, у вас внутри сервера не раз встретится такая штука, как блокирующее ожидание, во время которого процессор ждет ответа от внешнего сервера (базы данных) или файловой системы - в это время другие ОС передаст управление другим потокам, и вы окажетесь только в выигрыше. Единственная проблема, которую вносит многопоточная схема - это момент, при котором количество приходящих запросов начинает превышать количество запросов, которые может обработать сервер, и появляющиеся снова потоки начинают отбирать процессорное время, ухудшая производительность. Здесь, наконец, и появляется то, ради чего я писал весь этот ответ.
Вам не стоит делать createThread(). Создавать поток на каждый запрос печально по двум причинам - во-первых, это ненужные расходы на новый тред, а во-вторых - отсутствие контроля за количеством тредов, которое решило бы вышеозвученную проблему с переполнением сервера. Вам нужно реализовать пул тредов фиксированной величины; максимальная вместимость пула - это то, сколько одновременных запросов готов обработать сервер, а свежепришедшие запросы будут вынуждены ждать освобождения рабочего треда, что позволит сервису не превратиться-таки в тыкву. При этом размер пула может быть как равен числу процессоров, так и ичисляться сотнями - тут сложно посоветовать что-то конкретное, но в текущем проекте поднятие лимита с двадцати до пятисот тредов во встроенном сервере дало прирост производительности в десять раз (на самом деле не прирост, а более эффективное использование ресурсов, да и число в пятьсот было взято с потолка, и, наверное, стоит его уменьшить).
В общем, не волнуйтесь так сильно за модель, она более чем рабочая. Не забывайте только ограничивать возможности сервиса сверху.

Комментариев нет:

Отправить комментарий