Персональный
сайт
Игоря
Сысоева


 
english
обо мне
 
sysoev.ru
 
nginx
 
mod_accel
 документация    
 изменения    
 скачать    
mod_realip
mod_deflate
программирование
всякая всячина
windows
freebsd
apache
pppd
unix
web
 
 

Модуль mod_accel

 

17.02.2005

Версия 1.0.34

Модуль mod_accel предназначен для использования Apache в режиме акселератора, то есть, он реализует функциональность ProxyPass модуля mod_proxy. Однако, в отличие от mod_proxy, mod_accel правильно взаимодействует с бэкендом (подробнее об этом ниже), может учитывать при кэшировании cookie, использовать busy lock'и и ограничивать число соединений с бэкендом, позволяет записывать в лог результаты своей работы и, наконец, ответы mod_accel могут быть перекодированы Russian Apache'ем и сжаты модулем mod_deflate.

Модуль mod_accel не может работать в режиме обычного прокси-сервера как mod_proxy. Однако вряд ли целесообразно использовать Apache в этом качестве вместо Squid и Oops. Тем не менее, часто при конфигурировании mod_proxy вместе с функциональностью акселератора по ошибке разрешают стандартное проксирование (ProxyRequests on), что приводит к появлению очередного публичного прокси-сервера. И в то же время, использование Squid или Oops в качестве акселератора менее удобно, чем Apache, поскольку Apache может также обрабатывать обычные непроксируемые запросы.

mod_accel представляет из себя собственно модуль, использующий Apache EAPI, и набор патчей для Apache и модулей mod_proxy, mod_rewrite, mod_ssl и mod_charset (Russian Apache). Кроме того, в дистрибутиве есть три модуля - mod_randban, mod_quoted и mod_freeze, модифицирующие тело передаваемого ответа. Первый изменяет случайное значение в URL'ах для вызова баннеров, второй перекодирует русские символы в конструкциях вида '<a href="/show?%80%81%82">', а третий изменяет некоторые активные теги и параметры.

Содержание

Установка
Динамическая загрузка
Как это работает
Когда ответ может быть получен из кэша
Busy lock'и
Ограничение числа запросов
Примитивное распределение нагрузки и отказоустойчивость
Какие ответы кэшируются
Заметки, с помощью которых можно управлять модулем
Как работать с cookie
Что можно записать в лог
Чистка кэша
Директивы
AccelAddVia
AccelAddXForwardedFor
AccelBkRcvBuffSize
AccelBkRcvSockBuffSize
AccelBusyLock
AccelCacheCookie
AccelCacheRoot
AccelCacheSendSize
AccelCacheSetCookie
AccelClean
AccelCleanLastAccessed
AccelContentTail
AccelDefaultExpire
AccelIgnoreAuth
AccelIgnoreExpires
AccelIgnoreNoCache
AccelInvalidate
AccelLastModifiedFactor
AccelMaxStale
AccelModRewriteLocation
AccelNoCache
AccelNoPass
AccelPass
AccelPassCookie
AccelPassServer
AccelPassXAccel
AccelRetry5XX
AccelRequestBuffSize
AccelRevalidateUser
AccelReverse
AccelSendSize
AccelSetXHost
AccelSetXRealIP
AccelSetXURI
AccelSetXURL
AccelTimeout
AccelUnlinkNoCached
AccelWaitBeforeBodyRead
Известные ошибки и особенности
Менеджер кэша
Модуль mod_randban
RandomBanner
Модуль mod_quoted
RecodeQuoted
Модуль mod_freeze
FreezeStart
FreezeTags
Взаимодействие с модулем mod_rewrite
Настройка производительности
Как mod_proxy взаимодействует с бэкендом

Установка

Дистрибутив необходимо распаковать, перейти в каталог с исходными текстами и выполнить команду ./configure, указав ей путь к исходными текстам Apache и EAPI. После конфигурирования нужно выполнить команду make:

tar zxf mod_accel-1.0.34.tar.gz
cd mod_accel-1.0.34
./configure
    --with-apache=<apache_dir>
    --with-eapi=<eapi_dir>
make
Исходные тексты EAPI можно взять из дистрибутива mod_ssl, сам модуль mod_ssl при этом устанавливать не нужно, например:
./configure
    --with-apache=../apache_1.3.20
    --with-eapi=../mode_ssl-2.8.4-1.3.20/pkg.eapi
Если же Apache будет собираться с использованием модуля mod_ssl, то параметр --with-eapi не нужен, поскольку mod_ssl сам устанавливает Apache EAPI.

Нужно заметить, что версии EAPI, идущие в составе mod_ssl-2.8.13-1.3.27 и mod_ssl-2.8.14-1.3.27 не правильно работают с разделяемой памятью, необходимой при использовании busy lock'ов и ограничении числа соединений и ждущих процессов. Поэтому, начиная с версии 1.0.28, mod_accel при конфигурировании проверяет наличие EAPI с ошибками и патчит его при необходимости.

При использовании модулей mod_charset или mod_ssl их необходимо установить до mod_accel, поскольку при установке mod_accel эти модули необходимо патчить. mod_ssl рекомендуется устанавливать так, как описано в разделе "The flexible APACI-only way [FOR REAL HACKERS]" файла INSTALL из дистрибутива mod_ssl.

При конфигурировании можно указать ещё несколько параметров:

  • --with-mod_randban - установить исходный текст модуля mod_randban в каталог <apache_dir>/src/modules/extra/;

  • --with-mod_quoted - установить исходный текст модуля mod_quoted в каталог <apache_dir>/src/modules/extra/;

  • --with-mod_freeze - установить исходный текст модуля mod_freeze в каталог <apache_dir>/src/modules/extra/;

  • --without-mod_rewrite - не патчить модуль mod_rewrite;

  • --without-cachemgr - не собирать cachemgr. Параметр появился в версии 1.0.2;

  • --with-patch=<path_to_patch> - использовать указанную программу patch. Параметр появился в версии 1.0.7.

Начиная с версии 1.0.7, параметры --without-mod_charset и --without-mod_ssl не поддерживаются.

Команда make накладывает патчи на исходные тексты Apache и модулей mod_proxy, mod_rewrite, mod_charset и mod_ssl, устанавливает при необходимости Apache EAPI и копирует исходные тексты модуля mod_accel в каталог <apache_dir>/src/modules/accel/. Кроме того, если указаны дополнительные модули mod_randban, mod_quoted или mod_freeze то они копируются в каталог <apache_dir>/src/modules/extra/.

Если предполагается использовать busy lock'и и ограничение числа соединений и ждущих процессов, то нужно также установить библиотеку mm (она доступна по адресу http://www.engelschall.com/sw/mm/):

tar zxf mm-1.2.1.tar.gz
cd mm-1.2.1
./configure --disable-shared
make
С параметром --disable-shared библиотека будет собрана статически. Библиотеку можно собрать динамически, но в этом случае после сборки её нужно установить командой make install.

При конфигурировании Apache необходимо активировать модули, EAPI и EAPI_MM (если используется библиотека mm):

cd <apache_dir>
EAPI_MM=<mm_dir> ./configure
    ...
    --enable-rule=EAPI
    --activate-module=src/modules/extra/mod_randban.o
    --activate-module=src/modules/extra/mod_quoted.o
    --activate-module=src/modules/extra/mod_freeze.o
    --activate-module=src/modules/accel/libaccel.a
    ...
До версии mod_accel 1.0.7 модуль mod_accel должен быть указан после модулей mod_quoted и mod_randban. Начиная с версии 1.0.7, порядок не имеет значения. В случае, если библиотека mm собрана динамически, переменная EAPI_MM будет выглядеть таким образом:
EAPI_MM=SYSTEM

После установки Apache необходимо сделать каталог, в котором будут создаваться файлы кэша и временные файлы. Пользователь, от имени которого работают процессы Apache, должны иметь права на чтение и запись в этот каталог. Этот каталог нужно указать в директиве AccelCacheRoot, например:

AccelCacheRoot  cache

Динамическая загрузка

Модуль mod_accel может подгружаться динамически. Начиная с версии 1.0.7, порядок загрузки не имеет значения.

До версии 1.0.7, как и при статической сборке, модуль mod_accel должен быть указан после модулей mod_quoted и mod_randban:

LoadModule  randban_module  modules/mod_randban.so
LoadModule  quoted_module   modules/mod_quoted.so
LoadModule  accel_module    modules/libaccel.so
Если модуль mod_ssl тоже загружается динамически, то он должен быть указан до модуля mod_accel (это также не имеет значения, начиная с версии 1.0.7):
LoadModule  ssl_module      modules/libssl.so
LoadModule  accel_module    modules/libaccel.so

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

В фазе трансляции URI mod_accel проверяет, не совпадает ли начало URI c каким-либо из префиксом, указанных в списке директив AccelPass. Если совпадение найдено, то просматривается список директив AccelNoPass. Если в этом списке не найден ни один префикс или регулярное выражение, соответствующие URI, то обработчиком этого запроса будет "accel-handler", который направит этот запрос на другой сервер. Например, запрос "http://frontend/test/one.cgi?test=1" директива

AccelPass           /test/       http://backend/test/
преобразует в запрос "http://backend/test/one.cgi?test=1".

Если ответ на запрос может быть закэширован, то проверяется его наличие в кэше. Ключом для поиска в кэше является результат хэш-функции md5, в качестве аргумента которой передаётся преобразованный URL, в нашем случае "http://backend/test/one.cgi?test=1". Если ответ найден в кэше, то он считывается блоками, размер которых задаётся директивой AccelCacheSendSize и отдаётся клиенту.

Если же ответа нет, или он устарел, или же ответ вообще некэшируемый, то запрос должен быть передан бэкенду. После этого проверяются busy lock'и, число соединений с бэкендом и число ждущих процессов. Затем, если у запроса есть тело (например, запрос вида POST), то оно считывается в буфер, размер которого задаётся директивой AccelRequestBuffSize. Если тело запроса превышает размер буфера, то оно сохраняется во временный файл. И только после этого mod_accel соединяется с бэкендом (или с одним из них), передаёт ему запрос и затем читает заголовок ответа. Сокеты бэкенда и клиента переводятся в неблокирующий режим и клиенту передаётся заголовок ответа. После этого читается тело ответа в буфер приёма ответа, размер которого задаётся директивой AccelBkRcvBuffSize. Как только размер считанного становится равным размеру этого буфера, создаётся временный файл и в него записывается содержимое буфера. По мере получения ответа он отдаётся клиенту блоками размером, заданным директивой AccelSendSize.

В процессе передачи ответа клиенту он может модифицироваться цепочкой модулей, например, модулями mod_randban, mod_quoted и mod_freeze.

Как только ответ от бэкенда получен полностью, сокет бэкенда закрывается, а сокет клиента переводится в блокирующий режим. После этого ответ может быть записан в кэш и может использоваться другими процессами Apache.

Когда ответ может быть получен из кэша

Прежде чем запрос будет передан бэкенду, проверяется наличие ответа в кэше. Это происходит в следующем порядке:

  • Если активна директива AccelNoCache или запрос не вида GET или HEAD, то он передаётся бэкенду и заметка "accel_st" будет равна "PASS".

  • Если установлена заметка "accel_nocache" или переменная среды "ACCEL_NOCACHE", то запрос передаётся бэкенду и заметка "accel_st" будет равна "NTNC".

  • После этого проверяется заголовок "Authorization". Если директивы AccelIgnoreAuth и AccelRevalidateUser не активны, то запрос с авторизацией всегда передаётся бэкенду. На этом этапе заметка "accel_st" равна "AUTH".

Если предыдущие проверки пройдены, то считается, что ответ на запрос в принципе может быть закэширован.

  • Далее, если директива AccelInvalidate активна, то проверяется наличие строки обновления в конце URL'а. Если она есть, запрос передаётся бэкенду и заметка "accel_st" будет равна "INVL".

  • Если директива AccelIgnoreNoCache не активна, то проверяются заголовки "Pragma: no-cache", "Cache-Control: no-cache" и "Cache-Control: max-age=0". При наличии любого из перечисленных заголовков запрос передаётся бэкенду и заметка "accel_st" будет равна "PGNC".

  • Затем ответ ищется в кэше. Если он не найден, то делаются проверки busy lock'ов, числа соединений с бэкендом и числа ждущих процессов. Если ответ не обрабатывается другим процессом, число соединений или ждущих процессов не достигло максимума, то запрос передаётся бэкенду и заметка "accel_st" будет равна "MISS".

  • Если ответ есть и директива AccelIgnoreNoCache не активна, то проверяется заголовок "Cache-Control: max-age=<число>", задающий максимальное время хранения ответа в кэше. Если ответ хранится дольше запрошенного времени, то после проверки busy lock'ов, числа соединений с бэкендом и числа ждущих процессов запрос передаётся бэкенду и заметка "accel_st" будет равна "AGED".

  • Если директива AccelRevalidateUser активна, то запрос передаётся бэкенду и заметка "accel_st" будет равна "RVUS".

  • После этого проверяется, не устарел ли ответ. Если устарел, то после проверки busy lock'ов, числа соединений с бэкендом и числа ждущих процессов запрос передаётся бэкенду и заметка "accel_st" будет равна "EXPR".

  • И наконец, если ответ не устарел или же соединение с бэкендом запрещено, то он возвращается клиенту и заметка "accel_st" будет равна "HIT".

Если ответ на запрос есть в кэше, но уже устарел, то в запрос к бэкенду добавляется заголовок "If-Modified-Since". При получении ответа с кодом 304 (HTTP_NOT_MODIFIED) содержимое кэша обновляется в соответствии с полученными заголовками и клиенту передаётся ответ из кэша.

Если ответ на запрос клиента может быть не полным, например, если в запросе клиента есть заголовки "If-Modified-Since","If-Unmodified-Since","If-Match","If-None-Match" или запрашивается часть ответа (byte-range), а ответа в кэше нет, но он в принципе может быть закэширован, то эти заголовки удаляются при запросе к бэкенду с тем, чтобы получить полный ответ и, возможно, сохранить его в кэше. Клиенту же возвращается запрашиваемая им часть ответа (byte-range) или возвращается ответ с кодом 304 (HTTP_NOT_MODIFIED) при соблюдении условий, указанных в заголовках типа "If-Modified-Since".

То же самое относится к запросу HEAD. Если ответа в кэше нет, но он в принципе кэшируемый, то к бэкенду делается запрос GET, чтобы опять же получить полный ответ и, возможно, сохранить его в кэше. Клиенту же возвращается только заголовок ответа.

Busy lock'и

Прежде чем запрос уходит к бэкенду, предварительно проверяются busy lock'и, число соединений с бэкендом и число ждущих процессов. Busy lock позволяет не передавать бэкенду кэшируемый запрос в том случае, если точно такой же запрос уже обрабатывается бэкендом. Busy lock устанавливается директивой AccelBusyLock и гарантирует, что в течение заданного времени от начала выполнения запроса к бэкенду только один из процессов Apache передаст этот запрос бэкенду. Если в кэше есть устаревший ответ на этот запрос, но он устарел не более чем на время, заданное директивой AccelMaxStale, то все остальные процессы будут возвращать этот устаревший ответ из кэша. В противном случае все остальные процессы, выполняющие такой же запрос, будут или ждать ответ, который получит первый процесс, или же ждать истечения busy lock'а, установленного этим процессом. AccelBusyLock позволяет установить три значения busy lock'а. Первый используется в случае, если ответа в кэше нет или он устарел более чем на время, заданное директивой AccelMaxStale. Второй используется в случае, если ответ в кэше устарел менее чем на время, заданное предыдущей директивой. Второе значение рекомендуется устанавливать больше, чем первое. Третий задаёт максимальное время, которое процесс может провести в busy lock'е, поскольку процесс может ждать последовательно освобождения нескольких busy lock'ов. Предположим, что пришли одновременно три одинаковых запроса и у на заданы такие параметры:

AccelBusyLock   20 25 30
Первый запрос будет передан бэкенду. Два других ждут в busy lock'е. Предположим, что по истечении 20 секунд ответ от бэкенда не получен. Второй запрос передаётся бэкенду, третий же продолжает ждать первые два ответа. Но максимальное суммарное время его ожидания не может быть более 30 секунд, поэтому если в течение этого времени бэкенд не ответит, то третий запрос будет передан бэкенду при условии, что число соединений с бэкендом не достигло максимума.

Допустим, у нас заданы такие параметры:

AccelBusyLock   10 15
AccelMaxStale   60
Проиллюстрируем логику работы busy lock'ов следующим примером:

  • 00:00. Пришёл запрос, ответа на который нет в кэше. Поскольку в данный момент никто не обрабатывает такой же запрос, то процесс передаёт запрос бэкенду, предварительно установив busy lock, который истечёт в 00:10 (первое значение AccelBusyLock равно 10 секундам).

  • 00:04. Пришёл второй запрос. Поскольку первый ещё обрабатывается, то процесс, принявший второй запрос, ждёт освобождения busy lock'а или появления ответа в кэше.

  • 00:07. Пришёл третий запрос. Поскольку первый всё ещё обрабатывается, то процесс, принявший третий запрос, ждёт освобождения busy lock'а или появления ответа в кэше так же, как и второй процесс.

  • 00:10. Поскольку busy lock первого процесса истёк, один из процессов, допустим, третий устанавливает ещё один busy lock и передаёт запрос бэкенду. Второй процесс, как и прежде, ждёт освобождения уже нового busy lock'а или появления ответа в кэше. Необходимо заметить, что по истечении busy lock'а только один процесс из ждущих может установить ещё один busy lock.

  • 00:12. Первый процесс получил ответ и записал его в кэш. Заметка "accel" для этого запроса будет равна "MISS/-/0/- 200/CTL/300 12 ...", что означает - ответа не было в кэше и он был получен от бэкенда без ожидания в busy lock'е, код ответ бэкенда - 200 (HTTP_OK), ответ кэшируемый, время кэширования указано в заголовке "Cache-Control" и равно 300 секундам ("CTL/300"), бэкенд обрабатывал запрос 12 секунд. Начиная с этого момента, в течение секунды второй процесс увидит ответ в кэше и начнёт отдавать его клиенту. Заметка "accel" для второго запроса будет равна "HIT/-/8/- HIT/-/- - ...", что означает - процесс получил ответ из кэша, подождав 8 секунд(00:04-00:12) в busy lock'е.

  • 00:23. Третий процесс получил ответ и обновил ответ в кэше. Заметка "accel" будет равна "MISS/-/3/- 200/CTL/300 13 ...", что означает - прождав в busy lock'е 3 секунды (00:07-00:10), процесс передал запрос бэкенду, код ответ бэкенда - 200 (HTTP_OK), ответ кэшируемый, время кэширования указано в заголовке "Cache-Control" и равно 300 секундам, бэкенд обрабатывал запрос 13 секунд. Необходимо отметить, что ответ в кэше может одновременно отдаваться и обновляться несколькими процессами.

  • 03:10. Пришёл четвёртый запрос. Поскольку ответ ещё не устарел, то он отдаётся из кэша. Заметка "accel" будет равна "HIT/-/-/- HIT/-/- - ...", что означает - ответ был получен из кэша.

  • 06:16. Пришёл пятый запрос. Поскольку запрос устарел в 05:23 и в данный момент никто не обрабатывает такой же запрос, то процесс передаёт запрос бэкенду, предварительно установив busy lock.

  • 06:20. Пришёл шестой запрос. Ответ на него уже устарел, но ещё не очень старый (AccelMaxStale равен 60 секундам, то есть, ответ будет совсем устаревшим в 06:23). Поэтому, поскольку такой же запрос уже обрабатывается и его busy lock истечёт в 06:31 (второе значение AccelBusyLock равно 15 секундам), то ответ берётся из кэша и заметка "accel" будет равна "HIT/57/0/U HIT/-/- - ...", что означает - хотя ответ устарел на 57 секунд(05:23-06:20), но поскольку в тот момент запрос уже обновлялся ("U"), то ответ был получен из кэша без ожидания в busy lock'е.

  • 06:24. Пришёл седьмой запрос. Ответ на него уже совсем устарел, но поскольку такой же запрос обрабатывается и его busy lock для седьмого запроса истечёт в 06:26 (для ответов устаревших больше, чем на время, заданное директивой AccelMaxStale, используется первое значение AccelBusyLock, в нашем случае равное 10 секундам), то процесс ждёт освобождения busy lock'а или появления ответа в кэше.

  • 06:25. Процесс, работающий с пятым запросом, получил ответ от бэкенда и обновил его в кэше. Заметка "accel" будет равна "EXPR/53/0/- 200/CTL/300 13 ...", что означает - ответ устарел на 53 секунды (05:23-06:16) и он был получен от бэкенда без ожидания в busy lock'е, код ответ бэкенда - 200 (HTTP_OK), ответ кэшируемый, время кэширования указано в заголовке "Cache-Control" и равно 300 секундам, бэкенд обрабатывал запрос 13 секунд. Начиная с этого момента, в течение секунды процесс, работающий с седьмым запросом, увидит ответ в кэше и начнёт отдавать его клиенту. Заметка "accel" будет равна "HIT/61/1/N HIT/-/- - ...", что означает - ответ устарел на 61 секунду (05:23-06:24), и подождав 1 секунду (06:24-06:25) в busy lock'е, процесс получил обновлённый ответ из кэша.

Ограничение числа запросов

Busy lock'и предназначены для уменьшения нагрузки на бэкенд. В то же время возможны ситуации, когда бэкенд не справляется со обработкой полученных запросов, а новые запросы продолжают поступать. В этом случае процессы фронтенда, обрабатывающие новые запросы, будут или ждать ответа от бэкенда, подсоединившись к нему, или пытаться соединиться с бэкендом, или же ждать в busy lock'е. Число процессов фронтенда при этом может достигнуть своего максимума и если фронтенд кроме проксирования этого бэкенда должен заниматься ещё и другими запросами, то эти запросы не будут обслуживаться. Таким образом, один перегруженный бэкенд может сделать недоступными остальные ресурсы, обслуживаемые его фронтендом. Для избежания подобных ситуаций в директиве AccelPass можно указать флаги MC и MW, позволяющие ограничить соответственно число соединений с бэкендом и число процессов, ждущих этот бэкенд в busy lock'е. Если достигнут максимум по одному из параметров и в кэше есть ответ на запрос, то он всегда возвращается клиенту, независимо от того, насколько он устарел. Если же ответа в кэше нет, то возвращается ошибка 503 (HTTP_SERVICE_UNAVAILABLE). Кроме того, такие же флаги можно указывать в директиве RewriteRule модуля mod_rewrite вместе с флагом P, если только модуль mod_accel не был сконфигурирован с параметром --without-mod_rewrite.

Примитивное распределение нагрузки и отказоустойчивость

Примитивное распределение нагрузки и отказоустойчивость можно реализовать на основе DNS, когда одно имя соответствует нескольким IP-адресам. Необходимо заметить, что указание альтернативных адресов в файле /etc/hosts для этого не подойдёт - нужно делать запросы к DNS-серверу. Кроме того, необходимо указать флаг NR в директиве AccelPass для запрещения определения IP-адреса бэкенда на старте. В этом случае фронтенд будет определять IP-адреса бэкендов при каждом обращении и соединяться с одним из нескольких бэкендов. Для генерации ключа в кэше используется URL в том виде, в каком он получается после замены локального префикса на проксируемый без определения IP-адресов, поэтому с точки зрения mod_accel ответы от разных бэкендов на один и тот же запрос равнозначны и попавший в кэш ответ одного сервера будет отдаваться клиентам до тех пор, пока не устареет. То же самое относится к busy lock'ам и ограничениям на соединения, то есть, группа бэкендов рассматривается как один бэкенд.

Примитивную отказоустойчивость можно реализовать, задав небольшое значение второму параметру директивы AccelTimeout. По умолчанию таймаут на соединение равен примерно 75 секундам. При нормальной работе соединение, как правило, устанавливается за доли секунды. Если установить этот таймаут на несколько секунд, то по его истечении mod_accel попытается подсоединиться к следующему бэкенду. Однако не всегда бэкенд может вернуть результат, даже если фронтенд смог с ним соединиться. Поэтому по истечении таймаутов, заданных параметрами директивы AccelTimeout, при обрыве соединения с бэкендом или при получении ответа с результатом 5XX mod_accel закрывает соединение и переходит к следующему бэкенду. С помощью директивы AccelRetry5XX off можно запретить пытаться соединиться с другим бэкендом при получении ответа с результатом 5XX. Результаты попыток соединений фиксируются в заметках, то есть, их можно увидеть в логе, но они никак не используются для выбора бэкенда при следующих запросах.

Какие ответы кэшируются

Решение от том, кэшировать полученный ответ или нет, принимается в следующем порядке:

  • Прежде всего, заметка "accel_st" не должна быть равна "PASS", "NTNC" и "AUTH". Кроме того, ответ от бэкенда должен быть получен полностью и иметь код 200 (HTTP_OK), 301 (HTTP_MOVED_PERMANENTLY) или 302 (HTTP_MOVED_TEMPORARILY).

    Если заметка "accel_st" равна "NTNC", то заметка "accel_bc" будет равна "NNC". Полученный ответ не кэшируется, но и старый ответ в кэше не удаляется.

  • Затем проверяется специальный заголовок "X-Accel-Expires: <число>", в котором указывается, через сколько секунд ответ устареет. Секунды отсчитываются от времени, указанного в заголовке "Date". Хотя при этом возможно некоторое расхождение во времени между фронтендом и бэкендом, тем не менее, заголовок "X-Accel-Expires: 0" всегда запрещает кэширование, независимо от расхождения во времени. В отличие от заголовков "Cache-Control" и "Expires", "X-Accel-Expires" клиенту не передаётся. Если решение о кэшировании принято на основании этого заголовка, то заметка "accel_bc" будет равна "XAE", а заметка "accel_bct" - времени, указанному в этом заголовке.

  • Если предыдущий заголовок отсутствует, то проверяется заголовок "Cache-Control", в котором учитываются два значения -"no-cache" и "max-age=<число>". Как и в предыдущем случае, "max-age=0" не зависит от расхождения во времени. Проверку этого заголовка можно запретить с помощью директивы AccelIgnoreExpires on. Если решение о кэшировании принято на основании этого заголовка, то заметка "accel_bc" будет равна "CTL", а заметка "accel_bct" - времени, указанному в этом заголовке. Если заголовок имеет значение "no-cache", то заметка "accel_bct" будет равна "N".

  • Затем проверяется заголовок "Expires". В случае, если время, указанное в заголовке "Date", меньше либо равно времени, указанного в заголовке "Expires", ответ не кэшируется. Проверку этого заголовка можно запретить с помощью директивы AccelIgnoreExpires on. Если решение о кэшировании принято на основании этого заголовка, то заметка "accel_bc" будет равна "EXP", а заметка "accel_bct" - разнице во времени, указанного в заголовке "Expires" и "Date".

  • Если предыдущие проверки ни к чему не привели и код ответа равен 302 (HTTP_MOVED_TEMPORARILY), то ответ не кэшируется. Если код ответа равен 301 (HTTP_MOVED_PERMANENTLY), то ответ кэшируется на время, заданное директивой AccelCleanLastAccessed, заметка "accel_bc" будет равна "MVD", а заметка "accel_bct" - времени кэширования. Если же код ответа равен 200 (HTTP_OK), то выполняются следующие проверки:

  • Если указана директива AccelLastModifiedFactor, а в ответе есть заголовок "Last-Modified", то время устаревания вычисляется по такой формуле:

    Expires = (Date - LastModified) * AccelLastModifiedFactor / 100
    

    Время устаревания отсчитывается от текущего времени на фронтенде. Если решение о кэшировании принято на этом этапе, то заметка "accel_bc" будет равна "LMF", а заметка "accel_bct" - вычисленному времени.

  • Наконец, время устаревания можно указать директивой AccelDefaultExpire, отсчитываемое от текущего времени на фронтенде. В этом случае заметка "accel_bc" будет равна "ADE", а заметка "accel_bct" - указанному времени.

  • Если в результате не удалось определить время устаревания, то ответ не кэшируется и заметки "accel_bc" и "accel_bct" будут равны "-".

Заметки, с помощью которых можно управлять модулем

Начиная с версии 1.0.2, поведением модуля mod_accel можно управлять с помощью заметки "accel_nocache". Если выставить эту заметку, то mod_accel всегда будет делать запрос к бэкенду, не проверяя кэш. Полученный ответ никогда не кэшируется, а если в кэше ответ уже есть, то он не удаляется. Начиная с версии 1.0.24, то же самое можно делать с помощью переменной среды "ACCEL_NOCACHE".

Начиная с версии 1.0.18, модулю mod_accel в заметке "accel_request_body" можно передавать тело запроса.

Начиная с версии 1.0.18, модулю mod_accel в заметке "accel_rewrite_response" можно передавать адрес процедуры для обработки ответа бэкенда. Процедура должна иметь следующий прототип:

int rewrite_handler(accel_rec *a);
Она вызывается после получения заголовка и, возможно, первой части ответа от бэкенда. Если ответ бэкенда нужно вернуть неизменным, то процедура должна вернуть значение DECLINED и mod_accel продолжит обработку запроса. При возврате любого другого значения предполагается, что процедура сама полностью обработала запрос.

Как работать с cookie

Если ответы могут быть закэшированы, то заголовок "Cookie" удаляется из запроса к бэкенду, а заголовок "Set-Cookie" - из ответа бэкенда, поскольку cookie может влиять на содержимое ответа. Однако, если Вы уверены, что cookie никак не влияют на содержимое ответов, то можете установить директиву AccelPassCookie on. Если Вы уверены, что заголовок "Set-Cookie" имеет смысл кэшировать, то можете установить директиву AccelCacheSetCookie on. Кроме того, можно кэшировать ответ в зависимости от cookie с помощью директивы AccelCacheCookie. Наконец, если авторизация сделана с помощью cookie, то можно использовать директиву AccelRevalidateUser.

Что можно записать в лог

Деятельность mod_accel можно анализировать с помощью заметок, которые можно записывать в лог в виде %{accel}x:

  • accel_r — первая строка запроса, переданного бэкенду, например, "GET http://127.0.0.1/test/ HTTP/1.0". Адрес бэкенда в этой заметке всегда представлен в численном виде.

  • accel — заметка, в которой объединены все нижеперечисленные заметки, её формат такой: "accel_st/accel_et/accel_lt/accel_ls accel_bs/accel_bc/accel_bct accel_bt accel_bnr/accel_bfr/accel_br accel_fl"

  • accel_st — состояние запроса. Может принимать значения "PASS", "NTNC", "AUTH", "INVL", "PGNC", "MISS", "AGED", "EXPR", "RVUS" и "HIT" в порядке, описанном в разделе "Когда ответ может быть получен из кэша" и значение "RMVD", если файл был удалён из кэша с помощью менеджера кэша.

  • accel_et — время в секундах, показывающее насколько устарел хранящийся в кэше ответ на запрос.

  • accel_lt — время в секундах, в течение которого процесс был в busy lock'е.

  • accel_ls — состояние busy lock'а, ограничений на момент отдачи ответа на запрос и показатель свежести ответа. Может быть комбинацией таких значений - "U", "C", "W" и "N". Символ "U" означает, что запрос уже обновляется другим процессом. Это значение всегда появляется в сочетании с заметкой accel_st, равной "HIT", например, "HIT/5/0/U", то есть, ответ на запрос есть в кэше, он устарел на 5 секунд, но поскольку это меньше AccelMaxStale и ответ уже обновляется, то клиенту вернули этот устаревший ответ. Символ "C" означает, что число соединений с бэкендом достигло значения MC, а символ "W" - что число процессов, ждущих в busy lock'ах ответа от бэкенда достигло значения MW. Значения "C" и "W" появляется в сочетании с заметкой accel_st, равной "HIT" или "MISS", например, "HIT/10/0/C" (в кэше есть устаревший на 10 секунд ответ и поскольку число соединений достигло максимума, то клиенту вернули этот устаревший ответ) и "HIT/15/5/CW" (в кэше есть устаревший на 15 секунд ответ, но этот ответ уже обновляется, процесс прождал в busy lock'е 5 секунд и вернул устаревший ответ, так как к этому времени число соединений и ждущих процессов достигло максимума). Символ "N" означает, что после ожидания в busy lock'е процесс получил обновлённый запрос. Например, этот символ в примере, подобном предыдущему - "HIT/15/5/CWN", означает, что клиенту вернули обновлённый запрос.

  • accel_bs — код ответа бэкенда, если же ответ был получен из кэша, то эта заметка будет равна "HIT".

  • accel_bc — на основании чего было принято решение о кэшировании ответа. Может принимать значения - "NNC", "XAE", "CTL", "EXP", "MVD", "LMF" и "ADE" в порядке, описанном в разделе "Какие ответы кэшируются".

  • accel_bct — время кэширования в секундах.

  • accel_bt — время обработки запроса бэкендом в секундах.

  • accel_bnr - количество операций чтения ответа бэкенда. Может быть на единицу больше реального числа, в частности для маленьких ответов, размер которых меньше кадра ethernet'а реальное количество чтений будет 2 (весь ответ принимается за одно чтение, при втором чтении будет получен конец файла), а в заметке будет записано 3. Тем не менее, эта заметка отражает реальное положение вещей в случае большого ответа и некоторой задержки, заданной в директиве AccelWaitBeforeBodyRead.

  • accel_bfr - суммарный размер заголовка ответа и части тела ответа, полученной за первое чтение после задержки, заданной в директиве AccelWaitBeforeBodyRead.

  • accel_br - полный размер ответа, полученный от бэкенда в байтах. Включает в себя заголовок ответа.

  • accel_fl - флаги ответа. Может быть комбинацией значений "R", "Q" и "F". "R" указывает, что ответ был обработан модулем mod_randban, "Q" - модулем mod_quoted, а "F" - модулем mod_freeze.

Чистка кэша

Чистка кэша делается отдельным процессом - сборщиком мусора, который запускается основным сервером Apache и им же контролируется. После старта сборщик мусора переключается на пользователя и группу, указанных в директивах User и Group. Большую часть времени этот процесс спит, просыпаясь лишь для чистки кэша. Его можно отличить от других процессов Apache с помощью команды ps - в имени процесса присутствует строка "garbage collector". Директива AccelClean задаёт, как часто нужно чистить кэш. Чистить можно раз в сутки в заданное время или же через определённый интервал времени, который отсчитывается от времени запуска процесса или от времени завершения последней чистки кэша. Необходимо отметить, что при старте сборщик не сразу начинает чистку кэша, а ждёт одну минуту, поскольку, как правило, во время перезапуска Apache нагрузка на сервер увеличивается. Кроме того, во время чистки сборщик засыпает на одну секунду после проверки каждой 1000 файлов. Под файлами в данном случае подразумеваются не только обычные файлы кэша, но файлы каталогов, потому что при задании уровней кэша "1 2", число файлов каталогов будет 4096. Если необходимо срочно почистить кэш, не дожидаясь истечения интервала, то можно просто завершить процесс командой kill и Apache перезапустит сборщика снова.

Сборщик удаляет файлы в кэше, которые устарели более часа назад или же к ним не обращались в течение времени, заданного директивой AccelCleanLastAccessed и не следит за размером кэша, поскольку это имеет смысл в случае проксирования Интернета, а не конкретного сайта, размер которого более или менее известен. Кроме того, сборщик удаляет временные файлы, к которым не обращались более часа и каталоги и файлы кэша, оставшиеся после изменения структуры кэша.

Директивы


Директива AccelAddVia

syntax: AccelAddVia on|off
default: AccelAddVia off
context: server config, virtual host, location, files

Директива определяет, добавлять или нет в заголовок "Via" в запросе, передаваемом бэкенду, строку о фронтенде. Добавляемая строка имеет следующий вид - "'версия протокола запроса' 'доменное имя фронтенда' ('версия mod_accel')", например, "1.1 www.domain.com (mod_accel/1.0.34)".


Директива AccelAddXForwardedFor

syntax: AccelAddXForwardedFor on|off
default: AccelAddXForwardedFor off
context: server config, virtual host, location, files

Директива определяет, добавлять или нет в заголовок "X-Forwarded-For" в запросе, передаваемом бэкенду, IP-адрес, с которого был сделан запрос к фронтенду.


Директива AccelBkRcvBuffSize

syntax: AccelBkRcvBuffSize размер
default: AccelBkRcvBuffSize 16
context: server config, virtual host, location, files

Директива задаёт размер буфера в килобайтах для приёма ответа от бэкенда. Если размер ответа превышает размер буфера, то содержимое буфера записывается во временный файл.


Директива AccelBkRcvSockBuffSize

syntax: AccelBkRcvSockBuffSize размер
default: AccelBkRcvSockBuffSize 0
context: server config, virtual host, location, files

Директива задаёт размер TCP-буфера ядра в килобайтах для приёма ответа от бэкенда. Размер устанавливается системным вызовом setsockopt() с параметром SO_RCVBUF. Если значение директивы равно нулю, то setsockopt() не вызывается и используется размер TCP-буфера, заданный по умолчанию.


Директива AccelBusyLock

syntax: AccelBusyLock "время" ["время" ["время"]]
default: AccelBusyLock 0 0 AccelTimeout
context: server config, virtual host, location, files

Директива задаёт три значения busy lock'ов. Первый используется в случае, если ответа в кэше нет или он устарел более чем на время, заданное директивой AccelMaxStale. Второй используется в случае, если ответ в кэше устарел менее чем на время, заданное предыдущей директивой. Третий задаёт максимальное время ожидания в busy lock'е. Подробно логика работы busy lock'ов описана в разделе "Busy lock'и". Оба параметра можно задавать в виде строки, например, "2 minutes 30 seconds" или в виде одного числа, означающего секунды.

Если второй параметр не задан, то он равен первому.

Если третий параметр не задан, то он равен первому параметру директивы AccelTimeout. Третий параметр появился в mod_accel, начиная с версии 1.0.7. В более ранних версиях его значение равно первому параметру директивы AccelTimeout.


Директива AccelCacheCookie

syntax: AccelCacheCookie off|all|[!]строка|[!]~[*]регулярное выражение
default: AccelCacheCookie off
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.2 и выше

Директива задаёт имена cookie, которое будет учитываться при кэшировании ответа сервера. При задании имени cookie можно использовать имя или регулярное выражение. Символы "~*" позволяют задать регулярное выражение без учёта регистра символов. Параметр "all" задаёт все cookie. Символ "!" позволяет исключить часть cookie. Параметр "off" запрещает учёт cookie при кэшировании. Предположим, у нас заданы директивы

AccelPass           /test/       http://backend/test/
AccelCacheCookie    all  !~^id
В этом случае запрос "http://frontend/test/one.html" будет перенаправлен в запрос "http://backend/test/one.html". Ключом для поиска в кэше является результат хэш-функции md5, в качестве аргумента которой передаётся преобразованный URL, в нашем случае "http://backend/test/one.cgi?test=1". Однако, если пользователь передал cookie, например, "userid=12345; pref=34; id=92", то аргументом хэш-функции md5 будет строка "http://backend/test/one.cgi?test=1 pref=34; userid=12345". Перед тем, как cookie добавляются к URL, они сортируются по алфавиту. Если запрос будет передан бэкенду, то к нему тоже попадут только указанные cookie - "pref=34; userid=12345".

Директива учитывает только заголовки "Cookie", приходящие от клиента, и никак не влияет на заголовок "Set-Cookie", переданный от бэкенда.

В одном блоке может быть задано несколько директив AccelCacheCookie. Если в блоке не указана ни одна директива, то они наследуются из предыдущего. Директивы, заданные в вложенном блоке не объединяются с директивами, заданными в предыдущем блоке. Параметр "off" отменяет действие всех остальных параметров.

В версии 1.0.2 допустимы только два вида параметров - "off" и строка. Кроме того, в одной директиве можно указывать только один параметр и имена cookie не сортируются.


Директива AccelCacheRoot

syntax: AccelCacheRoot путь [1|2 [1|2 [1|2]]] [noauto]
default: AccelCacheRoot нет 1 2
context: server config

Директива задаёт каталог, в котором будут создаваться файлы кэша и временные файлы. Весь кэш должен находиться на одном дисковом разделе, так как временные файлы перемещаются в кэш операцией link(). Если указан не абсолютный путь, то каталог определяется относительно ServerRoot. Пользователь, от имени которого работают процессы Apache, должны иметь права на чтение и запись в этот каталог. С помощью этой директивы можно задать уровень вложенности каталогов кэша. По умолчанию используются 16 каталогов первого уровня с именами 0 .. f, и 256 каталогов второго уровня с именами 00 .. ff. Кроме того, параметром "noauto" можно запретить автоматическое создание каталогов в кэше. Автоматическое создание нежелательно на сильно нагруженных серверах поскольку при каждом создании нового файла кэша будет так же сделана попытка создать все промежуточные каталоги. Для повышения производительности рекомендуется перед запуском Apache предварительно создать все каталоги в кэше с помощью скрипта create_cache и запретить автоматическое создание каталогов. Например, директива

AccelCacheRoot /var/cache 1 1 noauto
указывает, что кэш находится в каталоге /var/cache, глубина его вложенности 2, имена каталогов обоих уровней — 0 .. f, и эти каталоги не нужно создавать автоматически в процессе работы.


Директива AccelCacheSendSize

syntax: AccelCacheSendSize размер
default: AccelCacheSendSize 8
context: server config, virtual host, location, files

Директива определяет размер буфера в килобайтах для передачи ответа из кэша клиенту.


Директива AccelCacheSetCookie

syntax: AccelCacheSetCookie on|off
default: AccelCacheSetCookie off
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.23 и выше

Директива определяет, можно ли сохранять в кэш заголовок "Set-Cookie".


Директива AccelClean

syntax: AccelClean "[@] время"
default: AccelClean "1 hour"
context: server config

Директива задаёт время или интервал чистки кэша. Если в строке указан символ "@", то чистка делается раз в сутки в указанное время, иначе значение означает интервал времени, по истечении которого производится чистка кэша. Примеры использования:

AccelClean "1 hour 30 minutes"
AccelClean "@ 5 hours"


Директива AccelCleanLastAccessed

syntax: AccelCleanLastAccessed "время"
default: AccelCleanLastAccessed "24 hours"
context: server config

Директива определяет максимальное время неактивной жизни файла в кэше. Если в течение этого времени к файлу не обращались, то файл будет удалён при чистке кэша, даже если он не считается устаревшим.


Директива AccelContentTail

syntax: AccelContentTail длина
default: AccelContentTail 32
context: server config
compatibility: mod_accel 1.0.0-1.0.10

Директива задаёт размер буфера, используемого модулями, модифицирующими тело ответа.

Начиная с версии 1.0.11, размер буфера настраивается автоматически и директива упразднена.


Директива AccelDefaultExpire

syntax: AccelDefaultExpire "время"
default: AccelDefaultExpire 0
context: server config, virtual host, location, files

Директива задаёт время хранения ответа при условии, что определить это время другими способами не удалось.


Директива AccelIgnoreAuth

syntax: AccelIgnoreAuth on|off
default: AccelIgnoreAuth off
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.5 и выше

Директива определяет, игнорировать или нет заголовок "Authorization" в запросе клиента. Директиву можно применять в случае, если авторизация проводится фронтендом и ответ бэкенда не зависит от пользователя и следовательно может выдаваться из кэша. Кроме того, некоторые программы пакетного скачивания, в частности FlashGet, используют заголовок "Authorization" в роли заголовка "Pragma: no-cache".


Директива AccelIgnoreExpires

syntax: AccelIgnoreExpires on|off
default: AccelIgnoreExpires off
context: server config, virtual host, location, files

Директива определяет, игнорировать или нет заголовки "Expires" и "Cache-Control", выдаваемые бэкендом.


Директива AccelIgnoreNoCache

syntax: AccelIgnoreNoCache on|off
default: AccelIgnoreNoCache off
context: server config, virtual host, location, files

Директива определяет, игнорировать или нет заголовки "Pragma: no-cache", "Cache-Control: no-cache" и "Cache-Control: max-age=<число>" в запросе, переданным клиентом.

При нажатии на Reload Netscape посылает заголовок "Pragma: no-cache".
Netscape 6 и Mozilla при нажатии на Reload посылает два заголовка "Pragma: no-cache" и "Cache-Control: max-age=0", а при нажатии Shift-Reload — "Pragma: no-cache" и "Cache-Control: no-cache".

При нажатии на Refresh MSIE под Windows до версии 5.5 включительно посылает заголовок "Pragma: no-cache" только в том случае, если ему указано использовать прокси-сервер. MSIE 5.5SP1 при нажатии Control-Refresh всегда посылает заголовок "Pragma: no-cache" или "Cache-Control: no-cache", в зависимости от того, какой версии HTTP он делает запрос. Версии под Macintosh не посылают этот заголовок вообще никогда.

При нажатии на Reload Konqueror посылает заголовки "Pragma: no-cache" и "Cache-Control: no-cache".

Заголовок "Cache-Control: max-age=<число>" обычно передаётся Squid'ом, в его конфигурации он задаётся директивой refresh_pattern и по умолчанию равен трём суткам или 259200 секундам.


Директива AccelInvalidate

syntax: AccelInvalidate строка|off
default: AccelInvalidate off
context: server config, virtual host, location, files

Директива задаёт строку, при нахождении которой в конце URL содержимое кэша для данного запроса будет обновлено. Например, если задана директива

AccelInvalidate   _INVALIDATE
то для обновления запроса "http://frontend/test/one.hml?arg=1", нужно сделать запрос "http://frontend/test/one.hml?arg=1_INVALIDATE".


Директива AccelLastModifiedFactor

syntax: AccelLastModifiedFactor число
default: AccelLastModifiedFactor 0
context: server config, virtual host, location, files

Директива задаёт число в процентах для определения времени кэширования на основании текущего времени и заголовка "Last-Modified". Подробно это описано в разделе "Какие ответы кэшируются".


Директива AccelMaxStale

syntax: AccelMaxStale "время"
default: AccelMaxStale 0
context: server config, virtual host, location, files

Директива задаёт время, в течение которого устаревший ответ на запрос может отдаваться из кэша при условии, что один из процессов Apache уже получает обновлённый ответ от бэкенда. Оба параметра можно задавать в виде строки, например, "2 minutes" или в виде одного числа, означающего секунды.


Директива AccelModRewriteLocation

syntax: AccelModRewriteLocation on|off
default: AccelModRewriteLocation off
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.30 и выше

Директива указывает, использовать ли mod_rewrite для переписывания заголовков "Location" и "Refresh".


Директива AccelNoCache

syntax: AccelNoCache on|off
default: AccelNoCache off
context: server config, virtual host, location, files

Директива указывает, могут ли ответы на запросы кэшироваться.


Директива AccelNoPass

syntax: AccelNoPass префикс|~[*]регулярное выражение
default: нет
context: server config, virtual host

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

AccelPass           /            http://backend/
AccelNoPass         /images/  /download/  ~*\.jpg$
то запросы, начинающиеся на "/images/" или "/download/", не будут передаваться бэкенду. Кроме того, запросы заканчивающиеся на ".jpg" или ".JPG" также не будут передаваться бэкенду. Символы "~*" перед регулярным выражением указывают игнорирование регистра. Необходимо заметить, что регулярные выражения применяются только к URI, и не применяются к PATH_INFO и аргументам запроса.

В одном блоке может быть задано несколько директив. Директивы, заданные в вложенном блоке объединяются с директивами, заданными в предыдущем блоке.

В mod_accel до версии 1.0.3 нельзя использовать регулярные выражения с игнорированием регистра. Кроме того, в директиве можно использовать только один параметр и между регулярным выражением и символом "~" может стоять пробел.


Директива AccelPass

syntax: AccelPass префикс url [флаги]
default: нет
context: server config, virtual host

Директива определяет, какие запросы должны передаваться на другой сервер. Например, директива

AccelPass           /test/       http://backend/test/
будет перенаправлять все запросы, начинающиеся на /test/, в запросы "http://backend/test/". Например, запрос "http://frontend/test/one.html" будет перенаправлен в запрос "http://backend/test/one.html".

Если не установлен флаг PH, то в запрос всегда включается заголовок "Host", содержащий имя и порт (если порт не равен 80) сервера, на который перенаправлен запрос.

Если ответ на запрос представляет из себя редирект, то заголовок "Location" при необходимости корректируется, то есть, если заголовок "Location" содержит "http://backend/test/two.html", то он будет изменён на "http://frontend/test/two.html". В этом смысле директива

AccelPass           /test/       http://backend/test/
эквивалента двум директивам модуля mod_proxy
ProxyPass           /test/       http://backend/test/
ProxyPassReverse    /test/       http://backend/test/
Кроме заголовка "Location" также корректируется заголовок "Refresh".

Начиная с версии 1.0.29, mod_accel также корректирует заголовок "Destination", если имя хоста в этом заголовке совпадает с содержимым заголовка "Host", или же если URI не абсолютный.

Начиная с версии 1.0.11, в ответ на запрос /test mod_accel возвращает редирект на URL с добавленным слэшом, то есть, http://frontend/test/.

Директивы просматриваются в порядке их описания, поэтому более общие префиксы нужно располагать в конце, например:

AccelPass           /test/       http://backend1/test/
AccelPass           /            http://backend2/

В директиве можно использовать следующие флаги:

  • MC=<число> - Maximum Connect;
    MW=<число> - Maximum Waiting;
    MP=<H|P|L|Tтэг> - Maximum Part.

    Флаг MP=Tтэг появился в mod_accel 1.0.6.

    Эти флаги ограничивают число соединений с бэкендом (MC) и число процессов ждущих в busy lock'ах (MW). Если флаг MW не указан, то он будет равен значению флага MC. Флаг MP определяет, какая часть URL во втором параметре директивы используется для ограничения. MP=L означает весь URL, указанный в директиве, MP=P - имя бэкенда и порт и MP=H - только имя бэкенда. Последний вариант используется по умолчанию. Кроме того, флаг MP=Tтэг позволяет гибко настраивать ограничение. В качестве тэга может быть выбрано любое слово. Рассмотрим действие флагов на примере:

    AccelPass  /t1/  http://backend:80/t1/    [MC=30,MW=40,MP=P]
    AccelPass  /t2/  http://backend:80/t2/    [MC=30,MW=40,MP=P]
    
    AccelPass  /t3/  http://backend:8080/t3/  [MC=20,MW=30,MP=H]
    AccelPass  /t4/  http://backend:8081/t4/  [MC=20,MW=30,MP=H]
    
    AccelPass  /t5/  http://backend:80/t5/    [MC=10,MW=10,MP=L]
    
    AccelPass  /t6/  http://backend:80/t6/    [MC=15,MW=15,MP=Tbk]
    AccelPass  /t7/  http://backend:80/t7/    [MC=15,MW=15,MP=Tbk]
    
    Первые две директивы ограничивают число соединений с портом 80 сервера backend до 30 и число процессов, ждущих в busy lock'ах до 40. Однако это не относится к URL, начинающимся на "http://backend:80/t5/" - для них число соединений и ждущих процессов ограничено 10. То же самое относится к URL, начинающимся на "http://backend:80/t6/" и "http://backend:80/t7/" - для них суммарное число соединений и ждущих процессов ограничено 15. Таким образом, максимальное суммарное число соединений с портом 80 сервера backend - 55 (30, 10 и 15), число ждущих процессов - 65 (40, 10 и 15). В то же время число соединений с портами 8080 и 8081 сервера backend в сумме не может превысить 20, а число ждущих процессов - 30. Всего же с сервером backend может быть установлено не более 75 (30, 10, 15 и 20) соединений, а число ждущих при этом процессов - не более 95 (40, 10, 15 и 30).

    В директивах, ограничивающих доступ к одному и тому же ресурсу, следует использовать одинаковые значения флагов MC и MW. То есть, директивы

    AccelPass  /t3/  http://backend:8080/t3/  [MC=10,MW=20,MP=H]
    AccelPass  /t4/  http://backend:8081/t4/  [MC=20,MW=30,MP=H]
    
    работать будут, но, возможно, не так, как Вы ожидаете.

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

  • NR - No Resolve.

    На старте mod_accel определяет один ip-адрес каждого бэкенда и в дальнейшем использует только его. Флаг NR запрещает определение ip-адресов на старте. Этот флаг нужно использовать, когда одному доменному имени соответствует несколько бэкендов и с помощью сервера DNS реализуется примитивное распределение нагрузки и отказоустойчивость.

  • PH - Preserve Host.

    Этот флаг появился в mod_accel 1.0.12.

    Флаг PH позволяет передать бэкенду имя хоста в заголовке "Host" неизменным. Если в запросе клиента этот заголовок отсутствует, то он также не передаётся бэкенду. Номер порта при необходимости изменяется. Если вместе с флагом PH заданы флаги MC или MW, но не задан флаг MP, то для ограничения числа соединений или ждущих процессов используется содержимое заголовка "Host". При формировании ключа в кэше используется заголовок "Host".

    Флаг MP вместе с PH работает корректно, начиная с версии 1.0.20.

    Если фронтенд и бэкенд используют разные порты, то mod_accel 1.0.12 не корректирует порт в заголовках "Location" и "Refresh". Начиная с версии 1.0.13, mod_accel корректно работает с этими заголовками.

Начиная с версии 1.0.14, mod_accel сначала проверяет директивы AccelPass из виртуального сервера, а лишь затем директивы из основного сервера. Такой порядок позволяет задать в основном сервере директиву с флагом PH, а затем переопределить её в некоторых виртуальных серверах.

Начиная с версии 1.0.16, mod_accel позволяет использовать в директиве AccelPass специальное имя хоста _the_same_host_, например:

AccelPass           /            http://_the_same_host_:8080/
В этом случае mod_accel в качестве IP-адреса бэкенда будет использовать тот же IP-адрес, что и у фронтенда. Необходимо заметить, что при использовании этого имени хоста автоматически устанавливается флаг PH. Если вместе с именем _the_same_host_ заданы флаги MC или MW, но не задан флаг MP, то для ограничения числа соединений или ждущих процессов используется ip-адрес хоста. При формировании ключа в кэше используется IP-адрес бэкенда.

Флаг MP вместе с _the_same_host_ работает корректно, начиная с версии 1.0.20.

Начиная с версии 1.0.27, бэкенд, доступный через _the_same_host_, может использовать named-based виртуальные хосты.


Директива AccelPassCookie

syntax: AccelPassCookie on|off
default: AccelPassCookie off
context: server config, virtual host, location, files

Директива определяет, можно ли передавать cookie клиента бэкенду, если его ответ на запрос в принципе может быть закэширован. Поскольку ответы на запросы с одинаковым URL могут отличаться в зависимости от содержимого cookie, то такие ответы кэшировать нельзя. Поэтому, если ответ в принципе может быть закэширован, то в запрос в бэкенду не включаются cookie, переданные клиентом. Однако возможна конфигурация, когда бэкенд сам указывает, какие ответы нужно кэшировать, а какие - нет. В этом случае можно разрешить передачу cookie бэкенду.

Директива передаёт заголовки "Cookie", приходящие от клиента, и заголовки "Set-Cookie", переданные от бэкенда, но никак не влияет на их кэширование.


Директива AccelPassServer

syntax: AccelPassServer on|off
default: AccelPassServer off
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.16 и выше

Директива определяет, нужно ли сохранять заголовок "Server", установленный бэкендом, или же этот заголовок должен быть установлен фронтендом.

Возможность устанавливать собственный заголовок "Server" появилась в Apache 1.3.24, и если с этой версией Apache используется mod_accel более ранний, чем 1.0.16, то клиенту будет передаваться заголовок "Server", установленный бэкендом. По умолчанию эта директива выключена и mod_accel-1.0.16, как и предыдущие версии, не передаёт заголовок бэкенда "Server".

При использовании с Apache 1.3.26 и старше и mod_accel версий 1.0.16-1.0.20 заголовок "Server" вообще не выдаётся, если директива выключена. Исправлено в mod_accel-1.0.21.


Директива AccelPassXAccel

syntax: AccelPassXAccel on|off
default: AccelPassXAccel off
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.22 и выше

Директива определяет, нужно ли передавать клиенту заголовок "X-Accel-Expires", установленный бэкендом. Эта директива полезна при использовании каскада из двух mod_accel, тогда на сервере, более близком к бэкенду, нужно разрешить передачу X-Accel-Expires для первого фронтенда.


Директива AccelRetry5XX

syntax: AccelRetry5XX on|off
default: AccelRetry5XX on
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.18 и выше

Директива определяет, нужно ли пытаться соединиться с другим бэкендом, если результат ответа равен 5XX и используется примитивное распределение нагрузки и отказоустойчивость.


Директива AccelRequestBuffSize

syntax: AccelRequestBuffSize
default: AccelRequestBuffSize 8
context: server config, virtual host, location, files

Директива определяет размер буфера в килобайтах для приёма тела запроса. Если размер последнего превышает размер буфера, то используется временный файл.


Директива AccelRevalidateUser

syntax: AccelRevalidateUser on|off
default: AccelRevalidateUser off
context: server config, virtual host, location, files

Директиву можно применять в случае, если выполняются все нижеперечисленные условия:

  • авторизация клиента выполняется бэкендом посредством заголовка "Authorization" или cookie;

  • ответ бэкенда для разных пользователей одинаковый и следовательно может отдаваться из кэша;

  • авторизация является дешёвой операцией в отличии от генерации тела ответа;

  • бэкенд умеет возвращать код ответа 304 (HTTP_NOT_MODIFIED) и это является дешёвой операцией по сравнению с генерацией полного ответа.

Если директива активна, то при наличии в кэше ответа на запрос, выполняется запрос к бэкенду с заголовком "If-Modified-Since".


Директива AccelReverse

syntax: AccelReverse префикс url
default: нет
context: server config, virtual host
compatibility: mod_accel 1.0.10 и выше

Директива определяет, какие URL в заголовках "Location" и "Refresh" должны корректироваться. Например, если задана директива

AccelReverse        /            http://backend/
и заголовок "Location" содержит "http://backend/test/two.html", то он будет изменён на "http://frontend/test/two.html".

Директива AccelReverse аналогична директиве ProxyPassReverse модуля mod_proxy, однако её не нужно использовать совместно с директивой AccelPass, поскольку AccelPass уже имеет функциональность AccelReverse. Область применения директивы AccelReverse — использование mod_accel совместно с модулем mod_rewrite.

Начиная с версии 1.0.26, AccelReverse может использоваться для коррекции заголовков независимо от того, как делается проксирование — с помощью AccelPass или же модулем mod_rewrite.


Директива AccelSendSize

syntax: AccelSendSize размер
default: AccelSendSize 8
context: server config, virtual host, location, files

Директива определяет размер буфера в килобайтах для передачи ответа бэкенда клиенту.


Директива AccelSetXHost

syntax: AccelSetXHost on|off
default: AccelSetXHost off
context: server config, virtual host, location, files

Директива определяет, добавлять или нет заголовок "X-Host" в запрос, передаваемом бэкенду. В этом заголовке передаётся содержимое заголовка "Host", переданное фронтенду клиентом.


Директива AccelSetXRealIP

syntax: AccelSetXRealIP on|off
default: AccelSetXRealIP off
context: server config, virtual host, location, files

Директива определяет, добавлять или нет заголовок "X-Real-IP" в запрос, передаваемом бэкенду. В этом заголовке передаётся IP-адрес, с которого был сделан запрос к фронтенду.


Директива AccelSetXURI

syntax: AccelSetXURI on|off
default: AccelSetXURI off
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.18 и выше

Директива определяет, добавлять или нет заголовок "X-URI" в запрос, передаваемом бэкенду. В этом заголовке передаётся URI, переданный фронтенду клиентом.

Директива работает, только начиная с версии 1.0.28.


Директива AccelSetXURL

syntax: AccelSetXURL on|off
default: AccelSetXURL off
context: server config, virtual host, location, files

Директива определяет, добавлять или нет заголовок "X-URL" в запрос, передаваемом бэкенду. В этом заголовке передаётся URL, переданный фронтенду клиентом.


Директива AccelTimeout

syntax: AccelTimeout "время" ["время"]
default: AccelTimeout Timeout Timeout
context: server config, virtual host, location, files

Директива задаёт таймауты при работе с бэкендом. Первый параметр определяет таймаут при передаче запроса бэкенду и чтении его ответа. Второй параметр определяет таймаут на соединение с бэкендом. Оба параметра можно задавать в виде строки, например, "1 minute 30 seconds" или в виде одного числа, означающего секунды. Если таймаут на соединение не указан, он равен значению первого параметра. Необходимо заметить, что у системного вызова connect() есть свой неизменяемый таймаут и, как правило, он равен 75 секундам, поэтому задавать таймаут на соединение больше 75 секунд не имеет смысла. По умолчанию значения обоих параметров равны значению директивы Timeout, задающей таймаут при работе с клиентом.

Задавать большой таймаут при работе с бэкендом имеет смысл в том случае, если до истечения таймаута ответ от бэкенда всё же может быть получен и этот ответ кэшируется. Тогда последующие запросы будут отдаваться из кэша до тех пор, пока ответ не устареет. В этом случае рекомендуется устанавливать директивой AccelBusyLock значения busy lock'ов большие, чем значение таймаута.

Задавать небольшие значения обоих таймаутов рекомендуется для реализации примитивного распределения нагрузки и отказоустойчивости.


Директива AccelUnlinkNoCached

syntax: AccelUnlinkNoCached on|off
default: AccelUnlinkNoCached off
context: server config, virtual host, location, files

Директива определяет, нужно ли удалять из кэша ответы, даже если они не могут быть закэшированы.


Директива AccelWaitBeforeBodyRead

syntax: AccelWaitBeforeBodyRead время в миллисекундах
default: AccelWaitBeforeBodyRead 0
context: server config, virtual host, location, files

Директива определяет время ожидания в миллисекундах между первым и вторым чтением ответа бэкенда.


Известные ошибки и особенности

  • Модуль mod_accel работает с бэкендами, поддерживающими протокол HTTP/1.0 и выше.

  • Модуль mod_accel делает запрос к бэкенду по протоколу HTTP/1.0.

  • Если на фронтенде добавляется несколько заголовков "Set-Cookie", то в ответ, отдаваемый модулем mod_accel, добавляется только один такой заголовок.

  • Модуль mod_accel не поддерживает метод TRACE.

  • Модуль, возможно, будет собираться на не-Unix платформах, но никаких телодвижений по портированию не делалось.

Менеджер кэша

При сборке mod_accel так же собирается менеджер кэша при условии, что mod_accel не был сконфигурирован с параметром --without-cachemgr. С помощью менеджера кэша можно обновлять или удалять отдельные URL в кэше. Для того, что бы менеджер кэша был доступен по запросу "/cachemgr", нужно задать такие директивы:

<Location       /cachemgr>
    SetHandler  "accel-cachemgr"
</Location>
После этого менеджер кэша может быть вызван с двумя параметрами — "/cachemgr?action=<action>&url=<url>", причём url должен быть обязательно последним. Параметр action может принимать два значения: refresh — для обновления содержимого кэша и remove — для удаления файла из кэша. Действие remove не работало в версиях mod_accel 1.0.4-1.0.24. Параметр url должен задаваться без префикса "http://", имени хоста и порта. При ответе cachemgr добавляет три заголовка:

  • X-Accel-Cachemgr-Action указывает, какое действие было предпринято менеджером. Может принимать следующие значения: create — файл кэша был создан, refresh — файл кэша был обновлён, remove — файл кэша был удалён.

  • X-Accel-Cachemgr-URL указывает URL, который был задан менеджеру.

  • X-Accel-Cachemgr-Status указывает результат выполнения операции. Может принимать следующие значения:

    • success — операция выполнена успешно;

    • invalid — ошибка в параметрах, переданных менеджеру кэша;

    • no_accelerated — запрос не обрабатывается модулем mod_accel;

    • not_found — файл в кэше не найден (при операции remove) или бэкенд вернул ответ 404 (HTTP_NOT_FOUND);

    • no_cachable — ответ бэкенда некэшируем;

    • cache_error — ошибка при работе с кэшом;

    • backend_error — ошибка при работе с бэкендом.

Модуль mod_randban

Модуль mod_randban подставляет случайное число в ответах модуля mod_accel.


Директива RandomBanner

syntax: RandomBanner on|off|строка длина
default: нет
context: server config, virtual host, location, files

Директива указывает искать заданную строку и заменять следующее за ней число случайным числом. Максимальное число заменяемых символов указывается во втором параметре директивы. При этом идущие подряд одинаковые числа будут меняться на одинаковые случайные числа. Например, директивы

RandomBanner  random=rb  10
RandomBanner  rand=      10
заменит текст

<a href="http://host/path0?place=1&random=rb00000">
<img src="http://host/path1?place=1&random=rb00000">
</a>

<a href="http://host/path0?place=1&key=1234">
<img src="http://host/path1?place=1&key=1234&rand=11111">
</a>

<a href="http://host/path0?place=1&random=rb00000">
<img src="http://host/path1?place=1&random=rb00000">
</a>

на такой:

<a href="http://host/path0?place=1&random=rb42943">
<img src="http://host/path1?place=1&random=rb42943">
</a>

<a href="http://host/path0?place=1&key=1234">
<img src="http://host/path1?place=1&key=1234&rand=31520">
</a>

<a href="http://host/path0?place=1&random=rb06172">
<img src="http://host/path1?place=1&random=rb06172">
</a>


Модуль mod_quoted

Модуль mod_quoted перекодирует русские символы в ответах модуля mod_accel, представленные в виде quoted printable, в конструкциях вида '<a href="/show?page=2&word=%80%81%82">'. Такие конструкции обычно используются при выдаче результатов поиска для ссылок на оставшиеся страницы с результатами поиска. Символы перекодируются только в ссылках вида '<a href= ... >'. Хотя закодированные символы в принципе могут встречаться в ссылках вида '<img src= ... >' или '<iframe src= ... >', на практике такого не случается. Перекодирование осуществляется с помощью модуля mod_charset и настраивается его директивами.


Директива RecodeQuoted

syntax: RecodeQuoted on|off
default: off
context: server config, virtual host, location, files

Директива разрешает или запрещает перекодирование quoted printable символов.


Модуль mod_freeze

Модуль mod_freeze замораживает слишком активные теги и параметры в ответах модуля mod_accel:

  • тэги '<script ... >', '<object ... >', '<iframe ... >', '<style ... >', '<layer ... >', '<ilayer ... >', '<applet ... >' и '<embed ... >' и соответствующие им закрывающие тэги;
  • параметры внутри любого тэга 'onBlur', 'onChange', 'onFocus', 'onSelect', 'onSubmit', 'onReset', 'onMouseOver', 'onMouseUp', 'onMouseOut', 'onMouseDown', 'onMouseMove', 'onClick', 'onDblClick', 'onKeyUp', 'onKeyDown', 'onKeyPress', 'onLoad', 'onUnload', 'onAbort' и 'onError';
  • схемы адресации в параметрах 'javascript:', 'livescript:', 'behavior:', 'vbscript:', 'about:' и 'mocha:'.
Замораживание заключается в изменении последнего символа на символ 'X'. Например, '<script ... >' будет заменён на '<scripX ... >', '<onMouseOver ... >' — на '<onMouseOveX ... >', а '<javascript: ... >' — на '<javascripX: ... >'. Замораживание начинается после строки, указанной в директиве FreezeStart.


Директива FreezeStart

syntax: FreezeStart строка
default: нет
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.22 и выше

Директива задаёт строку, после которой замораживаются теги и параметры. По умолчанию замораживание начинается с самого начала ответа.

До версии mod_accel 1.0.23 по умолчанию замораживание начиналось после тэга body. Кроме того, директиву можно было задать только глобально для всего сервера.


Директива FreezeTags

syntax: FreezeTags on|off
default: off
context: server config, virtual host, location, files
compatibility: mod_accel 1.0.22 и выше

Директива разрешает или запрещает изменение тегов.


Взаимодействие с модулем mod_rewrite

Хотя при установке mod_accel модуль mod_proxy продолжает работать, это не относится к флагу P директивы RewriteRule модуля mod_rewrite, если, конечно, модуль mod_accel не был сконфигурирован с параметром --without-mod_rewrite.

При использовании mod_accel можно использовать в директиве RewriteRule флаги MC, MW и MP, как в директиве AccelPass:

RewriteRule  ^/one.html$  http://backend/$1 [P,MC=10,MP=Tsome]
за одним исключением — в флаге MP нельзя использовать значение L.

Поддержка флага MP в директиве RewriteRule появилась в mod_accel, начиная с 1.0.6. В более ранних версиях ресурс можно ограничивать только именем бэкенда.

Первый параметр директивы RewriteRule можно использовать в SSI, например:

<!--#include virtual="/one.html?arg=one" -->

Полная поддержка директивы RewriteRule обеспечена в mod_accel, начиная с версии 1.0.5.

Начиная с mod_accel версии 1.0.10, с помощью директивы AccelReverse можно корректировать заголовки "Location" и "Refresh":

AccelReverse        /            http://backend/

Начиная с mod_accel версии 1.0.29, при проксировании с помощью модуля mod_rewrite можно переписывать заголовок "Location" и "Refresh" с помощью этого же модуля. Например, запрос "http://frontend/one/test" с помощью директивы

RewriteRule                ^/([^/]+)/(.*)$         http://$1.backend/$2  [P,L]
будет направлен на "http://one.backend/test". Если "test" окажется каталогом, то бэкенд вернёт редирект на "http://one.backend/test/". Это редирект можно переписать на фронтенде, указав имя бэкенда при тестировании переменной среды "ACCEL_REWRITE":
RewriteCond                %{ENV:ACCEL_REWRITE}    ^([^.]+)\.backend$
RewriteRule                ^(.*)$                  http://frontend/%1/$1 [P,L]

RewriteRule                ^/([^/]+)/(.*)$         http://$1.backend/$2  [P,L]
Правила, переписывающие заголовки "Location" и "Refresh", должны идти перед правилами, описывающими проксирование.

Начиная с версии 1.0.30, вышеописанная процедура с модулем mod_rewrite разрешается с помощью директивы AccelModRewriteLocation.

Настройка производительности

Использование директив AccelIgnoreNoCache, AccelIgnoreAuth, AccelBusyLock, AccelMaxStale и ограничение числа соединений позволяет уменьшить нагрузку на бэкенд, отдавая закэшированные ответы.

Увеличение размера буфера приёма данных от бэкенда директивой AccelBkRcvBuffSize позволяет избежать использования временного файла в качестве буфера. Для кэшируемых ответов это не так критично, поскольку этот же временный файл впоследствии будет перелинкован в файл кэша. Если размер ответа превышает размер буфера в памяти, то в ErrorLog пишется предупреждение: "accel: request ... is buffered to tempfile".

Установка директивы AccelUnlinkNoCached в состояние off позволяет не считать хэш-функцию md5 для запросов, которые точно не будут кэшироваться.

Поскольку системные вызовы являются достаточно дорогостоящими операциями, то минимизация их числа ведёт к увеличению производительности системы. Рассмотрим способы уменьшения числа системных вызовов при работе mod_accel.

Если фронтенд и бэкенд соединены ethernet'ом, то ответ зачастую читается блоками по 1460 байт или блоками размером кратным 1460. В этом случае ответ размером 20K может быть прочитан за 10-15 пар системных вызовов select() и read(). Если же с помощью директивы AccelWaitBeforeBodyRead подождать 200-300 миллисекунд прежде, чем читать тело ответа, то, вполне вероятно, что ядро за это время примет если не весь ответ, то хотя бы его большую часть, и ответ будет прочитан всего за одну или несколько пар системных вызовов select() и read(). Разумеется, ядро не сможет принять весь ответ за раз, если размер его буфера меньше размера ответа, поэтому, возможно, нужно будет увеличить размер TCP-буфера приёма в ядре директивой AccelBkRcvSockBuffSize. Кроме того, для того, что бы весь ответ был прочитан из TCP-буфера ядра за один раз необходимо, что бы размер буфера, задаваемый директивой AccelBkRcvBuffSize был не меньше размер буфера TCP-буфера ядра. Проанализировать действие вышеописанных директив можно с помощью заметок "accel_bnr", "accel_bfr", "accel_br", в которых записывается соответственно число чтений бэкенда, размер заголовка ответа и части ответа, считанной за первое чтение, и полный размер ответа.

Увеличение размера буфера отправки данных клиенту директивой AccelSendSize позволяет уменьшить число системных вызовов select() и write(). Необходимо заметить, что select() используется только в случае, когда параллельно с отправкой данных клиенту происходит чтение ответа от бэкенда. Если же ответ полностью считан, то используется только блокирующий write().

Увеличение размера буфера отправки данных клиенту директивой AccelCacheSendSize позволяет уменьшить число системных вызовов read() для чтения ответа из кэша и write() для отправки данных клиенту.

Параметр noauto в директиве AccelCacheRoot позволяет не делать системных вызовов mkdir() для создания промежуточных каталогов при каждом создании нового файла кэша.

Как mod_proxy взаимодействует с бэкендом

Хотя модуль mod_proxy часто используется как буфер между тяжёлым сервером (mod_perl) и медленным клиентом, он плохо справляется с этой задачей. Это связано с тем, что mod_proxy читает ответ от бэкенда блоками по 8K и такими же блоками отдаёт его клиенту. После получения всего ответа mod_proxy вызывает функцию ap_bflush(), которая записывает оставшиеся данные из внутреннего буфера Apache размером 4K в сокет клиента. И лишь после этого mod_proxy закрывает сокет бэкенда. Если в качестве бэкенда используется Apache, то после передачи всех данных фронтенду, он выполняет функцию lingering_close() — делает shutdown() на запись в сокет и, подождав в select()'е 2 секунды, закрывает сокет.

Таким образом, вся буферизация по сути выполняется не mod_proxy, а ядром и зависит только от размеров TCP-буферов приёма и отправки в ядре на машине с mod_proxy и от размера TCP-буфера отправки на бэкенде. Рассмотрим варианты ситуаций при работе с медленным клиентом (скорость около 3K/s), которые могут возникнуть на FreeBSD версии 3.0-4.3, где размер этих буферов по умолчанию равен 16K.

  • Если объём передаваемых данных не превышает размера TCP-буфера отправки на фронтенде, то бэкенд освобождается практически сразу же после передачи всех данных фронтенду. Из буфера отправки бэкенда все данные сразу же попадают в буфер приёма фронтенда. mod_proxy быстро считывает их по 8K и записывает в буфер отправки и после этого закрывает сокет к бэкенду.

  • Если объём передаваемых данных не превышает суммарного размера TCP-буферов отправки и приёма фронтенда, TCP-буфера отправки бэкенда и внутреннего буфера mod_proxy, равного 8K, (в нашем случае суммарный размер 56K), то бэкенд ждёт ещё две секунды после передачи всех данных фронтенду. Это происходит из-за того, что буфер отправки фронтенда заполнен полностью первыми 16K, следующие 8K считаны mod_proxy, ещё 16K находятся в буфере приёма фронтенда и последние 16K находятся в буфере отправки бэкенда. До тех пор, пока фронтенд не передаст клиенту первые 16K (а это займёт около 5 секунд), mod_proxy не сможет записать в буфер отправки оставшиеся данные и соответственно не сможет закрыть сокет к бэкенду.

  • Если объём передаваемых данных больше суммарного размера TCP-буферов отправки и приёма фронтенда, TCP-буфера отправки бэкенда и внутреннего буфера mod_proxy, то бэкенд занят в течение времени, необходимого для передачи медленному клиенту данных, превышающих суммарный размер буферов, плюс ещё две секунды после передачи всех данных фронтенду.

Надо сказать, что эти дополнительные 2 секунды в Apache нигде не фиксируются, то есть, их нельзя увидеть в логах во времени выполнения запроса - %T и в запросе server-status, так как время, затраченное на lingering_close(), в %T не учитывается и процесс помечается как свободный в scoreboard ещё до вызова lingering_close(). Единственный способ увидеть эти две секунды - это запустить команду netstat на бэкенде и посмотреть, сколько сокетов, соединяющих бэкенд и фронтенд, находятся в состоянии FIN_WAIT2. Сокет переходит в это состояние после вызова бэкендом shutdown() и получения подтверждения от ядра фронтенда. Однако, при анализе необходимо учитывать, что сокет может оставаться в этом состоянии и после того, как бэкенд выждал 2 секунды и вызвал close(), то есть, уже готов к обработке нового запроса.

Уменьшить время ожидания бэкенда при записи больших ответов можно с помощью директивы ProxyReceiveBufferSize задающей размер TCP-буфера приёма в ядре, и SendBufferSize задающей размер TCP-буфера отправки в ядре. Кроме того, можно увеличить размеры всех TCP-буферов на фронтенде и бэкенде средствами операционной системы. Однако увеличение буфера отправки на фронтенде лишь отдаляет проблему 2-х секундной задержки. Избежать 2-х секундной задержки можно, если собрать бэкенд с параметром -DNO_LINGCLOSE.

В Apache 1.3.24 частично решена проблема взаимодействия mod_proxy и бэкенда — появилась директива ProxyIOBufferSize, позволяющая задать размер буфера для получения ответа от бэкенда. Кроме того, если от бэкенда получен весь ответ, то соединение с ним закрывается, что позволяет избежать 2-х секундной задержки. Тем не менее, проблема решена не полностью — если размер ответа бэкенда будет больше суммы размера заданного буфера и ядерных TCP-буферов, то бэкенд будет занят на время, необходимое для передачи части ответа, превышающей суммарный размер буферов.

Кроме синхронного получения ответа ответа от бэкенда и отдачи его клиенту, mod_proxy так же синхронно принимает тело запроса клиента и передаёт его бэкенду.

При использовании mod_accel такой проблемы нет, поскольку чтение из сокета бэкенда и запись в сокет клиента выполняются асинхронно, все операции неблокирующие и готовность обоих сокетов проверяется с помощью select()'a. mod_accel считывает ответ от бэкенда в буфер, размер которого задаётся директивой AccelBkRcvBuffSize. Если размер ответа превышает размер буфера, то содержимое буфера записывается во временный файл. Если ответ кэшируемый, то этот временный файл позже перелинковывается в файл кэша. Кроме того, имеется директива AccelBkRcvSockBuffSize, которая так же, как и ProxyReceiveBufferSize позволяет задавать размер TCP-буфера приёма в ядре с тем отличием, что размер задаётся в килобайтах, а не в байтах. Однако в mod_accel она предназначена для уменьшения количества системных вызовов select() и read(), необходимых для чтения ответа бэкенда.

Что касается тела запроса, то оно полностью принимается от клиента и только после этого передаётся бэкенду. Размер буфера для приёма задаётся директивой AccelRequestBuffSize. Если тело запроса больше размера буфера, то оно записывается во временный файл.

(C) Игорь Сысоев
http://sysoev.ru