На днях меня заинтересовала технология FastCGI. Это связано с тем, что связка nginx+apache под нагрузкой частенько проседает. Например, на слабом сервере мне удалось добиться ситуации, когда в синтетическом тесте утилитой ab из 1000 запросов было обслужено всего 8, остальные получили чистую страницу в ответ (причина - таймаут соединения между апачем и nginx; кстати, таймаут соединения составляет 90 секунд).

Меня заинтересовало почему же такая аказия происходит? ответ довольно простой: с момента приёма соединения до ответа на него у основного экземпляра апача есть 90 секунд, за это время он должен форкнуть себя, форк должен обработать (в моём случае это mod_php) скрипт, вернуть ответ родительскому процессу, который прозрачно передаст полученные данные фронтэнду и даст команду на уничтожение форка (nginx работает в режиме прокси только по протоколу http/1.0 (rfc1945) почему так, а не http/1.1, c keepalive - это вопрос к Игорю Сысоеву, автору nginx). Увеличивать таймаут смысла глубокого нету, так как при порождении большого количества апачей время выполнения запроса зашкаливает за 1500 секунд, доходя даже до умопомрачительных 2900, что уже довольно сложно назвать интерактивным взаимодействием с пользователем, а уменьшение максимального количества запущенных апачей радует пользователя ответом nginx с ошибкой 502.

Апач по умолчанию может принять (поставить в очередь на обработку, т.н. backlog) 1024 соединения, при этом сам апач может трудиться максимум над 256 запросами в случае no keepalive или над запросами от 256 клиентов, в случае keepalive (таковая фича есть только у http/1.1 и будущих версий протокола http, которых пока что нет :) ). То есть в нагрузке обрабатывать относительно большое количество запросов возможно только если в распоряжении имеется несколько ядер, больше двух, кроме того в случае с апачем (и в случае традиционной модели prefork, которую я использовал) есть узкое место: апач одновременно не порождает большого количества форков и вообще форков не может быть больше 256, так ещё и операционное окружение не может обеспечить достаточную скорость порождения и утилизации форков из-за слабого железа (медленный одноядерный процессор, медленная шина, fork() под нагрузкой получается дорогим удовольствием).

Вобщем приняв во внимание эту ситуацию было решено не использовать worker mpm для апача (worker mpm в принципе на linux оправдан, а на фрюхе например fork лучше работает, чем порождение потоков), тем более что вроде бы не особо дружит с mod_php, а взяться за реализацию fastcgi, благо nginx это умеет, да ещё и не только по tcp, но и по unix domain socket, кстати второй вариант предпочтительнее опять же на фрюхе, причём при неочень больших нагрузках на файловую систему, на linux tcp работает быстрее чем unix domain socket, на фрюхе - наоборот, но только при условии что файловая система не загружена большим количеством операций ввода-вывода, иначе и на фре лучше использовать tcp.

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

Итак, наверно, надо бы описать боян - как настроить эту байду. Я использую debian, так что в настройке сайта (/etc/nginx/sites-available/site) мне пришлось дописать вот что:

** location ~ \.php$ {
if (!-f $request_filename) {
return 404;
}
include /etc/nginx/fastcgi_params;
fastcgi_pass  127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param  SCRIPT_FILENAME  /var/www$fastcgi_script_name;
}**

вот <a href="http://exs.elm.ru/snippets/site.txt" target="_blank">пример</a> полной записи виртуального хоста.
прошу заметить переменную SCRIPT_FILENAME - она в обязательном порядке должна указывать на корень веб-сайта, в дефолтной конфигурации nginx которая идёт в debian эти строки надо просто раскомментировать. Таким образом получается, что у меня на данный момент работает php в режиме fastcgi а все остальные скрипты проксируются на apache.

С nginx закончили, теперь надо настроить ту часть, которая будет висеть по адресу 127.0.0.1 на порту 9000.
Для этого я пошёл по пути наименьшего сопротивления и запользовал spawn-fcgi из lighttpd (в и-нете рекомендуют скачать и собрать lighttpd но не устанавливать а только скопировать бинарник), благо у меня была под рукой система с уже установленным lighttpd, откуда я и позаимствовал этот бинарничек и само-собой надо было этот спаунер как-то запускать - дело не хитрое:

**#!/bin/bash**

**PHP_SCRIPT=’/usr/local/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u www-data -g www-data -C 9 -f /usr/bin/php5-cgi’
RETVAL=0

****case “$1″ in
start)
$PHP_SCRIPT
RETVAL=$?
;;
stop)
killall -9 php5-cgi
RETVAL=$?
;;
restart)
killall -9 php5-cgi
sleep 1
$PHP_SCRIPT
RETVAL=$?
;;
*)
echo “Usage: $0 {start|stop|restart}”
RETVAL='1'
;;
esac
exit $RETVAL
**

В результате в худшем случае обслуживается порядка 730 запросов из 1000, что уже в принципе не так уж плохо для синтетического теста. Скорость исполнения скриптов незначительно быстрее по сранению с Apache mod_php (prefork mpm), замерялась при условии непараллельного запуска на незагруженном сервере, понятно, что под нагрузкой всё будет зависеть (кроме как от процессора) от количества обработчиков fastcgi запросов.

Next Post