Apache. Документация на русском


Разделы:   1    2    3    4    5    6    7    8      9      10    11    12    13    14    15    16  

Раздел 9. Разная документация Apache

Пункты:     81      82    83    84  

 <         > 
  RU            EN  

Пункт 81. Примечания к производительности — настройка Apache

Apache 2.x — это веб-сервер общего назначения, обеспечивающий баланс гибкости, переносимости и производительности. Хотя Apache 2.x не разрабатывался специально для установления рекордов производительности, он способен обеспечить высокую производительность во многих реальных ситуациях.

По сравнению с Apache 1.3, версия 2.x содержит множество дополнительных оптимизаций для увеличения пропускной способности и масштабируемости. Большинство этих улучшений включены по умолчанию. Однако существуют варианты конфигурации времени компиляции и времени выполнения, которые могут существенно повлиять на производительность. В этом документе описываются параметры, которые администратор сервера может настроить для настройки производительности установки Apache 2.x. Некоторые из этих параметров конфигурации позволяют httpd лучше использовать возможности оборудования и ОС, в то время как другие позволяют администратору обменивать функциональность на скорость.

Проблемы с оборудованием и операционной системой

Единственная самая большая аппаратная проблема, влияющая на производительность веб-сервера, — это оперативная память. Веб-сервер никогда не должен переключаться, поскольку обмен увеличивает задержку каждого запроса сверх точки, которую пользователи считают «достаточно быстрой». Это заставляет пользователей нажимать «Стоп» и «перезагружать», что еще больше увеличивает нагрузку. Вы можете и должны контролировать этот MaxRequestWorkers параметр, чтобы ваш сервер не порождал так много дочерних элементов, что он начал бы подкачиваться. Процедура для этого проста: определите размер вашего среднего процесса Apache, просмотрев список процессов с помощью такого инструмента, как , top и разделите его на общую доступную память, оставив место для других процессов.

Помимо этого, остальное обыденно: получите достаточно быстрый процессор, достаточно быструю сетевую карту и достаточно быстрые диски, где «достаточно быстрый» — это то, что нужно определить экспериментальным путем.

Выбор операционной системы во многом зависит от местных интересов. Но некоторые рекомендации, которые оказались в целом полезными, следующие:

  • Запустите последнюю стабильную версию и уровень исправления выбранной операционной системы. В последние годы многие поставщики ОС значительно улучшили производительность своих стеков TCP и библиотек потоков.

  • Если ваша ОС поддерживает sendfile(2) системный вызов, убедитесь, что вы установили выпуск и/или исправления, необходимые для его включения. (Например, для Linux это означает использование Linux 2.4 или более поздней версии. Для ранних выпусков Solaris 8 может потребоваться применение исправления.) В системах, где он доступен, позволяет Apache 2 доставлять статический контент быстрее sendfile и с меньшей загрузкой ЦП. использование.

Проблемы с конфигурацией во время выполнения

HostnameLookups и другие аспекты DNS

До Apache 1.3 HostnameLookups по умолчанию использовался On . Это увеличивает задержку для каждого запроса, поскольку для завершения запроса требуется выполнить поиск DNS. В Apache 1.3 этот параметр по умолчанию равен Off . Если вам нужно, чтобы адреса в файлах журналов преобразовывались в имена хостов, используйте программу logresolve , поставляемую с Apache, или один из многочисленных доступных пакетов отчетов по журналам.

Рекомендуется выполнять подобную постобработку файлов журнала на каком-либо компьютере, отличном от компьютера с рабочим веб-сервером, чтобы это действие не оказало неблагоприятного влияния на производительность сервера.

Если вы используете какие- либо директивы или (т. е. используете имя хоста или доменное имя, а не IP-адрес), то вы заплатите за два поиска DNS (обратный, за которым следует прямой поиск, чтобы убедиться, что обратный поиск не выполняется). подделка). Поэтому для наилучшей производительности при использовании этих директив используйте IP-адреса, а не имена, если это возможно. Allow from domain Deny from domain

Обратите внимание, что можно ограничить директивы, например, внутри <Location "/server-status"> раздела. В этом случае поиск DNS выполняется только для запросов, соответствующих критериям. Вот пример, который отключает поиск, кроме файлов .html и .cgi :

 Имя хостаПоиск отключен
<Файлы ~ "\.(html|cgi)$">
 Имя хоста
</файлы> 

Но даже несмотря на это, если вам просто нужны DNS-имена в некоторых CGI, вы можете рассмотреть возможность выполнения вызова gethostbyname в конкретных CGI, которым это нужно.

FollowSymLinks и SymLinksIfOwnerMatch

Везде, где в вашем URL-пространстве у вас нет Options FollowSymLinks , или у вас есть Options SymLinksIfOwnerMatch , Apache должен будет выполнить дополнительные системные вызовы для проверки символических ссылок. (Один дополнительный вызов для каждого компонента имени файла.) Например, если у вас было:

 DocumentRoot "/www/htdocs"
<Каталог "/">
 Параметры SymLinksIfOwnerMatch
</Каталог> 

и делается запрос на URI /index.html , тогда Apache будет выполнять lstat(2) , /www , /www/htdocs и /www/htdocs/index.html . Их результаты lstats никогда не кэшируются, поэтому они будут возникать при каждом отдельном запросе. Если вы действительно хотите проверить безопасность символических ссылок, вы можете сделать что-то вроде этого:

 DocumentRoot "/www/htdocs"
<Каталог "/">
 Параметры
</Каталог>
<Каталог "/www/htdocs">
 Опции -FollowSymLinks +SymLinksIfOwnerMatch
</Каталог> 

Это, по крайней мере, позволяет избежать дополнительных проверок пути DocumentRoot . Обратите внимание, что вам нужно будет добавить похожие разделы, если у вас есть какие-либо пути Alias или RewriteRule пути за пределами корня вашего документа. Для максимальной производительности и без защиты символических ссылок установите FollowSymLinks везде и никогда не устанавливайте SymLinksIfOwnerMatch .

Аллововеррайд

Везде, где в вашем URL-пространстве вы разрешаете переопределения (обычно .htaccess файлы), Apache попытается открыть .htaccess для каждого компонента имени файла. Например,

 DocumentRoot "/www/htdocs"
<Каталог "/">
 РазрешитьПереопределить все
</Каталог> 

и делается запрос на URI /index.html . Затем Apache попытается открыть файлы /.htaccess , /www/.htaccess и /www/htdocs/.htaccess . Решения аналогичны предыдущему случаю Options FollowSymLinks . Для максимальной производительности используйте AllowOverride None везде в вашей файловой системе.

Переговоры

По возможности избегайте согласования контента, если вы действительно заинтересованы в максимальной производительности. На практике преимущества переговоров перевешивают потери производительности. Есть один случай, когда вы можете ускорить сервер. Вместо использования подстановочного знака, такого как:

 Индекс DirectoryIndex 

Используйте полный список опций:

 DirectoryIndex index.cgi index.pl index.shtml index.html 

где вы перечисляете наиболее распространенный выбор первым.

Также обратите внимание, что явное создание type-map файла обеспечивает более высокую производительность, чем использование MultiViews , поскольку необходимую информацию можно определить, прочитав этот единственный файл, вместо того, чтобы сканировать каталог на наличие файлов.

Если вашему сайту требуется согласование содержимого, рассмотрите возможность использования type-map файлов, а не Options MultiViews директивы для выполнения согласования. См. документацию Content Negotiation для полного обсуждения методов согласования и инструкций по созданию type-map файлов.

Отображение памяти

В ситуациях, когда Apache 2.x необходимо просмотреть содержимое доставляемого файла, например, при выполнении обработки включения на стороне сервера, он обычно сопоставляет файл с памятью, если ОС поддерживает какую-либо форму mmap(2) .

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

  • В некоторых операционных системах mmap не масштабируется, а также read(2) при увеличении количества процессоров. Например, на многопроцессорных серверах Solaris Apache 2.x иногда быстрее доставляет проанализированные сервером файлы, если mmap он отключен.

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

Для установок, к которым применим любой из этих факторов, следует EnableMMAP off отключить сопоставление памяти доставленных файлов. (Примечание: эта директива может быть переопределена для каждого каталога.)

Отправить файл

В ситуациях, когда Apache 2.x может игнорировать содержимое доставляемого файла, например, при обслуживании статического содержимого файла, он обычно использует поддержку файла отправки ядра для файла, если ОС поддерживает эту операцию sendfile(2) .

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

  • Некоторые платформы могут иметь неработающую поддержку sendfile, которую система сборки не обнаружила, особенно если бинарные файлы были собраны на другой машине и перенесены на такую машину с нарушенной поддержкой sendfile.

  • В файловой системе, смонтированной через NFS, ядро может быть не в состоянии надежно обслуживать сетевой файл через собственный кэш.

Для установок, где применим любой из этих факторов, вы должны EnableSendfile off отключить доставку содержимого файла через sendfile. (Примечание: эта директива может быть переопределена для каждого каталога.)

Создание процесса

До Apache 1.3 все настройки MinSpareServers , MaxSpareServers и StartServers сильно влияли на результаты тестов. В частности, Apache требовался период «разгона», чтобы достичь количества дочерних элементов, достаточного для обслуживания применяемой нагрузки. После первоначального создания StartServers дочерних элементов будет создаваться только один дочерний элемент в секунду, чтобы удовлетворить MinSpareServers настройку. Таким образом, к серверу, к которому одновременно обращаются 100 клиентов, при использовании значения по умолчанию StartServers потребуется 5 порядка 95 секунд, чтобы создать достаточно дочерних элементов для обработки нагрузки. На практике это прекрасно работает на реальных серверах, потому что они не часто перезапускаются. Но он очень плохо справляется с тестами, которые могут работать всего десять минут.

Правило «одна в секунду» было реализовано, чтобы не загружать машину запуском новых дочерних элементов. Если машина занята созданием потомков, она не может обслуживать запросы. Но он настолько сильно повлиял на воспринимаемую производительность Apache, что его пришлось заменить. Начиная с Apache 1.3, код ослабляет правило «один в секунду». Он создаст одного, подождет секунду, затем породит двух, подождет секунду, затем породит четырех, и так будет продолжаться экспоненциально, пока не будет создаваться 32 ребенка в секунду. Он остановится всякий раз, когда он удовлетворяет MinSpareServers настройке.

Это кажется достаточно отзывчивым , поэтому почти не нужно крутить ручки MinSpareServers и . Когда в секунду создается более 4 дочерних элементов, в . Если вы видите много таких ошибок, рассмотрите возможность настройки этих параметров. Используйте вывод в качестве руководства. MaxSpareServers StartServers ErrorLog mod_status

С созданием процесса связана смерть процесса, вызванная настройкой MaxConnectionsPerChild . По умолчанию это 0 , что означает, что количество подключений, обрабатываемых одним дочерним элементом, не ограничено. Если в вашей конфигурации в настоящее время установлено какое-то очень низкое значение, например 30 , вы можете значительно увеличить это значение. Если вы используете SunOS или старую версию Solaris, ограничьте это до 10000 или около того из-за утечек памяти.

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

Проблемы конфигурации во время компиляции

Выбор MPM

Apache 2.x поддерживает подключаемые модели параллелизма, называемые многопроцессорными модулями (MPM). При сборке Apache вы должны выбрать MPM для использования. Для некоторых платформ существуют MPM для конкретных платформ: mpm_netware , mpmt_os2 , и mpm_winnt . Для общих систем типа Unix существует несколько MPM на выбор. Выбор MPM может повлиять на скорость и масштабируемость httpd:

  • MPM worker использует несколько дочерних процессов с множеством потоков каждый. Каждый поток обрабатывает одно соединение за раз. Worker обычно является хорошим выбором для серверов с высоким трафиком, поскольку он требует меньше памяти, чем MPM prefork.
  • MPM event является многопоточным, как Worker MPM, но позволяет одновременно обслуживать больше запросов, передавая часть обработки вспомогательным потокам, освобождая основные потоки для работы с новыми запросами.
  • MPM prefork использует несколько дочерних процессов с одним потоком в каждом. Каждый процесс одновременно обрабатывает одно соединение. Во многих системах prefork сравним по скорости с worker, но использует больше памяти. Беспоточный дизайн Prefork в некоторых ситуациях имеет преимущества перед рабочими: его можно использовать со сторонними модулями, не поддерживающими потокобезопасность, и его легче отлаживать на платформах с плохой поддержкой отладки потоков.

Дополнительные сведения об этих и других MPM см. в документации по MPM.

Модули

Поскольку использование памяти очень важно для производительности, вы должны попытаться исключить модули, которые фактически не используете. Если вы построили модули как DSO, удаление модулей — это просто вопрос комментирования соответствующей LoadModule директивы для этого модуля. Это позволяет вам поэкспериментировать с удалением модулей и посмотреть, работает ли ваш сайт в их отсутствие.

Если, с другой стороны, у вас есть модули, статически связанные с бинарным файлом Apache, вам нужно будет перекомпилировать Apache, чтобы удалить ненужные модули.

Попутный вопрос, который здесь возникает, конечно же, какие модули вам нужны, а какие нет. Ответ здесь, конечно, будет варьироваться от одного веб-сайта к другому. Однако минимальный список модулей, с которыми вы можете обойтись, обычно включает mod_mime , mod_dir и mod_log_config . mod_log_config это, конечно, необязательно, так как вы можете запустить веб-сайт без файлов журнала. Однако это не рекомендуется.

Атомные операции

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

По умолчанию APR реализует эти операции, используя наиболее эффективный механизм, доступный на каждой целевой платформе ОС/ЦП. Многие современные ЦП, например, имеют инструкцию, которая аппаратно выполняет операцию атомарного сравнения и замены (CAS). Однако на некоторых платформах APR по умолчанию использует более медленную реализацию атомарного API на основе мьютексов, чтобы обеспечить совместимость со старыми моделями ЦП, в которых такие инструкции отсутствуют. Если вы создаете Apache для одной из этих платформ и планируете работать только на более новых процессорах, вы можете выбрать более быструю атомарную реализацию во время сборки, настроив Apache с помощью параметра --enable-nonportable-atomics :

./buildconf
./configure --with-mpm=worker --enable-nonportable-atomics=yes

Опция --enable-nonportable-atomics актуальна для следующих платформ:

  • Solaris в SPARC
    По умолчанию APR использует атомарные объекты на основе мьютексов в Solaris/SPARC. Однако при настройке с помощью --enable-nonportable-atomics APR генерирует код, использующий код операции SPARC v8plus для быстрого сравнения и замены оборудования. Если вы настроите Apache с этой опцией, атомарные операции будут более эффективными (что позволит снизить нагрузку на ЦП и повысить уровень параллелизма), но результирующий исполняемый файл будет работать только на микросхемах UltraSPARC.
  • Linux на платформе x86
    По умолчанию APR использует атомарные методы на основе мьютекса в Linux. Однако при настройке с помощью --enable-nonportable-atomics APR генерирует код, использующий код операции 486 для быстрого сравнения и замены оборудования. Это приведет к более эффективным атомарным операциям, но полученный исполняемый файл будет работать только на чипах 486 и более поздних версий (а не на 386).

mod_status и ExtendedStatus включены

Если вы включите mod_status , а также установите ExtendedStatus On при сборке и запуске Apache, то при каждом запросе Apache будет выполнять два вызова gettimeofday(2) (или times(2) в зависимости от вашей операционной системы) и (до версии 1.3) несколько дополнительных вызовов time(2) . Все это делается для того, чтобы отчет о состоянии содержал временные указания. Для максимальной производительности установите ExtendedStatus off (значение по умолчанию).

принять сериализацию — несколько сокетов

Предупреждение:

Этот раздел не был полностью обновлен, чтобы учесть изменения, внесенные в версию 2.x HTTP-сервера Apache. Некоторая информация может быть актуальной, но, пожалуйста, используйте ее с осторожностью.

Здесь обсуждается недостаток в API сокетов Unix. Предположим, ваш веб-сервер использует несколько Listen операторов для прослушивания либо нескольких портов, либо нескольких адресов. Чтобы проверить каждый сокет, чтобы увидеть, готово ли соединение, Apache использует select(2) . select(2) указывает, что сокет имеет ноль или по крайней мере одно ожидающее соединение. Модель Apache включает в себя несколько дочерних узлов, и все незанятые из них проверяют наличие новых подключений одновременно. Наивная реализация выглядит примерно так (эти примеры не соответствуют коду, они придуманы в педагогических целях):

 для (;;) {
 для (;;) {
 fd_set accept_fds;
 FD_ZERO (&accept_fds);
 for (i = first_socket; i <= last_socket; ++i) {
 FD_SET(i, &accept_fds);
 }
 rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
 если (rc < 1) продолжить;
 новое_соединение = -1;
 for (i = first_socket; i <= last_socket; ++i) {
 если (FD_ISSET (i, &accept_fds)) {
 новое_соединение = принять (i, NULL, NULL);
 если (новое_соединение != -1) разрыв;
 }
 }
 если (новое_соединение != -1) разрыв;
 }
 process_the (новое_соединение);
 } 

Но у этой наивной реализации есть серьезная проблема с голоданием. Напомним, что несколько дочерних элементов выполняют этот цикл одновременно, поэтому несколько дочерних элементов будут блокироваться, select когда они находятся между запросами. Все эти заблокированные дочерние элементы пробудятся и вернутся, select когда в любом сокете появится один запрос. (Количество детей, которые пробуждаются, зависит от операционной системы и проблем со временем.) Затем все они попадут в петлю и попытаются установить accept соединение. Но только один будет успешным (при условии, что готово только одно соединение). Остальные будут заблокированы . accept Это эффективно блокирует эти дочерние элементы для обслуживания запросов из этого одного сокета, а не из других сокетов, и они будут застревать там до тех пор, пока в этом сокете не появится достаточно новых запросов, чтобы разбудить их всех. Эта проблема голодания была впервые задокументирована в PR # 467. Есть как минимум два решения.

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

Другое решение, используемое Apache, заключается в сериализации входа во внутренний цикл. Цикл выглядит так (различия выделены):

 for (;;) {
  accept_mutex_on ();
 для (;;) {
 fd_set accept_fds;
 
 FD_ZERO (&accept_fds);
 for (i = first_socket; i <= last_socket; ++i) {
 FD_SET(i, &accept_fds);
 }
 rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
 если (rc < 1) продолжить;
 новое_соединение = -1;
 for (i = first_socket; i <= last_socket; ++i) {
 если (FD_ISSET (i, &accept_fds)) {
 новое_соединение = принять (i, NULL, NULL);
 если (новое_соединение != -1) разрыв;
 }
 }
 если (новое_соединение != -1) разрыв;
 }
 accept_mutex_off ();
 обработать new_connection;
 } 

Функции accept_mutex_on и accept_mutex_off реализуют семафор взаимного исключения. Только один ребенок может иметь мьютекс в любой момент времени. Существует несколько вариантов реализации этих мьютексов. Выбор определяется в src/conf.h (до 1.3) или src/include/ap_config.h (1.3 или новее). В некоторых архитектурах нет выбора блокировки, в этих архитектурах небезопасно использовать несколько Listen директив.

Директиву Mutex можно использовать для изменения реализации мьютекса mpm-accept во время выполнения. В этой директиве задокументированы особые соображения для различных реализаций мьютексов.

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

В идеале вы должны запускать серверы без нескольких Listen операторов, если хотите добиться максимальной производительности. Но читайте дальше.

принять сериализацию — один сокет

Вышеизложенное прекрасно и модно для серверов с несколькими сокетами, но как насчет серверов с одним сокетом? Теоретически они не должны сталкиваться ни с одной из этих проблем, потому что все дочерние элементы могут просто блокироваться accept(2) до тех пор, пока не установится соединение, и никакого голодания не произойдет. На практике это скрывает почти то же самое «вращающееся» поведение, которое обсуждалось выше в неблокирующем решении. При реализации большинства стеков TCP ядро фактически пробуждает все заблокированные процессы accept при поступлении одного соединения. Один из этих процессов получает соединение и возвращается в пользовательское пространство. Остальные вращаются в ядре и снова засыпают, когда обнаруживают, что для них нет связи. Это вращение скрыто от пользовательского кода, но тем не менее оно есть. Это может привести к тому же расточительному поведению, что и неблокирующее решение для случая с несколькими сокетами.

По этой причине мы обнаружили, что многие архитектуры ведут себя более «хорошо», если мы сериализуем даже случай с одним сокетом. Так что это на самом деле значение по умолчанию почти во всех случаях. Грубые эксперименты под Linux (2.0.30 на двухъядерном процессоре Pentium pro 166 с 128 Мб ОЗУ) показали, что сериализация случая с одним сокетом приводит к уменьшению количества запросов в секунду менее чем на 3% по сравнению с несериализованным одиночным сокетом. Но несериализованный односокетный сервер показал дополнительную задержку в 100 мс на каждый запрос. Эта задержка, вероятно, является недостатком на дальних линиях и проблемой только в локальных сетях. Если вы хотите переопределить сериализацию с одним сокетом, вы можете определить SINGLE_LISTEN_UNSERIALIZED_ACCEPT , и тогда серверы с одним сокетом вообще не будут сериализоваться.

Затяжное закрытие

Как обсуждалось в разделе 8 draft-ietf-http-connection-00.txt, чтобы HTTP-сервер надежно реализовывал протокол, он должен отключать каждое направление связи независимо друг от друга. (Напомним, что соединение TCP является двунаправленным. Каждая половина независима от другой.)

Когда эта функция была добавлена в Apache, она вызвала шквал проблем в различных версиях Unix из-за недальновидности. Спецификация TCP не указывает, что FIN_WAIT_2 состояние имеет тайм-аут, но и не запрещает это. В системах без тайм-аута Apache 1.2 приводит к тому, что многие сокеты навсегда застревают в FIN_WAIT_2 состоянии. Во многих случаях этого можно избежать, просто установив последние исправления TCP/IP, предоставленные поставщиком. В тех случаях, когда поставщик никогда не выпускал патчи ( например , SunOS4 — хотя люди с исходной лицензией могут сами патчить), мы решили отключить эту функцию.

Есть два способа сделать это. Одним из них является вариант сокета SO_LINGER . Но по воле судьбы это никогда не было реализовано должным образом в большинстве стеков TCP/IP. Даже на тех стеках с правильной реализацией ( например , Linux 2.0.31) этот метод оказывается более дорогим (время процессора), чем следующее решение.

По большей части Apache реализует это в функции с именем lingering_close http_main.c ). Функция выглядит примерно так:

 void lingering_close (целое число с)
 {
 char мусорный_буфер[2048];
 
 /* отключаем передающую сторону */
 выключение (с, 1);
 сигнал (SIGALRM, lingering_death);
 сигнализация (30);
 для (;;) {
 выберите (s для чтения, 2-секундный тайм-аут);
 если (ошибка) перерыв;
 если (s готов к чтению) {
 если (читать(s,junk_buffer,sizeof(junk_buffer)) <= 0) {
 перерыв;
 }
 /* просто выбросьте все, что здесь есть */
 }
 }
 
 закрыть (с);
 } 

Это, естественно, увеличивает некоторые расходы в конце соединения, но это необходимо для надежной реализации. По мере того, как HTTP/1.1 становится все более распространенным, а все соединения являются постоянными, эти расходы будут амортизироваться при увеличении количества запросов. Если вы хотите поиграть с огнем и отключить эту функцию, вы можете определить NO_LINGCLOSE , но это совсем не рекомендуется. В частности, с появлением конвейерных постоянных соединений HTTP/1.1 lingering_close это абсолютно необходимо (а конвейерные соединения быстрее, поэтому вы хотите их поддерживать).

Файл табло

Родительский и дочерний элементы Apache общаются друг с другом через так называемую таблицу результатов. В идеале это должно быть реализовано в разделяемой памяти. Для тех операционных систем, к которым у нас либо есть доступ, либо для которых нам предоставлены подробные порты, это обычно реализуется с использованием общей памяти. Остальные по умолчанию используют файл на диске. Файл на диске не только медленный, но и ненадежный (и менее функциональный). Просмотрите src/main/conf.h файл для вашей архитектуры и найдите либо , USE_MMAP_SCOREBOARD либо USE_SHMGET_SCOREBOARD . Определение одного из этих двух (а также их компаньонов HAVE_MMAP и HAVE_SHMGET соответственно) включает предоставленный код общей памяти. Если в вашей системе есть другой тип разделяемой памяти, отредактируйте файл src/main/http_main.c и добавьте хуки, необходимые для его использования в Apache. (Отправьте нам патч, пожалуйста.)

Историческая справка: порт Apache для Linux не начал использовать общую память до версии 1.2 Apache. Эта оплошность привела к очень плохому и ненадежному поведению более ранних версий Apache в Linux.

DYNAMIC_MODULE_LIMIT

Если вы не собираетесь использовать динамически загружаемые модули (вероятно, вы этого не сделаете, если читаете это и настраиваете свой сервер для максимальной производительности), вам следует добавить его -DDYNAMIC_MODULE_LIMIT=0 при сборке сервера. Это сэкономит оперативную память, которая выделена только для поддержки динамически загружаемых модулей.

Приложение: Подробный анализ трассы

Вот трассировка системного вызова Apache 2.0.38 с рабочим MPM в Solaris 8. Эта трассировка была собрана с использованием:

truss -l -p httpd_child_pid.

Опция -l указывает truss регистрировать идентификатор LWP (облегченный процесс — форма потока на уровне ядра в Solaris), который вызывает каждый системный вызов.

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

В этой трассировке клиент запросил статический файл размером 10 КБ из httpd. Следы нестатических запросов или запросов с согласованием содержимого выглядят совершенно иначе (и в некоторых случаях довольно уродливо).

 /67: принять(3, 0x00200BEC, 0x00200C0C, 1) (спящий...)
/67: принять(3, 0x00200BEC, 0x00200C0C, 1) = 9 

В этой трассировке поток прослушивателя выполняется в LWP #67.

Обратите внимание на отсутствие accept(2) сериализации. На этой конкретной платформе рабочий MPM по умолчанию использует несериализованный прием, если только он не прослушивает несколько портов.
 /65: lwp_park(0x00000000, 0) = 0
/67: lwp_unpark(65, 1) = 0 

После принятия соединения поток прослушивателя активирует рабочий поток для обработки запроса. В этой трассировке рабочий поток, обрабатывающий запрос, сопоставляется с LWP #65.

 /65: получает имясокета(9, 0x00200BA4, 0x00200BC4, 1) = 0 

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

 /65: брк(0x002170E8) = 0
/65: брк(0x002190E8) = 0 

Вызовы brk(2) выделяют память из кучи. Их редко можно увидеть в трассировке системных вызовов, потому что httpd использует специальные распределители памяти ( apr_pool и apr_bucket_alloc ) для обработки большинства запросов. В этой трассировке httpd только что был запущен, поэтому он должен вызвать, malloc(3) чтобы получить блоки необработанной памяти, с помощью которых можно создать настраиваемые распределители памяти.

 /65: fcntl(9, F_GETFL, 0x00000000) = 2
/65: fstat64(9, 0xFAF7B818) = 0
/65:getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B910, 2190656) = 0
/65: fstat64(9, 0xFAF7B818) = 0
/65:getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B914, 2190656) = 0
/65: setsockopt(9, 65535, 8192, 0xFAF7B918, 4, 2190656) = 0
/65: fcntl(9, F_SETFL, 0x00000082) = 0 

Затем рабочий поток переводит соединение с клиентом (файловый дескриптор 9) в неблокирующий режим. Вызовы setsockopt(2) and getsockopt(2) являются побочным эффектом того, как libc Solaris обрабатывает fcntl(2) сокеты.

 /65: читать(9, "GET/10k.htm".., 8000) = 97 

Рабочий поток читает запрос от клиента.

 /65: стат("/var/httpd/apache/httpd-8999/htdocs/10k.html", 0xFAF7B978) = 0
/65: открыть("/var/httpd/apache/httpd-8999/htdocs/10k.html", O_RDONLY) = 10 

Этот httpd был настроен с Options FollowSymLinks и AllowOverride None . Таким образом, ему не нужно ни обращаться к lstat(2) каждому каталогу на пути, ведущем к запрошенному файлу, ни проверять наличие .htaccess файлов. Он просто вызывает stat(2) , чтобы убедиться, что файл: 1) существует и 2) является обычным файлом, а не каталогом.

 /65: sendfilev(0, 9, 0x00200F90, 2, 0xFAF7B53C) = 10269 

В этом примере httpd может отправить заголовок ответа HTTP и запрошенный файл с помощью одного sendfilev(2) системного вызова. Семантика файла отправки зависит от операционной системы. В некоторых других системах необходимо выполнить вызов write(2) или writev(2) для отправки заголовков перед вызовом sendfile(2) .

 /65: запись(4, " 1 2 7 . 0 . 0 . 1 - ".., 78) = 78 

Этот write(2) вызов записывает запрос в журнал доступа. Обратите внимание, что в этой трассировке отсутствует одна вещь — вызов time(2) . В отличие от Apache 1.3, Apache 2.x использует gettimeofday(3) для поиска время. В некоторых операционных системах, таких как Linux или Solaris, gettimeofday есть оптимизированная реализация, которая не требует столько накладных расходов, как обычный системный вызов.

 /65: выключение(9, 1, 1) = 0
/65: опрос (0xFAF7B980, 1, 2000) = 1
/65: чтение(9, 0xFAF7BC20, 512) = 0
/65: закрыть(9) = 0 

Рабочий поток выполняет затяжное закрытие соединения.

 /65: закрыть(10) = 0
/65: lwp_park(0x00000000, 0) (спящий...) 

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

 /67: принять(3, 0x001FEB74, 0x001FEB94, 1) (спящий...) 

Между тем поток слушателя может принять другое соединение, как только он отправит это соединение рабочему потоку (с учетом некоторой логики управления потоком в рабочем MPM, которая блокирует прослушиватель, если все доступные рабочие потоки заняты). Хотя это не очевидно из этой трассировки, следующая accept(2) может (и обычно происходит в условиях высокой нагрузки) происходить параллельно с обработкой рабочим потоком только что принятого соединения.



 <         > 

Пункты:     81      82    83    84  

Рейтинг@Mail.ru