Spec-Zone.ru › Apache HTTP Server

Apache MPM событие

Описание: Вариант worker MPM, цель которого — использовать потоки только для подключений с активной обработкой
Статус: MPM
Идентификатор модуля: mpm_event_module
Файл исходного кода: event.c

Обзор

Модуль многопроцессорной обработки (MPM) event предназначен для одновременного обслуживания большего количества запросов, передавая некоторые задачи обработки в потоки слушателей, освобождая потоки-обработчики для обслуживания новых запросов.

Для использования event MPM добавьте --with-mpm=event в аргументы скрипта configure при сборке httpd.

Взаимодействие с MPM обработчика

event основан на worker MPM, который реализует гибридный многопроцессорно-многопоточный сервер. Один контрольный процесс (родитель) отвечает за запуск дочерних процессов. Каждый дочерний процесс создаёт фиксированное количество серверных потоков, как указано в директиве ThreadsPerChild, а также поток слушателя, который слушает подключения и передаёт их потоку-обработчику для обработки по мере их поступления.

Директивы конфигурации во время выполнения идентичны тем, которые предоставляет worker, с добавлением только AsyncRequestWorkerFactor.

Как это работает

Этот MPM пытается решить проблему «поддержания соединения» в HTTP. После завершения клиентом первого запроса он может сохранить соединение открытым, отправляя последующие запросы через тот же сокет, тем самым значительно экономя ресурсы на создании TCP-соединений. Однако традиционно Apache HTTP Server держит весь дочерний процесс/поток в ожидании данных от клиента, что имеет свои недостатки. Для решения этой проблемы этот MPM использует выделенный поток слушателя для каждого процесса, который обрабатывает как сокеты прослушивания, все сокеты в режиме «Поддержание соединения», сокеты, где обработчик и фильтры протокола выполнили свою работу, а также сокеты, где осталось только отправить данные клиенту.

Эта новая архитектура, использующая неблокирующие сокеты и современные функции ядра, доступные через APR (например, epoll в Linux), больше не требует настройки mpm-accept Mutex для предотвращения проблемы «толпы».

Общее количество подключений, которые может обрабатывать один блок процесса/потоков, регулируется директивой AsyncRequestWorkerFactor.

Асинхронные подключения

При использовании предыдущих MPM асинхронные подключения требовали выделенного потока-обработчика для каждого подключения, но не в случае event. Страница состояния mod_status отображает новые столбцы в разделе «Асинхронные подключения»:

Запись
Во время отправки ответа клиенту может произойти заполнение буфера TCP-записи из-за медленного соединения. В этом случае функция write() для сокета возвращает значения EWOULDBLOCK или EAGAIN, чтобы сокет стал доступным для записи после периода ожидания. Поток-обработчик, удерживающий сокет, может передать задачу ожидания потоку-слушателю, который в свою очередь перенаправит её первому доступному свободному потоку-обработчику после возникновения события для сокета (например, «сокет теперь доступен для записи»). Подробнее см. раздел «Ограничения».
Поддержание соединения
Обработка «Поддержания соединения» — самое простое улучшение по сравнению с MPM обработчика. После завершения обработки потоком-обработчиком ответа клиенту, он может передать обработку сокета потоку-слушателю, который, в свою очередь, ждёт события от ОС, например, «сокет готов для чтения». Если от клиента поступает новый запрос, слушатель перенаправит его первому доступному потоку-обработчику. И наоборот, если произойдёт KeepAliveTimeout, сокет будет закрыт потоком-слушателем. Таким образом, потоки-обработчики не несут ответственности за неактивные сокеты и могут быть повторно использованы для обслуживания других запросов.
Закрытие
Иногда MPM требуется выполнить «закрытие с задержкой», то есть отправить клиенту ошибку до того, как будут переданы все данные httpd. Отправка ответа и немедленное закрытие соединения некорректно, поскольку клиент (по-прежнему пытающийся отправить остаток запроса) получит сброс соединения и не сможет прочитать ответ httpd. Закрытие с задержкой ограничено по времени, но может занимать относительно длительное время, поэтому оно перекладывается на поток-обработчик (включая обработку завершения и фактическое закрытие сокета). Начиная с версии 2.4.28, это также относится к случаям, когда соединения в конечном итоге истекают (поток-слушатель никогда не обрабатывает подключения, кроме ожидания и обработки их событий).

Эти улучшения справедливы как для HTTP-, так и для HTTPS-соединений.

Плавное завершение процесса и использование доски учета

Этот mpm в прошлом демонстрировал некоторые узкие места в масштабируемости, что приводило к следующей ошибке: «доска учета заполнена, не достигнуто MaxRequestWorkers». MaxRequestWorkers ограничивает количество одновременных запросов, которые будут обрабатываться в любой момент времени, а также количество разрешённых процессов (MaxRequestWorkers / ThreadsPerChild); в то же время, доска учета представляет собой отображение всех работающих процессов и состояния их потоков-обработчиков. Если доска учета заполнена (то есть все потоки находятся в состоянии, отличном от idle), но количество активных обработанных запросов не равно MaxRequestWorkers, это означает, что некоторые из них блокируют новые запросы, которые могли быть обработаны, но вместо этого находятся в очереди (вплоть до ограничения, наложенного ListenBacklog). В большинстве случаев потоки застревают в состоянии «Плавное завершение», то есть ожидают завершения работы с TCP-соединением для безопасного завершения и освобождения слота на доске учёта (например, при обработке длительных запросов, медленных клиентов или подключений с поддержкой keep-alive). Два сценария очень распространены:

  • Во время плавного перезапуска родительский процесс сигнализирует всем своим дочерним процессам завершить работу и завершиться, а сам он перезагружает конфигурацию и порождает новые процессы. Если старые дочерние процессы продолжают работать в течение некоторого времени перед остановкой, доска учета будет частично занята, пока их слоты не освободятся.
  • Нагрузка сервера снижается, что приводит к остановке некоторых процессов httpd (например, из-за MaxSpareThreads). Это особенно проблематично, потому что когда нагрузка снова увеличивается, httpd попытается запустить новые процессы. Если этот паттерн повторяется, количество процессов может значительно увеличиться, в результате чего получится смесь старых процессов, пытающихся остановиться, и новых, пытающихся выполнить работу.

Начиная с версии 2.4.24, mpm-event стал более интеллектуальным и способен лучше обрабатывать плавные завершения. Некоторые из улучшений:

  • Разрешить использование всех слотов доски учета до ServerLimit. MaxRequestWorkers и ThreadsPerChild используются для ограничения количества активных процессов; в то же время ServerLimit также учитывает процессы, выполняющие плавное завершение, чтобы разрешить дополнительные слоты при необходимости. Идея состоит в использовании ServerLimit для указания httpd, сколько процессов в целом допускается, прежде чем это повлияет на ресурсы системы.
  • Принудительное завершение процессов, выполняющих плавное завершение, при закрытии их подключений в состоянии keep-alive.
  • Во время плавного завершения, если работающих потоков-обработчиков больше, чем открытых подключений для данного процесса, завершить эти потоки для более быстрого освобождения ресурсов (что может потребоваться для новых процессов).
  • Если доска учета заполнена, предотвратить начало завершения работы большего количества процессов из-за снижения нагрузки, пока старые процессы не завершатся (в противном случае ситуация ухудшится, когда нагрузка снова увеличится).

Описание последнего пункта полностью отображается в таблице сводки подключений через mod_status, в двух новых столбцах: «Слот» и «Остановка». Первый столбец указывает PID, а второй — если процесс останавливается или нет; дополнительное состояние «Да (старое поколение)» указывает, что процесс всё ещё работает после плавного перезапуска.

Ограничения

Улучшенная обработка подключений может не работать с определёнными фильтрами подключений, которые объявили о своей несовместимости с событием. В этих случаях этот MPM вернётся к поведению worker MPM и зарезервирует один поток-обработчик на каждое подключение. Все модули, поставляемые с сервером, совместимы с MPM события.

Аналогичное ограничение в настоящее время существует для запросов, связанных с фильтром вывода, которому нужно прочитать и/или изменить весь ответ. Если подключение к клиенту блокируется во время обработки данных фильтром, а объём данных, генерируемых фильтром, слишком велик для буферизации в памяти, поток, используемый для запроса, не освобождается, пока httpd ожидает, пока ожидаемые данные будут отправлены клиенту.
Для иллюстрации этого момента можно рассмотреть два случая: предоставление статического ресурса (например, файла CSS) по сравнению с предоставлением содержимого, полученного из FCGI/CGI или с прокси-сервера. Первый случай предсказуем, то есть MPM событий имеет полную информацию о завершении содержимого и может использовать события: поток-обработчик, обслуживающий содержимое ответа, может отправлять первые байты, пока не вернётся EWOULDBLOCK или EAGAIN, делегируя остальную часть потоку-слушателю. Этот поток, в свою очередь, ждёт события на сокете и делегирует работу по отправке остального содержимого первому свободному потоку-обработчику. В то время как во втором случае (FCGI/CGI/проксированное содержимое) MPM не может предсказать конец ответа, и потоку-обработчику нужно завершить свою работу, прежде чем передать управление потоку-слушателю. Единственная альтернатива — буферизовать ответ в памяти, но это не лучший вариант с точки зрения стабильности сервера и использования памяти.

Справочная информация

Модель событий стала возможной благодаря введению новых API в поддерживаемые операционные системы:

  • epoll (Linux)
  • kqueue (BSD)
  • событийные порты (Solaris)

До появления этих новых API приходилось использовать традиционные API select и poll. Эти API становятся медленными при обработке большого количества подключений или высокой частоте изменений набора подключений. Новые API позволяют отслеживать гораздо больше подключений и работают значительно эффективнее, когда набор подключений, подлежащих отслеживанию, меняется часто. Поэтому эти API позволили написать MPM событий, который масштабируется намного лучше при типичной HTTP-структуре с множеством неактивных подключений.

MPM предполагает, что реализация базового apr_pollset достаточно потокобезопасна. Это позволяет MPM избегать чрезмерной блокировки высокого уровня или необходимости будить поток-слушатель, чтобы отправить ему сокет keep-alive. В настоящее время это совместимо только с KQueue и EPoll.

Требования

Этот MPM зависит от атомарных операций сравнения и обмена APR для синхронизации потоков. Если вы компилируете для x86-целевого процессора и вам не нужно поддерживать 386-процессоры, или вы компилируете для SPARC и вам не нужно запускать на процессорах до UltraSPARC, добавьте --enable-nonportable-atomics=yes в аргументы скрипта configure. Это заставит APR реализовывать атомарные операции с помощью эффективных кодов операций, недоступных в более старых процессорах.

Этот MPM плохо работает на старых платформах, которые не имеют хорошей многопоточности, но требование к EPoll или KQueue делает это неактуальным.

  • Для использования этого MPM на FreeBSD рекомендуется FreeBSD 5.3 или выше. Однако можно запустить этот MPM на FreeBSD 5.2.1, если вы используете libkse (см. man libmap.conf).
  • Для NetBSD рекомендуется как минимум версия 2.0.
  • Для Linux рекомендуется ядро 2.6. Также необходимо убедиться, что ваша версия glibc скомпилирована с поддержкой EPoll.

Директива AsyncRequestWorkerFactor

Описание: Ограничение одновременных подключений на процесс
Синтаксис:
AsyncRequestWorkerFactor factor
По умолчанию: 2
Контекст: настройка сервера
Статус: MPM
Модуль: event
Совместимость: Доступно в версии 2.3.13 и выше

MPM event обрабатывает некоторые подключения асинхронно, где потоки обработчиков запросов выделяются только на короткое время по мере необходимости, а для других подключений зарезервирован один поток обработчика запросов на подключение. Это может привести к ситуациям, когда все потоки заняты и ни один поток обработчика запросов недоступен для обработки новой работы по установленным асинхронным подключениям.

Для решения этой проблемы MPM event выполняет два действия:

  • Он ограничивает количество принимаемых подключений на процесс в зависимости от количества свободных потоков обработчиков запросов;
  • Если все потоки заняты, он будет закрывать подключения в состоянии keep-alive, даже если таймаут keep-alive еще не истек. Это позволяет соответствующим клиентам повторно подключиться к другому процессу, у которого могут быть доступные потоки обработчика запросов.

Эта директива может быть использована для тонкой настройки лимита подключений на процесс. Процесс будет принимать новые подключения только если текущее количество подключений (без учета подключений в состоянии "закрытия") ниже:

ThreadsPerChild + (AsyncRequestWorkerFactor * количество свободных потоков)

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

(ThreadsPerChild + (AsyncRequestWorkerFactor * количество свободных потоков)) * ServerLimit

Пример

ThreadsPerChild = 10
ServerLimit = 4
AsyncRequestWorkerFactor = 2
MaxRequestWorkers = 40

idle_workers = 4 (average for all the processes to keep it simple)

max_connections = (ThreadsPerChild + (AsyncRequestWorkerFactor * idle_workers)) * ServerLimit
                = (10 + (2 * 4)) * 4 = 72

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

(AsyncRequestWorkerFactor + 1) * MaxRequestWorkers

Пример

ThreadsPerChild = 10
ServerLimit = 4
MaxRequestWorkers = 40
AsyncRequestWorkerFactor = 2

Если у всех процессов все потоки свободны, то:

idle_workers = 10

Мы можем рассчитать абсолютное максимальное количество одновременных подключений двумя способами:

max_connections = (ThreadsPerChild + (AsyncRequestWorkerFactor * idle_workers)) * ServerLimit
                = (10 + (2 * 10)) * 4 = 120

max_connections = (AsyncRequestWorkerFactor + 1) * MaxRequestWorkers
                = (2 + 1) * 40 = 120

Настройка AsyncRequestWorkerFactor требует знания о трафике, обрабатываемом httpd в каждом конкретном случае использования, поэтому изменение значения по умолчанию требует обширного тестирования и сбора данных с mod_status.

MaxRequestWorkers называлась MaxClients до версии 2.3.13. Вышеуказанное значение показывает, что старое имя не точно описывало его смысл для MPM event.

AsyncRequestWorkerFactor может принимать нецелые значения, например "1.5".

© 2018 The Apache Software Foundation
Licensed under the Apache License, Version 2.0.
https://httpd.apache.org/docs/2.4/en/mod/event.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API