什麼是FastCGI

FastCGI :Fast Common Gateway Interface(快速通用網關接口),它是CGI的增強版。FastCGI是一個快速、開放和安全的web server接口,解決了傳統CGI的性能問題,卻又沒有帶來編程的複雜性。老的CGI程序可以很輕易的移植成FastCGI程序。

FastCGI的技術原理

如果想了解FastCGI的技術原理就要了解何為"短生存期應用程序",何為"長生存期應用程序"。

先從CGI技術開刀,以下是CGI技術的理論:每次當客户請求一個CGI的時候,Web服務器就請求操作系統生成一個新的CGI進程。當CGI滿足要求後,服務器就殺死這個進程。服務器對客户端的每個請求都要重複這樣的過程。

而FastCGI技術的理論為:FastCGI程序一旦產生後,他可以持續工作,足夠滿足客户的請求直到被明確的終止。如果你希望通過協同處理來提高程序的性能,你可以請求Web服務器運行多個FastCGI 應用程序的副本。 CGI就是所謂的短生存期應用程序,FastCGI就是所謂的長生存期應用程序。

由於FastCGI程序並不需要不斷的產生新進程,可以大大降低服務器的壓力。並且產生較高的應用效率。

FastCGI的特點

  1. 打破傳統頁面處理技術

傳統的頁面處理技術,程序必須與Web服務器或Application服務器處於同一台服務器中。這種歷史已經早N年被FastCGI技術所打破,FastCGI技術的應用程序可以被安裝在服務器羣中的任何一台服務器,而通過TCP/IP協議與Web服務器通訊,這樣做既適合開發大型分佈式Web羣,也適合高效數據庫控制。

 

  1. 明確的請求模式

CGI技術沒有一個明確的角色,在FastCGI程序中,程序被賦予明確的角色(響應器角色(Responder)、認證器角色(Authorizer)、過濾器角色(Filter))。

 

  1. 獨立性 

架構獨立性:FastCGI接口並不綁定特定的應用服務器架構,應用既可以是單線程的也可是多線程的。

進程獨立性:FastCGI進程是獨立於服務器的進程,對FastCGI進程的調試不影響服務器。同理,FastCGI進程的崩潰也不至於服務器崩潰。

語言獨立性:FastCGI技術目前支持語言有:C/C++、Java、Perl、Tcl、Python、SmallTalk、Ruby等。

 

如何開發FastCGI程序

實現細節

先來看看傳統CGI是怎麼做的,如下圖:

fastcgi 實現restful 協議處理_默認值

 

圖一:CGI的數據流

CGI應用是通過標準的POSIX流(stdin, stdout, stderr和環境變量)加上環境變量,來與HTTP服務器進行通信。

 

與CGI類似,FastCGI採用的方法是:在HTTP進程和FastCGI進程之間創建一個全雙工的連接,數據報通過FastCGI協議封裝在兩個進程之間傳遞。

stdin和環境變量被封裝在連接的輸入部分;stdout和stderr被封裝在連接的輸出部分。

因此,作為輸入端,FastCGI程序從連接上接收數據,解包,分離出stdin和環境變量,然後調用應用處理邏輯。作為輸出端,FastCGI用FastCGI協議打包stdout和stderr,發送給HTTP服務器處理。

 

由於FastCGI應用不要求和HTTP服務器運行在同一個節點上,因此,FastCGI支持兩種形式的連接:1)流管道,用於在同一個節點上的通信;2)TCP流,用於不同節點上的通信。

下圖是FastCGI應用和HTTP服務器不在同一個機器上的數據流圖:

fastcgi 實現restful 協議處理_字段_02

 

圖二:FastCGI和HTTP不在同一節點的數據流

程序框架

不同於CGI程序的單次執行特性,FastCGI的主程序框架有一個循環,如下:


Initialize application;
while(FCGI_Accept() >= 0) {
Process request;
}

初始化部分,只執行一次,所以常常把一些耗時的工作放在此處理,如:打開和連接數據庫,計算表和位圖值等。

另外初始化部分,還完成環境變量初始化等工作。

 

然後,程序阻塞在FCGI_Accep()調用上,一旦有連接上了就處理,否則就阻塞。

 

一個簡單的FastCGI程序實例:

#include "fcgi_stdio.h" /* fcgi library; put it first*/
#include <stdlib.h>
 
int count;
 
void initialize(void)
{
  count=0;
}
 
void main(void)
{
/* Initialization. */  
  initialize();
 
/* Response loop. */
  while (FCGI_Accept() >= 0)   {
    printf("Content-type: text/html\r\n"
           "\r\n"
           "<title>FastCGI Hello! (C, fcgi_stdio library)</title>"
           "<h1>FastCGI Hello! (C, fcgi_stdio library)</h1>"
           "Request number %d running on host <i>%s</i>\n",
            ++count, getenv("SERVER_HOSTNAME"));
  }
}

另外一個例子,是從stdin和環境變量獲取表單參數的例子,由於代碼過長,以附件形式給出:

 

 

附錄:CGI常用環境變量

環境變量

説明

ALL_HTTP

未包括在本表格中給出的變量內的所有HTTP標題,如:這個變量來自表格HTTP_<標題欄名稱>

QUERY_STRING

傳遞給程式的 query 信息,即:在指向URL中跟在問號(?)後的信息

REMOTE_HOST

使用者發出 request 的遠端 host 名稱

REMOTE_ADDR

使用者發出 request 的遠端 IP 位址

AUTH_TYPE

用來確定使用者合法性的監定方法

REMOTE_USER

使用者的合法名稱

REMOTE_IDENT

發出 request 的使用者

CONTENT_TYPE

query 信息中的 MIME 類型

CONTENT_LENGTH

以字節為單位的從客户端接收來的腳步長度

HTTP_FORM

使用者發出 request 的電子郵件訊息

HTTP_ACCEPT

client 可以接受的 MIME 類型列表

HTTP_COOKIE

與你站點打交道的客户的任何cookie(存儲片)集合

HTTP_USER_AGENT

client 用來發出 request 的瀏覽器

GATEWAY_INTERFACE

Server 使用的 CGI 版本

SERVER_NAME

Server 的 host 名稱或 IP 位址

SERVER_SOFTWARE

迴應 client request 的 Server 軟體名稱和版本

SERVER_PROTOCOL

傳遞資訊所用的協定名稱或版本

SERVER_PORT

Server 正在執行的 port number

SERVER_PORT_SECURE

值為0或1,值1表示請求出現在加密端口

URL

請求的URL地址

REQUEST_METHOD

發出 request 的方法

PATH_INFO

傳遞給 CGI 程式的額外路徑

PATH_TRANSLATED

存在 PATH_INFO 中的給定路徑的傳遞版本

SCRIPT_NAME

程式執行時的 virtual path

DOCUMENT_ROOT

網路提供的文件服務所在路徑

HTTP_REFERER

在讀取 CGI 程式前,client 所指的文件 URL

 

 

這個模塊允許nginx同FastCGI協同工作,並且控制哪些參數將被安全傳遞。

例:

location / 
 
{  
fastcgi_pass   localhost:9000;  
fastcgi_index  index.php;   
fastcgi_param  SCRIPT_FILENAME  /home/www/scripts/php$fastcgi_script_name;  
fastcgi_param  QUERY_STRING     $query_string;  
fastcgi_param  REQUEST_METHOD   $request_method;  
fastcgi_param  CONTENT_TYPE     $content_type;  
fastcgi_param  CONTENT_LENGTH   $content_length;
}
 
一個在緩存中的實例:
 
http 
{  
fastcgi_cache_path   /path/to/cache  
levels=1:2          keys_zone=NAME:10m      inactive=5m   clean_time=2h00m;  
	server
	{    
		location / 
		{      
 
		fastcgi_pass    http://127.0.0.1;     
		fastcgi_cache   NAME;      
		fastcgi_cache_valid   200 302  1h;      
		fastcgi_cache_valid   301      1d;      
		fastcgi_cache_valid   any      1m;      
		fastcgi_cache_min_uses  1;      
		fastcgi_cache_use_stale error  timeout invalid_header http_500;    
		} 
	}
}

 

0.7.48以後,緩存遵循後端服務器的Cache-Control, Expires等。

 

fastcgi_buffers

語法:fastcgi_buffers the_number is_size;

默認值:fastcgi_buffers 8 4k/8k;

使用字段:http, server, location

這個參數指定了從FastCGI服務器到來的應答,本地將用多少和多大的緩衝區讀取,默認這個參數等於分頁大小,根據環境的不同可能是4K, 8K或16K。

 

fastcgi_buffer_size

語法:fastcgi_buffer_size the_size ;

默認值:fastcgi_buffer_size 4k/8k ;

使用字段:http, server, location

這個參數指定將用多大的緩衝區來讀取從FastCGI服務器到來應答的第一部分。

通常來説在這個部分中包含一個小的應答頭。

默認這個值為fastcgi_buffers指令中的每塊大小,可以將這個值設置更小。

 

fastcgi_cache

語法:fastcgi_cache zone;

默認值:off

使用字段:http, server, location

為緩存實際使用的共享內存指定一個區域,相同的區域可以用在不同的地方。

 

fastcgi_cache_key

語法:fastcgi_cache_key line ;

默認值:none

使用字段:http, server, location

設置緩存的關鍵字,如:

fastcgi_cache_key localhost: 9000 $ request_uri;

 

fastcgi_cache_methods

語法:fastcgi_cache_methods [GET HEAD POST];

默認值:fastcgi_cache_methods GET HEAD;

使用字段:main,http,location

無法禁用GET/HEAD ,即使你只是這樣設置:

fastcgi_cache_methods  POST;

 

fastcgi_cache_min_uses

語法:fastcgi_cache_min_uses n

默認值:fastcgi_cache_min_uses 1

使用字段:http, server, location

 

fastcgi_cache_path

語法:fastcgi_cache_path /path/to/cache [levels=m:n keys_zone=name:time inactive=time clean_time=time]

默認值:none

使用字段:http, server, location

 

fastcgi_cache_use_stale

語法:fastcgi_cache_use_stale [updating|error|timeout|invalid_header|http_500]

默認值:fastcgi_cache_use_stale off;

使用字段:http, server, location

 

fastcgi_cache_valid

語法:fastcgi_cache_valid [http_error_code|time]

默認值:none

使用字段:http, server, location

 

fastcgi_index

語法:fastcgi_index file

默認值:none

使用字段:http, server, location

如果URI以斜線結尾,參數將加到URI後面,這個值將存儲在變量$fastcgi_script_name中。

 

fastcgi_hide_header

語法:fastcgi_hide_header name

使用字段:http, server, location

默認情況下nginx不會將來自FastCGI服務器的"Status"和"X-Accel-..."頭傳送到客户端,這個參數也可以隱藏某些其它的頭。

如果必須傳遞"Status"和"X-Accel-..."頭,則必須使用fastcgi_pass_header強制其傳送到客户端。

 

fastcgi_ignore_client_abort

語法:fastcgi_ignore_client_abort on|off

默認值:fastcgi_ignore_client_abort off

使用字段:http, server, location

如果當前連接請求FastCGI服務器失敗,為防止其與nginx服務器斷開連接,可以用這個指令。

 

fastcgi_intercept_errors

語法:fastcgi_intercept_errors on|off

默認值:fastcgi_intercept_errors off

使用字段:http, server, location

這個指令指定是否傳遞4xx和5xx錯誤信息到客户端,或者允許nginx使用error_page處理錯誤信息。

你必須明確的在error_page中指定處理方法使這個參數有效,正如Igor所説“如果沒有適當的處理方法,nginx不會攔截一個錯誤,

這個錯誤不 會顯示自己的默認頁面,這裏允許通過某些方法攔截錯誤。

 

fastcgi_max_temp_file_size

語法:fastcgi_max_temp_file_size 0

默認值:?

使用字段:?

根據源代碼關閉FastCGI緩衝。

 

fastcgi_param

語法:fastcgi_param parameter value

默認值:none

使用字段:http, server, location

指定一些傳遞到FastCGI服務器的參數。

可以使用字符串,變量,或者其組合,這裏的設置不會繼承到其他的字段,設置在當前字段會清除掉任何之前的定義。

下面是一個PHP需要使用的最少參數:

fastcgi_param  SCRIPT_FILENAME  /home/www/scripts/php$fastcgi_script_name;  fastcgi_param  QUERY_STRING     $query_string;

PHP使用SCRIPT_FILENAME參數決定需要執行哪個腳本,QUERY_STRING包含請求中的某些參數。

如果要處理POST請求,則需要另外增加三個參數:

fastcgi_param  REQUEST_METHOD   $request_method;  

fastcgi_param  CONTENT_TYPE     $content_type;  

fastcgi_param  CONTENT_LENGTH   $content_length;

如果PHP在編譯時帶有--enable-force-cgi-redirect,則必須傳遞值為200的REDIRECT_STATUS參數:

fastcgi_param  REDIRECT_STATUS  200;
 
fastcgi_pass
語法:fastcgi_pass fastcgi-server 
默認值:none 
使用字段:http, server, location 
指定FastCGI服務器監聽端口與地址,可以是本機或者其它:
fastcgi_pass   localhost:9000;
使用Unix socket:
fastcgi_pass   unix:/tmp/fastcgi.socket;
同樣可以使用一個upstream字段名稱:
upstream backend  
{  
server   localhost:1234;
} 
fastcgi_pass   backend;
 
fastcgi_pass_header

語法:fastcgi_pass_header name

默認值:none

使用字段:http, server, location

 

fastcgi_read_timeout

語法:fastcgi_read_timeout time

默認值:60

使用字段:http, server, location

前端FastCGI服務器的響應超時時間,如果有一些直到它們運行完才有輸出的長時間運行的FastCGI進程,或者在錯誤日誌中出現前端服務器響應超時

錯誤,可能需要調整這個值。

 

fastcgi_redirect_errors

語法:fastcgi_redirect_errors on|off

將來自fastcgi_intercept_errors的錯誤重命名。參數傳遞到FastCGI服務器。

請求頭以參數的形式傳遞到FastCGI服務器,在FastCGI的應用程序或者腳本中運行,這些參數通常是環境變量的形式,例如:User-agent

頭是以HTTP_USER_AGENT形式轉發,另外可以藉助fastcgi_param指令可以傳遞任意參數的HTTP請求頭。

 

fastcgi_split_path_info
語法:fastcgi_split_path_info regex 
使用字段:location 
可用版本:0.7.31以上
location ~ ^(.+\.php)(.*)$ 
 
{
...
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param SCRIPT_FILENAME /path/to/php$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
...
}
 
fastcgi_store
語法:fastcgi_store [on | off | path] 
默認值:fastcgi_store off 
使用字段:http, server, location

制定了存儲前端文件的路徑,參數on指定了將使用root和alias指令相同的路徑,off禁止存儲,此外,參數中可以使用變量使路徑名更明確:

fastcgi_store   /data/www$original_uri;

應答中的"Last-Modified"頭將設置文件的最後修改時間,為了使這些文件更加安全,可以將其在一個目錄中存為臨時文件,使用 fastcgi_temp_path指令。

這個指令可以用在為那些不是經常改變的後端動態輸出創建本地拷貝的過程中。如:

location /images/ 
{  
root                 /data/www;  
error_page           404 = /fetch$uri;
} 
location /fetch 
 
{  
internal;   
fastcgi_pass           fastcgi://backend; 
fastcgi_store          on; 
fastcgi_store_access   user:rw  group:rw  all:r;  
fastcgi_temp_path      /data/temp;   
alias                  /data/www;
}

fastcgi_store並不是緩存,某些需求下它更像是一個鏡像。

 

fastcgi_store_access

語法:fastcgi_store_access users:permissions [users:permission ...]

默認值:fastcgi_store_access user:rw

使用字段:http, server, location

這個參數指定創建文件或目錄的權限,例如:

fastcgi_store_access  user:rw  group:rw  all:r;

如果要指定一個組的人的相關權限,可以不寫用户,如:

fastcgi_store_access  group:rw  all:r;

·變量

$fastcgi_script_name

這個變量等於一個以斜線結尾的請求URI加上fastcgi_index給定的參數。可以用這個變量代替SCRIPT_FILENAME 和PATH_TRANSLATED,以確定php腳本的名稱。

如下例,請求"/info/":

fastcgi_index  index.php;  fastcgi_param  SCRIPT_FILENAME  /home/www/scripts/php$fastcgi_script_name;

SCRIPT_FILENAME等於"/home/www/scripts/php/info/index.php"