FastCGI jest bardzo często kojarzone wyłącznie z Perl'em lub PHP, w przypadku serwera Lighttpd, użycie FastCGI jest jedynym sposobem aby zmusić go do pracy z PHP, ale musimy wiedzieć, że jest niezależne od języka programowania i serwera WWW, jest to raczej coś w rodzaju sposobu komunikacji pomiędzy serwerem a aplikacją (pomostem). Zasadniczą różnicą pomiędzy CGI a FastCGI jest wydajność, która została znacznie podniesiona poprzez wyeliminowanie konieczności uruchamiania skryptu/aplikacji przy każdym odwołaniu. W przypadku CGI serwer WWW otrzymując żądanie od przeglądarki uruchamiał "na boku" aplikację i zwracał wynik, natomiast dzięki FastCGI istnieje możliwość uruchomienia konkretnej liczby instancji aplikacji, które w trybie ciągłym oczekują na wywołanie bez konieczności każdorazowego uruchamiania procesu.
Przewagą FastCGI nad CGI może być również to, że uruchomiona aplikacja jest w stanie przechowywać różne wartości w pamięci do których możemy się odwołać w dowolnym momencie, w przypadku CGI nie było to możliwe, ponieważ po obsłużeniu każdego żądania proces umierał i zawartość buforów była tracona.
Procesy FastCGI (aplikacje) są całkowicie niezależne od serwera, po prostu działają obok, a to zdecydowanie utrudnia wywołanie awarii w której padnie serwer WWW.
Jeśli chodzi o skalowanie, to sprawa wygląda tak, że serwer WWW otrzymuje request od użytkownika i przekazuje go w pełni do aplikacji fcgi, w serwerze WWW możemy określić w jaki sposób ma nastąpić połączenie (np. socket, lub tcp), dzięki temu możemy mieć odseparowane aplikacje od warstwy HTTP.
Spróbujmy napisać i uruchomić prostą aplikację FastCGI w C (Lighttpd + Ubuntu):
Instalacja niezbędnej biblioteki libfcgi do obsługi FastCGI w ANSI C
# apt-get install libfcgi-dev
Konfiguracja serwera Lighttpd:
# cat /etc/lighttpd/conf-enabled/10-fastcgi.conf
server.modules += ( "mod_fastcgi" )fastcgi.server = ( ".fcgi" =>
((
"bin-path" => "/tmp/app-cgi",
"socket" => "/tmp/cgi.socket",
"max-procs" => 4,
"check-local" => "disable",
"bin-copy-environment" => (
"PATH", "SHELL", "USER"
),
))
)
- server.modules - taki zapis pozwala na dodanie do listy modułów, modułu mod_fastcgi
- fastcgi.server - ".fcgi" - określa dla których rozszerzeń żądanie ma być przekazane do aplikacji fcgi
- bin-path - ścieżka do naszej aplikacji
- socket - ściezka do socketu po których będzie odbywała się komunikacja (lighttpd -> fcgi), istnieje również możliwość komunikacji po tcp, wtedy nasza aplikacja fcgi może być uruchomiona na zdalnym serwerze (klastrze)
- max-procs - maksymalna liczba procesow naszej aplikacji fcgi
- idle-timeout - czas bezczynności
- check-local - jeśli jest enable, to serwer lighttpd będzie najpierw szukał pliku ihha.fcgi w document.root serwera, jeśli nie znajdzie to zwróci 404, natomiast jeśli jest ustawiona na disable to przekaże od razu request do aplikacji fcgi
- bin-copy-environment - pozwala na kopiowanie wartości zmiennych środowiskowych (PATH, SHELL, USER)
Kod naszej aplikacji:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include "fcgi_stdio.h" #include <stdlib.h> int count; void main(void) { count = 0; while (FCGI_Accept() >= 0) { printf("Content-type: text/htmlrnrn" "<h1>FastCGI App!</h1>" "Request: %d <br />" "PID: %d", ++count, getpid()); } } |
- #include "fcgi_stdio.h" - plik nagłówkowy dla fcgi
- int count - deklaracja zmiennej count - licznika obsłużonych żądań (dla każdego procesu app-cgi jest unikalny)
- while (FCGI_Accept() >= 0) - pętla główna naszej aplikacji (specyficzne dla fastcgi, ponieważ aplikacja nie kończy działania)
- printf("Content-type: text/html") - wysyłamy nagłówek HTTP mówiący o tym, że kontent będzie danymi w postaci text/html (lighttpd przekazuje całe żądanie do aplikacji fastcgi, tak więc aplikacja musi odpowiedzieć wraz z nagłówkami, ten jest niezbędny aby wyświetlić tekst w przeglądarce)
- ++count, getpid() - inkrementacja licznika count oraz pobranie pidu procesu (argumenty dla printf(%d %d))
Kompilacja odbywa się w następujący sposób:
# gcc /tmp/cgi-app.c -lfcgi -o /tmp/app-cgi
- -lfcgi - mówi kompilatorowi by zlinkował nasz program app-cgi z biblioteką fcgi
# ldd /tmp/app-cgi
linux-gate.so.1 => (0x0070c000)
libfcgi.so.0 => /usr/lib/libfcgi.so.0 (0x00ef8000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00ce9000)
libnsl.so.1 => /lib/tls/i686/cmov/libnsl.so.1 (0x005fc000)
libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0x00234000)
/lib/ld-linux.so.2 (0x00665000)
Mając skompilowaną aplikację i skonfigurowany serwer WWW możemy spróbować uruchomić całość:
# /etc/init.d/lighttpd restart
Dodatkowo możemy sprawdzić czy nasza aplikacja FCGI została poprawnie zespawnowana:
# pstree | grep lighttpd
|-lighttpd---4*[app-cgi]
Widzimy 1 proces lighttpd i dodatkowe 4 procesy app-cgi ;-) czyli wszystko gra. Pobieramy stronę:
$ lwp-request http://192.168.1.194/app.fcgi
<h1>FastCGI App!</h1>Request: 1 <br />PID: 11282
Wszystko działa poprawnie, serwer nam zwrócił numer requestu: 1 dla konkretnego procesu app-cgi (PID). W przypadku dużego ruchu, Lighttpd rozrzuci ruch pomiędzy 4 procesy app-cgi, ale należy pamiętać, że zmienne aplikacji są tylko w obrębie jednego procesu - count dla każdego procesu app-cgi będzie inny.
Czy jest sens używania FastCGI? Oczywiście, że tak. W wielu zastosowaniach taka aplikacja będzie dużo bardziej wydajna niż skrypt napisany w PHP, czasami nie ma potrzeby zaprzęgania całego interpretera PHP czy Perl, do wykonania jakiejś prostej czynności, wszyscy przecież wiemy, że Linuks lubi C. ;-)
A Wy korzystacie z FastCGI? albo widzicie jakieś ciekawe zastosowanie?