Spec-Zone.ru › Apache HTTP Server

Apache MPM event

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

Краткое описание

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

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

Связь с MPM Worker

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

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

Принцип работы

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

Благодаря этой новой архитектуре, использующей неблокирующие сокеты и современные возможности ядра, доступные через APR (например, epoll в Linux), больше нет необходимости настраивать mpm-accept Mutex для предотвращения эффекта thundering herd.

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

Асинхронные соединения

В предыдущих MPM для асинхронных соединений требовался выделенный рабочий поток, а в event — нет. На странице состояния mod_status в разделе асинхронных соединений отображаются новые столбцы:

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

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

Корректное завершение процессов и использование таблицы состояний

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

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

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

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

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

Ограничения

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

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

Дополнительные сведения

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

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

До появления этих новых API приходилось использовать традиционные API select и poll. При обработке большого количества соединений или частых изменениях их набора эти API работают медленно. Новые API позволяют отслеживать гораздо больше соединений и значительно эффективнее работают, когда набор отслеживаемых соединений часто меняется. Благодаря этим API стало возможным создать MPM event, который гораздо лучше масштабируется при типичной для 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.

До версии 2.3.13 директива MaxRequestWorkers называлась MaxClients. Приведённое выше значение показывает, что старое название не вполне точно описывало её назначение в 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