Выполнение всеми участниками серверного пула вызова accept() является не единственным вариантом. Если пул состоит из отдельных процессов, то родитель может сам выполнить accept() и затем передать файловый дескриптор с новым соединением одному из свободных дочерних процессов (методика, которая для этого применяется, кратко описана в подразделе 57.13.3). При нахождении в пуле потоков главная программа может выполнить accept() и затем проинформировать одного из свободных участников пула о том, что для заданного дескриптора доступен новый клиент.
В некоторых случаях для обслуживания нескольких клиентов может хватить и одного серверного процесса. Чтобы реализовать такой подход, необходимо воспользоваться одной из моделей ввода/вывода (epoll, мультиплексированный или сигнальный ввод/вывод), позволяющей одному процессу следить за событиями сразу нескольких файловых дескрипторов.
При использовании однопроцессной архитектуры сервер должен взять на себя роль планировщика. В многопроцессной модели данная роль обычно отводится ядру, которое само распределяет между потомками (и, следовательно, между клиентами) доступ к ресурсам сервера. Но когда процесс один, ему приходится следить за тем, чтобы никто из клиентов не смог получить эксклюзивный доступ к серверу и затруднить тем самым обработку остальных соединений. Мы разберем этот момент чуть более подробно в подразделе 59.4.6.
Среди других подходов, рассчитанных на высокие нагрузки, можно выделить применение нескольких серверных систем —
Один из самых простых способов построения такой фермы (применяется в некоторых веб-серверах) заключается в
Циклическое распределение нагрузки является малозатратным и простым в настройке вариантом. Но у этого способа есть некоторые проблемы. Одной из них является кэширование, выполняемое удаленными DNS-серверами, из-за чего повторные запросы клиентов, находящихся на определенных компьютерах, не балансируются и всегда обрабатываются одним и тем же сервером. Кроме того, циклический алгоритм не предусматривает механизма обеспечения качественной балансировки (разные клиенты могут оказывать разную нагрузку на сервер) или высокой доступности (представьте, что один из серверов перестает работать или в его серверном приложении происходит сбой). Существует еще одна потенциальная проблема, характерная для многих архитектур, основанных на применении нескольких серверов —
Более гибким, но в то же время сложным решением является использование
Если взглянуть на содержимое файла /etc/services, можно увидеть буквально сотни разных служб. Это говорит о том, что система теоретически способна выполнять большое количество серверных процессов. Однако большинство данных серверов обычно простаивает в ожидании редких запросов на подключение или датаграмм. Тем не менее все они занимают место в таблице процессов ядра и некоторую память и пространство подкачки, вследствие чего создается нагрузка на систему.