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

Обслуживание нескольких клиентов с помощью одного процесса

В некоторых случаях для обслуживания нескольких клиентов может хватить и одного серверного процесса. Чтобы реализовать такой подход, необходимо воспользоваться одной из моделей ввода/вывода (epoll, мультиплексированный или сигнальный ввод/вывод), позволяющей одному процессу следить за событиями сразу нескольких файловых дескрипторов.

При использовании однопроцессной архитектуры сервер должен взять на себя роль планировщика. В многопроцессной модели данная роль обычно отводится ядру, которое само распределяет между потомками (и, следовательно, между клиентами) доступ к ресурсам сервера. Но когда процесс один, ему приходится следить за тем, чтобы никто из клиентов не смог получить эксклюзивный доступ к серверу и затруднить тем самым обработку остальных соединений. Мы разберем этот момент чуть более подробно в подразделе 59.4.6.

Использование серверных ферм

Среди других подходов, рассчитанных на высокие нагрузки, можно выделить применение нескольких серверных систем — серверной фермы.

Один из самых простых способов построения такой фермы (применяется в некоторых веб-серверах) заключается в циклическом распределении нагрузки с помощью DNS — когда полномочный сервер доменных имен, отвечающий за определенную зону, привязывает одно и то же доменное имя к нескольким IP-адресам (то есть несколько серверов имеют одно и то же имя). Последовательные запросы разрешения этого доменного имени обрабатываются в соответствии с циклическим алгоритмом, благодаря чему IP-адреса возвращаются в другом порядке. Дальнейшие подробности о циклическом распределении нагрузки путем DNS можно найти в книге [Albitz & Liu, 2006].

Циклическое распределение нагрузки является малозатратным и простым в настройке вариантом. Но у этого способа есть некоторые проблемы. Одной из них является кэширование, выполняемое удаленными DNS-серверами, из-за чего повторные запросы клиентов, находящихся на определенных компьютерах, не балансируются и всегда обрабатываются одним и тем же сервером. Кроме того, циклический алгоритм не предусматривает механизма обеспечения качественной балансировки (разные клиенты могут оказывать разную нагрузку на сервер) или высокой доступности (представьте, что один из серверов перестает работать или в его серверном приложении происходит сбой). Существует еще одна потенциальная проблема, характерная для многих архитектур, основанных на применении нескольких серверов — привязка к серверу (англ. server affinity). Речь идет о ситуации, когда последовательные запросы, выполняемые одним клиентом, должны быть направлены к одному и тому же серверу; это делается для того, чтобы сохранить актуальность информации о состоянии клиента, которая хранится на сервере.

Более гибким, но в то же время сложным решением является использование балансировщика нагрузки. Это подразумевает наличие балансирующего сервера, направляющего входящие клиентские запросы к одному из участников серверной фермы (для обеспечения высокой доступности может потребоваться запасной сервер, начинающий работать в случае сбоя в основном балансировщике). Так можно устранить проблемы, связанные с кэшированием в службе DNS, поскольку с точки зрения клиентов серверная ферма имеет единый IP-адрес (тот, который принадлежит балансировщику нагрузки). Балансировщик применяет специальные алгоритмы для измерения или оценки серверной нагрузки (возможно, исходя из показателей, предоставляемых участниками серверной фермы) и грамотно распределяет запросы между серверами. Он также автоматически обнаруживает сбои, происходящие с участниками фермы (и добавляет новые серверы, если нужно). Наконец, балансировщик также может поддерживать привязку к серверу. Подробности об этой методике можно найти в книге [Kopparapu, 2002].

56.5. Демон inetd

Если взглянуть на содержимое файла /etc/services, можно увидеть буквально сотни разных служб. Это говорит о том, что система теоретически способна выполнять большое количество серверных процессов. Однако большинство данных серверов обычно простаивает в ожидании редких запросов на подключение или датаграмм. Тем не менее все они занимают место в таблице процессов ядра и некоторую память и пространство подкачки, вследствие чего создается нагрузка на систему.

Перейти на страницу:

Похожие книги