Автор: | Jan Kneschke |
---|---|
Дата: | 2004-11-03 |
Версия: | 1.3 |
Суть
Интерфейс FastCGI это быстрейший и наиболее безопастный способ обработки запросов внешними программами, тикими как Perl, PHP а также вашими самописными приложениями.
lighttp предостовляет интерфейс ко внешним программам которые поддерживают FastCGI интерфейс. FastCGI Интерфейс определён http://www.fastcgi.com/ и это платформо-независимый и серверо-независимый интерфейс между web-приложением и web-сервером.
Это подразумевает что FastCGI программы запущенные с Apache web-сервером также запустятся с lighttpd и наоборот.
FastCGI ликвидирует множество ограничений CGI программ. Проблема CGI программ в том что они должны быть перезапущенны web-сервером при каждом запросе, что приводит к понижению производительности.
FastCGI убирает это ограничение сохраняя процесс запущенным и передавая запросы этому постоянно запущенному процессу. Это позволяет не тратить время на создание (fork()) новых процессов.
Пока CGI программы соединены с сервером через pipe'ы, FastCGI процессы используют Unix-Domain-Sockets или TCP/IP для связи с сервером. Это даёт следующее преимущество над обычными CGI программами: FastCGI программы могут быть запущенны не только на этом же сервере, но и где угодно в сети
lighttpd включает в себя внутренный FastCGI распределитель нагрузки который может использоваться для распределения сразу на несколько FastCGI серверов. В отличие от иных решений только FastCGI процесс должен находиться в кластере, а не целый web-сервер. Это позволяет использовать FastCGI процессу больше резурсов чем, например, load-balancer+apache+mod_php.
Если вы сравните FastCGI с apache+mod_php вы должны обратить внимание на то, что FastCGI обеспечивает дополнительную безопастность, как запуст FastCGI процесса под пользователем отличным от пользователя web-сервера, а также может находиться в chroot'е отличным от chroot'а web-сервера.
Поддержка FastCGI в lighttod предоставляется через модуль fastcgi (mod_fastcgi) который имеет 2 опции в конфигурационном файле:
сообщает модулю куда надо отправлять FastCGI вызовы. каждое расширение файла может отправлять своему собственному FastCGI серверу. Балансировка нагрузки реализуется указанием нескольких FastCGI сервером для одного расширения.
структура fastcgi.server секции:
( <extension> => ( <handle> => ( "host" => <string> , "port" => <integer> , "socket" => <string>, # socket # или пара host+port "bin-path" => <string>, # ОПЦИОНАЛЬНО "bin-environment" => <array>, # ОПЦИОНАЛЬНО "bin-copy-environment" => <array>, # ОПЦИОНАЛЬНО "mode" => <string>, # ОПЦИОНАЛЬНО "docroot" => <string> , # ОПЦИОНАЛЬНО если "mode" # не "authorizer" "check-local" => <string>, # ОПЦИОНАЛЬНО "min-procs" => <integer>, # ОПЦИОНАЛЬНО "max-procs" => <integer>, # ОПЦИОНАЛЬНО "max-load-per-proc" => <integer>, # ОПЦИОНАЛЬНО "idle-timeout" => <integer> # ОПЦИОНАЛЬНО ) ), ( <handle> => ... ) )
<extension>: | расширение файла или префикс (если начинается с "/") |
---|---|
<handle>: | уникальное имя для этого handle |
"host": | имя хоста/ip данного FastCGI процесса |
"port": | tcp-порт на "host" используемый FastCGI процессом |
"bin-path": | путь к локальному исполняемому файлу FastCGI который должен быть запущен если не используется отдельный FastCGI процесс |
"socket": | путь к unix-domain socket |
"mode": | режим FastCGI протокола. По умолчанию это "responder", также есть режим "authorizer" . |
"docroot": | это опционально, и это docroot (корневая директория для документов) на удалённом хосте для режима "responder" mode. Для режима "authorizer" это ОБЯЗАТЕЛЬНО и указывает на docroot для авторизованных запросов. По причинам безопастности рекомендуется держать этот docroot вне дерева каталога server.document-root. |
"check-local": | опционально и может быть включено "enable" (по умолчанию) или "disable". Если включено то сервер сначала проверяет файл в локальном server.document-root каталоге и возвращает 404 (Not Found) если такого файла нет. Если выключено, то сервер перенаправляет запрос к FastCGI интерфейсу без этой проверки. |
Если указана bin-path:
"min-procs": | выставляет минимальное количество процессов стартующих при запуске |
---|---|
"max-procs": | верхний лимит запущенных процессов |
"max-load-per-proc": | |
максимальное количество ожидающих процессов до того как запуститься новый процесс | |
"idle-timeout": | количество секунд после прошествия которых неиспользуемый процесс будет уничтожен |
"bin-environment": | |
помещает пременную окружения для запускаемого процесса | |
"bin-copy-environement": | |
очищает переменные окружения и копирует только указанные переменные в новое окружение создаваемого процесса |
Множественные расширения для того же хоста
fastcgi.server = ( ".php" => ( "grisu" => ( "host" => "127.0.0.1", "port" => 1026, "bin-path" => "/usr/local/bin/php" ) ), ".php4" => ( "grisu" => ( "host" => "127.0.0.1", "port" => 1026 ) ) )Пример с префиксом:
fastcgi.server = ( "/remote_scripts" => ( "fcg" => ( "host" => "192.168.0.3", "port" => 9000, "check-local" => "disable", "docroot" => "/" # удалённый сервер может использовать # свой docroot ) ) )Запрос http://my.host.com/remote_scripts/test.cgi будет перенаправлен на fastcgi сервер на 192.168.0.3 и значение "/remote_scripts/test.cgi" будет использовано для переменной SCRIPT_NAME. Удалённый сервер может обработать его со своим собственным document root. Обработка index файлов в данном случае также ложиться на удалённый сервер.
Пример для режима "authorizer":
fastcgi.server = ( "/remote_scripts" => ( "auth" => ( "host" => "10.0.0.2", "port" => 9000, "docroot" => "/path_to_private_docs", "mode" => "authorizer" ) ) )Заметьте что если "docroot" определена, тогда её значение будет использовано в переменных DOCUMENT_ROOT и SCRIPT_FILENAME FastCGI сервера.
FastCGI plugin предоставляет автоматическое распределение нагрузки между несколькими FastCGI серверами.
fastcgi.server = ( ".php" => ( "server1" => ( "host" => "10.0.0.3", "port" => 1030 ), "server2" => ( "host" => "10.0.0.3", "port" => 1030 ) ) )Чтобы понять как работает распределение нагрузки вы можете включить опцию fastcgi.debug и получить вывод подобный этому:
proc: 127.0.0.1 1031 1 1 1 31454 proc: 127.0.0.1 1028 1 1 1 31442 proc: 127.0.0.1 1030 1 1 1 31449 proc: 127.0.0.1 1029 1 1 2 31447 proc: 127.0.0.1 1026 1 1 2 31438 got proc: 34 31454 release proc: 40 31438 proc: 127.0.0.1 1026 1 1 1 31438 proc: 127.0.0.1 1028 1 1 1 31442 proc: 127.0.0.1 1030 1 1 1 31449 proc: 127.0.0.1 1031 1 1 2 31454 proc: 127.0.0.1 1029 1 1 2 31447Данный вывод показывает множество породений FastCGI на локальной машине. Следующее объяснение верно также и для удалённых соединений.
Вывод показывает:
Как вы можете видеть список всё время упорядочен по полю загрузуки
При запросе нового соеденения, выбирается первый указатель на FastCGI процесс (один с наименьшей нагрузкой), значение загрузки увеличивается на 1 (got proc: ...) и список сортируется вновь.
Если FastCGI запрос заканчивается или соединение обрывается, загрузка FastCGI proc уменьшается на 1 и список опять сортируется (release proc: ...)
Такое поведение занимает мало кода, весьма эффективно, и позволяет использовать fastcgi-сервера равнозагруженно, даже если они имеют разные CPU.
Начиная с 1.3.8 lighttpd может создавать просцессы по запросу если определена bin-path, и FastCGI процессы запускаются локально.
Если вы хотите иметь запущенным по крайней мере один FastCGI процесс и больше при запросах, вы можете использовать min-procs и max-procs.
Новый процесс запускается как только среднее количество запросов ожидающих обработку одним процессом превысит max-load-per-proc.
Параметр idle-timeout определяет как долго fastcgi-процесс должен ожидать новый запрос прежде чем завешит свою работу
fastcgi.server = ( ".php" => ( "localhost" => ( "socket" => "/tmp/php.socket", "bin-path" => "/usr/local/bin/php", "min-procs" => 1, "max-procs" => 32, "max-load-per-proc" => 4, "idle-timeout" => 20 ) ) )
Adaptive Spawning всё ещё новая возможность, и может вести себя нестабильно. Здесь указаны несколько возможностей как контролировать создание новых процессов:
"max-load-per-proc" => 1 если это работает у вас то всё хорошо.
Если не выставлено min-procs == max-procs.
Для PHP вы также можете использовать:
$ PHP_FCGI_CHILDREN=384 ./lighttpd -f ./lighttpd.conf fastcgi.server = ( ".php" => ( "localhost" => ( "socket" => "/tmp/php.socket", "bin-path" => "/usr/local/bin/php", "min-procs" => 1, "max-procs" => 1, "max-load-per-proc" => 4, "idle-timeout" => 20 ) ) )Это создаст один socket и позволит PHP самому создать 384 процесса.
Если вы не хотите позволять lighttpd управлять fastcgi процессами, уберите bin-path и используйте spawn-fcgi чтобы FastCGI создавал процессы сам
Одно из наиболее важных приложений которое имеет FastCGI интерфейс это php который может быть скачан с http://www.php.net/ . Вы должны перекомпилировать php из исходников чтобы активировать FastCGI интерфейс, т.к. он не включен по умолчанию.
Если у вас уже имеется работающий PHP на web-сервере, выполните короткий скрипт который просто содержит
<?php phpinfo(); ?>и посмотрите на строки содержащие вызов configure. Вы можете использовать их как основы для компиляции.
Вы должны удалить опции --with-apxs, --with-apxs2 и те которые используются для компилирования с поддержкой Apache. Добавьте следующие три опции для компиляции PHP с поддержкой FastCGI:
$ ./configure \ --enable-fastcgi \ --enable-discard-path \ --enable-force-cgi-redirect \ ...После компиляции и инстраляции проверьте что ваш PHP поддерживает FastCGI, выполнив:
$ php -v PHP 4.3.3RC2-dev (cgi-fcgi) (built: Oct 19 2003 23:19:17)Обратите внимание на (cgi-fcgi).
Важно чтобы php.ini содержал:
cgi.fix_pathinfo = 1В противном случае PHP_SELF не примет правильное значение.
Начиная с версии 1.3.6 lighttpd может сам создавать FastCGI процессы если необходимо:
fastcgi.server = ( ".php" => ( "localhost" => ( "socket" => "/tmp/php-fastcgi.socket", "bin-path" => "/usr/local/bin/php" ) ) )PHP предоставляет 2 специальные переменные окружения которые контролируют число рабочих запущенных процессов под контроллем одного наблюдательного процесса (PHP_FCGI_CHILDREN) и число запросов которые один рабочий процесс обработает до завершения.
fastcgi.server = ( ".php" => ( "localhost" => ( "socket" => "/tmp/php-fastcgi.socket", "bin-path" => "/usr/local/bin/php", "bin-environment" => ( "PHP_FCGI_CHILDREN" => "16", "PHP_FCGI_MAX_REQUESTS" => "10000" ) ) ) )Чтобы улучшить безопастность запущенных процессов вы только должны передать необходимые переменные окружения FastCGI процессу.
fastcgi.server = ( ".php" => ( "localhost" => ( "socket" => "/tmp/php-fastcgi.socket", "bin-path" => "/usr/local/bin/php", "bin-environment" => ( "PHP_FCGI_CHILDREN" => "16", "PHP_FCGI_MAX_REQUESTS" => "10000" ), "bin-copy-environment" => ( "PATH", "SHELL", "USER" ) ) ) )
Создание процесса FastCGI прямо в web-сервере имеет следующие недостатки
Как только вы начнёте использовать отдельный FastCGI сервер чтобы снять нагрузку с web-сервера, вы сможете контролировать процесс FastCGI внешними программами, такими как spawn-fcgi.
spawn-fcgi используется чтобы запустить FastCGI процесс в своём окружении, выставить ему user-id, group-id и сменить корневую директорию (chroot).
Для большего удобства должен быть использван wrapper скрипт берущий на себя заботу обо всех опциях. Такой скрипт включён в состав lighttpd,- spawn-php.sh.
Скрипт использует набор конфигурационных переменных на которые вы должны обратить внимание:
## АБСОЛЮТНЫЙ путь к исполняемому файлу spawn-fcgi SPAWNFCGI="/usr/local/sbin/spawn-fcgi" ## АБСОЛЮТНЫЙ путь к исполняемому файлу PHP FCGIPROGRAM="/usr/local/bin/php" ## bind к tcp-порту на localhost FCGIPORT="1026" ## bind к unix domain socket # FCGISOCKET="/tmp/php.sock" ## число запускаемых PHP потомков PHP_FCGI_CHILDREN=10 ## число запросов которые будет обрабатывать один php-процесс пока ## он не перезапуститься PHP_FCGI_MAX_REQUESTS=1000 ## IP адресса с которых PHP позволяет соединяться FCGI_WEB_SERVER_ADDRS="127.0.0.1,192.168.0.1" # разрешённые переменные окружения разделямые пробелами ALLOWED_ENV="ORACLE_HOME PATH USER" ## если скрипт запущен root'ом, сменить на следующего владельца USERID=wwwrun GROUPID=wwwrunКак только вы указали необходимые вам значения, вы можете запустить spawn-php.sh:
$ spawn-php.sh spawn-fcgi.c.136: child spawned successfully: PID: 6925Если вы видите "child spawned successfully: PID:" значит php процесс запущен успешно. Вы должны увидеть их в своём списке процессов:
$ ps ax | grep php 6925 ? S 0:00 /usr/local/bin/php 6928 ? S 0:00 /usr/local/bin/php ...Число процессов должно быть PHP_FCGI_CHILDREN + 1. В данном случае процесс 6925 это master slave'ов работающих параллельно. Число рабочих процессов указывается в PHP_FCGI_CHILDREN. Рабочий процесс автоматически завершает свою работу после обработки PHP_FCGI_MAX_REQUESTS запросов, т.к. в PHP могут возникнуть утечки памяти.
Если вы запустите скрипт как пользователь root, php процессы будут работать с пользовательским USERID и GROUPID группы. В ином случае php процессы запустятся с правами того пользователя которые запустил скрипт.
Так как скрипт может быть запущен с неопределённого уровня запуска или даже напрямую с коммандной строки, он очищает переменные окружения прежде чем запустить процессы. ALLOWED_ENV содержит все внешние переменные окружения которые должны быть доступны php-процессам.
Для Perl вы должны установить FCGI модуль с CPAN.
Для TCL ...
Базовая функциональнось авторизатора (см. http://www.fastcgi.com/devkit/doc/fcgi-spec.html, 6.3 для подробностей).
#include <fcgi_stdio.h> #include <stdlib.h> #include <unistd.h> int main () { char* p; while (FCGI_Accept() >= 0) { /* wait for fastcgi authorizer request */ printf("Content-type: text/html\r\n"); if ((p = getenv("QUERY_STRING")) == NULL) || <QUERY_STRING is unauthorized>) printf("Status: 403 Forbidden\r\n\r\n"); else printf("\r\n"); /* default Status is 200 - allow access */ } return 0; }Это позволит использовать любое другое значение предостовляемое интерфейсом FastCGI для проверки авторизации. Здесь только пример.
fastcgi.debug должен быть включен для решения проблем.
Если вы получаете:
(fcgi.c.274) connect delayed: 8 (fcgi.c.289) connect succeeded: 8 (fcgi.c.745) unexpected end-of-file (perhaps the fastcgi process died): 8процесс fastcgi принимает соединения но закрывает их в любом случае. Это случается если FCGI_WEB_SERVER_ADDRS не включает хост с которого происходит соединение.
Если вы получаете
(fcgi.c.274) connect delayed: 7 (fcgi.c.1107) error: unexpected close of fastcgi connection for /peterp/seite1.php (no fastcgi process on host/port ?) (fcgi.c.1015) emergency exit: fastcgi: connection-fd: 5 fcgi-fd: 7процесс fastcgi не запущен на хосте/порту к которому вы соединяетесь. Проверьте вашу конфигурацию.
Если вы получаете
(fcgi.c.274) connect delayed: 7 (fcgi.c.289) connect succeeded: 7всё нормально. Вызов connect() просто немного задержался, но соединениё установилось.