實戰FastCGI

火星人 @ 2014-03-12 , reply:0


  當網站日益走紅,聯機人數直線上升而心中暗自竊喜之時,突然客服中心湧來大批反應電話:『網站連不上去』、『按下去等好久畫面才出來』、『一直出現 Server Too Busy...』...。看來又要把硬體升級了,但是再加更多的內存,更多 CPU、換更貴的機器真的能解決問題嗎?有沒有比較省錢的方法呢?本文將介紹如何在阿帕契伺服器上安裝 FastCGI 的模塊,如何設定及使用 FastCGI 網站應用程序,讓你的網站程序在現有的架構上以全速執行。
----------------------------------------------------------------------------

1. 克服 CGI 的瓶頸

1.1 令人頭痛的效率問題

1.2 一些解決之道

1.3 更好的方法 - FastCGI

2. 安裝 FastCGI

2.1 在阿帕契伺服器上安裝 FastCGI 模塊

2.1.1 標準安裝 (利用 APACI)

2.1.2 將 mod_fastcgi 安裝成一個 DSO

2.2 加入使用 mod_fastcgi 的相關設定

2.3 安裝 FastCGI 開發套件

2.4 測試 FastCGI

2.5 安裝 FCGI 模塊 for Perl

3. 撰寫 FastCGI 應用程序

3.1 程序架構

3.2 引入 fcgi_stdio.h 標頭檔

3.3 FastCGI 處理循環

3.4 煉結 libfcgi.a 函式庫

3.5 撰寫 FastCGI 程序的注意事項

4. FastCGI 有多快?

4.1 評比工具 - ApacheBench

4.2 CGI vs. FastCGI

4.3 找出 Memory Leak

5. 參考

About this document ...

----------------------------------------------------------------------------

1. 克服 CGI 的瓶頸

1.1 令人頭痛的效率問題

拜 CGI 之賜,網站不再只有固定不變的圖形和文字,藉由程序動態產生的網頁可以讓網站好象『活』了起來。小從簡單的網頁計數器,留言版,大至處理眾多資料的搜尋引擎,可做線上實時交易的電子商務、網路下單等。CGI 簡單、開放、跨平台、與程序語言獨立的特性,使得撰寫網站應用程序變得很容易。

但隨著網站使用量日增,這些 CGI 程序從原本動態網頁的功臣,突然成了網站效率的頭號殺手。由於 CGI 先天的限制1,突然湧入大量的聯機請求 (request) ,常會造成網站主機瞬間資源被佔用,彷佛『當機』一樣,或是處理速度變得很慢。

另一個常遇到的限制是和資料庫聯機的問題,如果 CGI 程序後端需要聯機至資料庫執行指令再取得結果,突然大量的聯機請求可能會超過資料庫系統容許聯機的上限 (例如資料庫系統使用者數目的限制)。

因此對一個主要以使用 CGI 程序製作動態網站的開發者而言,解決 CGI 執行效率瓶頸成了一個頭痛的問題。以一個股市實時行情報價的網站為例,每天的聯機請求將近八成集中在股市開盤的尖峰時段內,更是對網站應用程序極大的考驗。

1.2 一些解決之道

現在已經有許多方案被提出來以解決 CGI 執行效率上的瓶頸,在『用 FastCGI 加速你的網站』一文中也有簡單的說明,這裡僅就筆者在開發股市實時報價的網站應用程序時,所嘗試過的一些方法提出個人的經驗和意見。以筆者的案例而言,原本的 CGI 程序是以 C 語言寫的,並且用了其它的 C 函式庫所以下列的方法主要是以提供 C 語言開發環境的方案為主。

NSAPI

由於原先網站是在 Unix 系統上,網站伺服器使用網景 Enterprise Server,所以最早想到是用 NSAPI 來改寫網站應用程序。在網景的網站上有非常詳細的 NSAPI 使用手冊,不幸的是沒有中文手冊。要用 NSAPI 改寫網站應用程序最麻煩的是你要把所有程序編譯成動態函式庫 (share library),以供 Enterprise Server 在 run-time 時期可以動態呼叫這些程序。由於利用 NSAPI 所寫的程序是直接從 Web Server 的執行空間內被呼叫,所以速度最快,但是程序必須遵循 Enterprise Server 的撰寫規則,而且一旦程序發生錯誤, Web Server 也會受影響。

ISAPI

相較於 NSAPI ,在 Microsoft NT IIS (Internet Information Server) 平台就是 ISAPI 了。類似 NSAPI ,利用 ISAPI 撰寫網站應用程序,必須把應用程序編譯成動態函式庫,也就是 DLL ?n。它的執行速度也很快,但要遵循 ISAPI 的撰寫規則和數據結構,程序發生錯誤時也會影響 IIS Server 的正常運作。

綜觀以上兩種以 Web Server API 為主的方案 (其實 Apache 也有相對應的 Server API,只是用的人可能更少) ,它們的執行速度都很快,就產生動態網頁而言比 CGI 快上好幾倍。但是就程序開發者的角度2來看,它們有一些缺點:

1. NSAPI 及 ISAPI 與網站平台相依性太高 (Platform dependency),也就是說使用了 NSAPI 或 ISAPI 后,應用程序就完全受限於所使用的網站伺服器平台,不能變換所使用的網站伺服器。不像 CGI 完全不受網站平台的限制,可以在任何網站伺服器 (Netscape, Microsoft IIS, Apache, NCSA)上執行。另外像 ISAPI 更只能限制在 Windows NT 平台上使用。

2. NSAPI 及 ISAPI 只提供 C 程序語言的界面,亦即開發者一定要使用 C 語言開發。不像 CGI 是與開發者所使用的程序語言完全無關,除了 C 之外,常用的還有 Perl,Tcl等。

3. Netscape Enterprise Server 和 Microsoft IIS 都是以多執行緒 (Multi-Threads) 的方式處理 NSAPI 及 ISAPI 的程序,所有執行緒共享同一塊變數空間,因此在變數數據的處理上要特別小心,以確保每一個執行緒內的變數資料的安全,不會互相影響。

4. NSAPI 和 ISAPI 應用程序都是直接在伺服器的執行行程 (process) 內被呼叫,如果程序當掉了,整個網站伺服器都會被影響。CGI 當掉伺服器會響應 Internal Server Error 的訊息,伺服器本身不受影響。

5. NSAPI 和 ISAPI 應用程序必須被伺服器呼叫才會被執行,偵錯 (debug) 相當不容易。

1.3 更好的方法 - FastCGI

如果你正飽受 CGI 效率不佳之苦,又不想受限於 NSAPI 及 ISAPI ,也沒有大筆銀子去買昂貴的 Application Server,我建議你試試看 FastCGI。

不同於 NSAPI 及 ISAPI 以及其它的網頁伺服器語言 (如 ASP, PHP3, mod_perl),FastCGI 比較類似 CGI,它只是一個網站應用程序設計的規格,因此先天上不受任何網站伺服器平台,操作系統平台,以及開發語言的限制,但又能大幅改善 CGI 效率不良的問題。FastCGI 的特色如下:

1. FastCGI 像是一個常駐 (long-live) 型的 CGI,它可以一直執行著,只要激活后,不會每次都要花費時間去 fork 一次 (這是 CGI 最為人詬病的 fork-and-execute 模式)。

2. FastCGI 可在任何平台上使用,Netscape Enterprise 及 IIS 都有 FastCGI 的模塊可供使用,阿帕契 (Apache,以及利用 Apache 衍生出做的伺服器) 上也有 mod_fastcgi 可用。

3. FastCGI 支持 C/C++,Perl,Tcl,Java,Python 等程序語言。

4. FastCGI 的應用程序亦兼容於 CGI。即 FastCGI 的應用程序也可以當成 CGI 來執行。

5. 現有的 CGI 程序要改寫成 FastCGI 非常簡單,最少可能只需要多加入三行程序代碼。

6. FastCGI 的偵錯方式與 CGI 大同小異,只要帶入程序所需的環境變數及參數,即可在命令列模式執行或偵錯。

7. FastCGI 應用程序的寫作方式與 CGI 類似,除了幾項原則要特別注意外,FastCGI 的寫作方式跟 CGI 幾乎一樣,與學習 Web Server API 比較起來, FastCGI 簡單多了。

8. FastCGI 支授分散式運算 (distributed computing),即 FastCGI 程序可以在網站伺服器以外的主機上執行並且接受來自其它網站伺服器來的請求。

看到 FastCGI 這些特色后,是否躍躍欲試呢。下一章將介紹如何在 Apache 伺服器上安裝 FastCGI 的步驟。

----------------------------------------------------------------------------

2. 安裝 FastCGI

要使用 FastCGI 你必需有一個可供 FastCGI 程序執行的環境 (run-time environment),以及 撰寫 FastCGI 程序的開發環境。以下就以阿帕契伺服器 (Apache Web Server) 做為 FastCGI 的執行平台,簡述如何在阿帕契伺服器使用 FastCGI。

由於阿帕契伺服器自由、開放、跨平台的特性,現今許多系統或發行套件 (distribution) 都內含阿帕契伺服器,如果你直接用預先編譯好的阿帕契程序,請自行找出符合該系統設定規則的安裝路徑。以下列出一些阿帕契伺服器相關的路徑設定規則,後面的範例將以阿帕契內定值為主,其它的系統請自行參考:

系統 執行文件路徑 設定文件路徑

阿帕契內定值 /usr/local/apache/bin /usr/local/apache/etc

FreeBSD /usr/local/sbin /usr/local/etc/apache

Red Hat Linux /usr/sbin /usr/etc

2.1 在阿帕契伺服器上安裝 FastCGI 模塊

安裝 mod_fastcgi 這個模塊,可以讓你的阿帕契伺服器支持 FastCGI 協議。mod_fastcgi 現在最新版本為 2.2.2 版,此版主要適用於 Apache 1.3 版以上。如果你的 Apache 還是 1.2 版,請配合 mod_fastcgi 2.0.18 版使用。以下設定以 Apache 1.3.6 及 mod_fast 2.2.2 為示範。

2.1.1 標準安裝 (利用 APACI)

1. 首先下載 apache_1.3.6.tar.gz 及 mod_fastcgi_2.2.2.tar.gz ,解開:

$ gunzip -c apache_1.3.6.tar.gz | tar xvf -

$ gunzip -c mod_fastcgi_2.2.2.tar.gz | tar xvf -

2. 把 mod_fastcgi 的原始碼複製到 Apache 的目錄下:

$ cp -rp mod_fastcgi_2.2.2 apache_1.3.6/src/modules/fastcgi

3. 設定 Apache 加入 mod_fastcgi 模塊:

$ cd apache_1.3.6

$ ./configure \

-activate-module=src/modules/fastcgi/libfastcgi.a \

-enable-module=info -enable-shared=info \

[ more APACI options ]

4. 編譯及安裝

$ make

$ make install

5. 看一下編譯出來的執行文件是否含有 mod_fastcgi 模塊:

$ /usr/local/apache/sbin/httpd -l

Compiled-in modules:

http_core.c

...

mod_fastcgi.c

...

2.1.2 將 mod_fastcgi 安裝成一個 DSO

假設你已經在系統上安裝好 Apache 1.3 版以上,並且你的平台支持 DSO (Dynamic Shared Object) 的方式動態加入模塊,那麼你可以透過 apxs (APache eXtenSion tool) 將 mod_fastcgi 安裝成一個 DSO 模塊。

1. 下載 mod_fastcgi_2.2.2.tar.gz 並且解開:

$ gunzip -c mod_fastcgi_2.2.2.tar.gz | tar xvf -

2. 編譯 mod_fastcgi 模塊成 DSO:

$ cd mod_fastcgi_2.2.2

$ /usr/local/apache/sbin/apxs -o mod_fastcgi.so -c *.c

3. 安裝

$ /usr/local/apache/sbin/apxs -i -a -n fastcgi mod_fastcgi.so

2.2 加入使用 mod_fastcgi 的相關設定

為了讓 Apache 區分出那一些聯機請求屬於 FastCGI 來處理的,我們必需在阿帕契的設定檔內加入讓 Apache 可以辨別 FastCGI 的設定。

1. mod_fastcgi 模塊會向 Apache 登記一個 fastcgi-script 的處理類型 (handler type),我們可以設定所有以 fcg 以及 fpl (for perl) 為擴展名的程序都是符合 FastCGI 協議的應用程序:

AddHandler fastcgi-script .fcg .fpl

2. 接下來我們定義 /usr/local/www/fcgi-bin 這個目錄用來存放已經寫好的 FastCGI 程序:

ScriptAlias /fcgi-bin/ /usr/local/www/fcgi-bin/

3. 檢查設定檔文法是否正確:

$ /usr/local/apache/sbin/apachectl configtest

Syntax OK

4. 重新激活阿帕契伺服器,讓新設定生效:

$ /usr/local/apache/sbin/apachectl graceful

/usr/local/apache/bin/apachectl graceful: httpd gracefully restarted

Apache 1.3.4 版之後將原本的 httpd.conf 、srm.conf、access.conf 合併成一個檔案。所以你所使用的 Apache 如果是 1.3.4 版之後,請直接修改 httpd.conf3 這個檔,如果是 1.3.3 版之前,我建議把 mod_fastcgi 模塊相關設定加在 srm.conf 這個檔裡頭。實際的設定文件路徑和設定文件文件名可能依每個人的環境不同而有差異,請根據您自己的環境做適當的調整。

2.3 安裝 FastCGI 開發套件

1. 下載 fcgi-devkit-2.1.tar.gz 並且解開:

$ gunzip -c fcgi-devkit-2.1.tar.gz | tar xvf -

2. 編譯

$ cd fcgi-devkit-2.1

$ ./configure

$ make

3. 將 C 的標頭檔 (header file) 及函式庫 (library) 安裝至系統:

$ cp -rp include /usr/local/include/fastcgi

$ cp libfcgi/libfcgi.a /usr/local/lib

2.4 測試 FastCGI

在 fcgi-devkit 套件中內含一個簡單的 FastCGI 范常式序 - echo.c,我們用它來做測試系統是否安裝正確。直接把已經編譯好的 echo.fcg 複製到預設放置 FastCGI 程序的目錄下:

$ cd fcgi-devkit-2.1/example

$ cp echo.fcg /usr/local/www/fcgi-bin

現在趕快用瀏覽器連到 http://localhost/fcgi-bin/echo.fcg 看看,如果看到以下結果表示您大功告成啦:

FastCGIecho

Requestnumber1,ProcessID:1013

Nodatafromstandardinput.

Requestenvironment:

FCGI_ROLE=RESPONDER

DOCUMENT_ROOT=/usr/local/apache/htdocs

HTTP_ACCEPT=text/html,text/plain,application/applefile,application/x-metamai

l-patch,sun-deskset-message,mail-file,default,postscript-file,audio-file,

x-sun-attachment,text/enriched,text/richtext,application/andrew-inset,x-be2

,application/postscript,message/external-body,message/partial,application/p

gp,application/pgp,video/mpeg,video/*,image/*,audio/*,audio/mod,text/sgm

l,video/mpeg,image/jpeg,image/tiff,image/x-rgb,image/png,image/x-xbitmap,

image/x-xbm,image/gif,application/postscript,*/*;q=0.01

HTTP_ACCEPT_ENCODING=gzip,compress

HTTP_ACCEPT_LANGUAGE=en

HTTP_HOST=localhost

HTTP_NEGOTIATE=trans

HTTP_USER_AGENT=Lynx/2.8.1pre.9libwww-FM/2.14

PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/usr/sbin:/opt/kde/bin:/home/m

yhsu/bin:/usr/X11R6/bin:/usr/sbin:/opt/kde/bin:/usr/X11R6/bin:/usr/sbin:/opt/kd

e/bin

REMOTE_ADDR=127.0.0.1

REMOTE_PORT=1024

SCRIPT_FILENAME=/usr/local/www/fcgi-bin/echo.fcg

SERVER_ADMIN=myhsu@localhost.localdomain

SERVER_NAME=localhost.localdomain

SERVER_PORT=80

SERVER_SIGNATURE=

Apache/1.3.6Serveratlocalhost.localdomainPort80

SERVER_SOFTWARE=Apache/1.3.6(Unix)mod_fastcgi/2.2.2

UNIQUE_ID=N1ptln8AAAEAAAPdDRkGATEWAY_INTERFACE=CGI/1.1

SERVER_PROTOCOL=HTTP/1.0REQUEST_METHOD=GETQUERY_STRING=

REQUEST_URI=/fcgi-bin/echo.fcgSCRIPT_NAME=/fcgi-bin/echo.fcg

Initialenvironment:

請注意在以上程序所顯示的 Request number 和 Process ID 這兩個變數,當我們繼續重新載入這支程序時,Request number 會一直累加而 Process ID 的值都不會改變。這表示這支程序在第一次激活之後就一直執行著沒有結束,而且在每次聯機請求中所參照到的變數空間是相同的 (所以 Request number 會不斷加一)。

到此為止,我們已經成功建立起一個可供發展 FastCGI 應用程序的環境及執行 FastCGI 應用程序的網站執行平台。

2.5 安裝 FCGI 模塊 for Perl

如果要使用 Perl 來撰寫 FastCGI 的程序,必須安裝 FCGI.pm 這個模塊,安裝的方法如下:

1. 下載 FCGI-0.45.tar.gz 並且解開

$ gunzip -c FCGI-0.45.tar.gz | tar xvf -

2. 編譯及安裝

$ perl Makefile.PL

$ make

$ make install

3. 測試

$ cp echo.fpl /usr/local/www/fcgi-bin

$ lynx http://localhost/fcgi-bin/echo.fpl

如果順利的話,應該會看到如下的結果:

FastCGI echo (Perl)

Request number 1

No data from standard input.

Request environment:

DOCUMENT_ROOT=/usr/local/apache/htdocs

FCGI_ROLE=RESPONDER

GATEWAY_INTERFACE=CGI/1.1

HTTP_ACCEPT=text/html, text/plain, application/applefile, application/x-metamai

l-patch, sun-deskset-message, mail-file, default, postscript-file, audio-file,

x-sun-attachment, text/enriched, text/richtext, application/andrew-inset, x-be2

, application/postscript, message/external-body, message/partial, application/p

gp, application/pgp, video/mpeg, video/*, image/*, audio/*, audio/mod, text/sgm

l, video/mpeg, image/jpeg, image/tiff, image/x-rgb, image/png, image/x-xbitmap,

image/x-xbm, image/gif, application/postscript, */*;q=0.01

HTTP_ACCEPT_ENCODING=gzip, compress

HTTP_ACCEPT_LANGUAGE=en

HTTP_HOST=localhost

HTTP_NEGOTIATE=trans

HTTP_USER_AGENT=Lynx/2.8.1pre.9 libwww-FM/2.14

PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/usr/sbin:/opt/kde/bin:/home/m

yhsu/bin:/usr/X11R6/bin:/usr/sbin:/opt/kde/bin:/usr/X11R6/bin:/usr/sbin:/opt/kd

e/bin

QUERY_STRING=

REMOTE_ADDR=127.0.0.1

REMOTE_PORT=1427

REQUEST_METHOD=GET

REQUEST_URI=/fcgi-bin/echo.fpl

SCRIPT_FILENAME=/usr/local/www/fcgi-bin/echo.fpl

SCRIPT_NAME=/fcgi-bin/echo.fpl

SERVER_ADMIN=myhsu@localhost.localdomain

SERVER_NAME=localhost.localdomain

SERVER_PORT=80

SERVER_PROTOCOL=HTTP/1.0

SERVER_SIGNATURE=


Apache/1.3.6 Server at localhost.localdomain Port 80

SERVER_SOFTWARE=Apache/1.3.6 (Unix) mod_fastcgi/2.2.2

UNIQUE_ID=N1VIbX8AAAEAAAQnKKo

More on its way ... wait a few seconds

Initial environment:

同樣的,如果持續連結 http://localhost/fcgi-bin/echo.fpl 可以看到 Request Number 不斷增加,表示 echo.fpl 已經被激活而且持續執行著。

至此,一個可供執行 FastCGI 程序的網站伺服器以及撰寫 FastCGI 程序的開發環境已經建置完成,接下來就只等著新的程序放上去??

----------------------------------------------------------------------------

3. 撰寫 FastCGI 應用程序

撰寫全新的 FastCGI 應用程序,或是將舊有的 CGI 程序改寫成 FastCGI 應用程序都非常的簡單,只要使用 fcgi-devkit 所附的 fcgi_stdio 函式庫即可。

基本上,fcgi_stdio 函式庫已被設計成讓開發人員撰寫 FastCGI 應用程序就像寫一般 CGI 程序一樣,同時做到程序保有和 CGI 最大的兼容度,又能享受到 FastCGI 所帶來的優點。使用 fcgi_stdio 函式庫的另一項好處是,編譯出來的執行檔可同時以 CGI 以及 FastCGI 的方式執行。

3.1 程序架構

對 CGI 程序而言,其生命期就是從一個聯機請求 (request) 開始到聯機結束。而 FastCGI 程序就像是比較『長命』的 CGI 程序,其生命期橫跨不同的聯機請求,可從 Web 伺服器激活開始到 Web 伺服器停止。

由於 FastCGI 程序長命的特性,它和一般 CGI 程序主要的差異就在於把初始化 (initialization) 的部份和處理聯機請求的部份區分開來,程序的架構如下所示:

Initialization Code

Start of response loop

body of response loop

End of response loop

Initialization Code 的部份只會在 FastCGI 程序激活時執行一次,程序初始化的部份像是內存的配置,建立和資料庫的聯機等都可以寫在這裡。

而 Start of response loop 到 End of response loop 之間的程序在每次發生聯機請求時就會執行,這部份的程序才是真正處理每次聯機請求要做的事情。例如接受使用者輸入的參數,從資料庫取出資料,執行運算動作,回復結果給使用者等等。

一個簡單的 FastCGI 程序如下 (tiny-fcgi.c)

#include "fcgi_stdio.h"

#include

void main(void)

{

/* Initialization Code */

int count = 0;

/* Start of response loop */

while(FCGI_Accept() >= 0) {

/* body of response loop */

printf("Content-type: text/html\r\n"

"\r\n"

""

"

FastCGI Hello! (C, fcgi_stdio library)

"

"Request number %d running on host %s "

"Process ID: %d\n",

++count, getenv("SERVER_NAME"), getpid());

}

/* End of response loop */

}

3.2 引入 fcgi_stdio.h 標頭檔

開始撰寫一個新的 FastCGI 應用程序時,必須引入 fcgi_stdio.h 這個標頭檔:

#include ``fcgi_stdio.h\

如果要改寫舊有的 CGI 程序成 FastCGI 程序,請把原本引入 stdio.h 的部份換掉:

#ifndef FASTCGI

#include

#else

#include ``fcgi_stdio.h\

#endif

上面的寫法是利用 C 的前置編譯器做處理,如果在編譯時加入 -DFASTCGI 的參數則引入 fcgi_stdio.h ,反之則否。假設我們把 fcgi_stdio.h 標頭文件放在 /usr/local/include/fastcgi 這個目錄下的話,為了在編譯時引入 fcgi_stdio.h 標頭檔,請加入 -I/usr/local/include/fastcgi 的參數。

$ cc -I/usr/local/include/fastcgi -c program.c

注意

如果你的程序使用到其它的函式庫,請務必檢查這些函式庫是否有使用到 stdio.h 這個檔,如果有的話必須一併換成 fcgi_stdio.h。舉例來說,假如你的程序用到 GD 函式庫4來產生 GIF 圖形,請記得把 gd.h 中引入 stdio.h 的部份換成 fcgi_stdio.h,否則遇到 IO 的函式時會發生錯誤 (Core Dump)。

3.3 FastCGI 處理循環

在 FastCGI 程序中負責處理聯機請求的程序,必須用一個 while 循環包起來,利用 fcgi_stdio 函式庫中的 FCGI_Accept() 函式庫來判斷是否有新的聯機請求發生。

FastCGI 程序被激活后,首先進行初始化的動作,如變數宣告、內存配置、或是與資料庫建立聯機等。執行到 FCGI_Accept() 時程序就會暫停,等到當某一個聯機請求發生時,FCGI_Accept() 會傳回大於零的值,程序即刻進入循環的主體,可能是執行 SQL 指令,運算以及產生 HTML 的輸出。處理完后又回到 FCGI_Accept() 循環的開始,等待下一個聯機請求。

由此可見, FastCGI 激活之後,省去原本 CGI 程序中 fork,內存配置,建立資料庫聯機等步驟,處理每個聯機請求的時間幾乎等於 FCGI_Accept() 這個循環中運算所需的時間。如果妥善將每次聯機請求時一樣的程序代碼從 FCGI_Accept() 循環抽出來,只保留每次會產生不同結果的部份,則程序處理每次聯機請求的時間可以更短,整個網站的效率也可以大幅的提升。

了解 FastCGI 程序的基本架構后,可以發現一般的 CGI 程序,只要加入 FCGI_Accept() 這個 while 循環后,大概就可以變成 FastCGI 的程序了。

3.4 煉結 libfcgi.a 函式庫

FastCGI 程序要煉結到 libfcgi.a 函式庫才可正確產生出執行檔,假設 libfcgi.a 的位置在 /usr/local/lib 這個目錄下,我們在編譯 (煉結時) 要加入 -L/usr/local/lib -lfcgi 的參數。


$ cc -o program.fcg program.o -L/usr/local/lib -lfcgi

3.5 撰寫 FastCGI 程序的注意事項

由於 FastCGI 程序被激活后就常駐在內存之中,對每個 FastCGI 的執行行程 (running process) 而言,每次聯機請求共享的變數空間是相同的,因些選寫 FastCGI 程序要特別留意一些注意事項。

1. 記住,對每個 FastCGI 程序而言,只有 FCGI_Accept() 循環內的程序才是真正處理每次聯機請求的主體,假設在程序中要讀取和 CGI 有關的環境變數,或是使用者透過窗體 (form) 所輸入的資料,必須在 FCGI_Accept() 循環內才處理。

2. 對每次聯機請求會改變的變數,別忘了在處理新的聯機請求前把變數初始化 (除非是有特殊用途) ,以避免變數值在不同的聯機請求之間互相影響。

3. 在 FCGI_Accept() 循環中配置 (allocate) 的內存別忘了釋放 (free)。在一般的 CGI 程序中, Memory Leak 不算是太大的問題,因為每處理完一次聯機請求,所有用到的內存隨著 CGI 程序的結束也被釋放。但是 FastCGI 程序會一直常駐在內存中,如果在 FCGI_Accept() 中有配置額外的內存,在循環結束前沒有釋放掉,則每處理一次聯機請求就吃掉一些系統的內存,多跑幾次下來,系統資源大概就被耗光了。Memory leak 的問題是 FastCGI 最常見的錯誤,要特別小心處理。

4. FastCGI 程序和其所要引用的子程序中,用到 stdio.h 函式的部份,記得要改成 fcgi_stdio.h 。否則當程序在遇到 IO 的動作時就會發生 Core Dump 的情況。

5. 在 FCGI_Accept() 循環中使用 FCGI_Finish() 函式以取代 exit() 函式。原本 CGI 程序中發生錯誤時,可能直接呼叫 exit() 函式結束 CGI 行程,FastCGI 程序也可以如此,因為 mod_fastcgi 模塊在 FastCGI 程序發生錯誤而意外結束時,會自動再激活另一個 FastCGI 的行程。但比較好的作法是呼叫 fcgi_stdio.h 中的 FCGI_Finish() 函式,呼叫 FCGI_Finish() 函式會跳出目前程序正在運算中的循環,回到 FCGI_Accept() 等待下一個聯機請求。如此可以省去一些網站伺服器激活新的 FastCGI 行程的負擔。

----------------------------------------------------------------------------

4. FastCGI 有多快?

看完安裝 FastCGI 的阿帕契模塊,以及無聊的程序設計注意事項后,我們來看看一些可以讓人振奮精神的數據,效能比較 (bench mark) 總是計算機玩家的最愛 :-)

4.1 評比工具 - ApacheBench

在阿帕契伺服器的套件中,有一個叫做 ab (ApacheBench) 的工具。ApacheBench 主要是用來測試阿帕契伺服器執行效率用的,我們就以 ApacheBench 做為 CGI vs. FastCGI 的評比工具。

ApacheBench 可以針對某個特定的 URL 模擬出連續的聯機請求,同時還可以模擬出同時間點數個相同的聯機請求,因此利用 ApacheBench 可幫助我們在網站開發期間模擬實際上線可能的情況,利用模擬出來的數據做為調整伺服器設定或程序的依據。 ApacheBench 的用法如下:

Usage: /usr/local/apache/bin/ab [options] [http://]hostname[:port]/path

Options are:

-n requests Number of requests to perform

-c concurrency Number of multiple requests to make

-t timelimit Seconds to max. wait for responses

-p postfile File containg data to POST

-T content-type Content-type header for POSTing

-v verbosity How much troubleshooting info to print

-w Print out results in HTML tables

-x attributes String to insert as table attributes

-y attributes String to insert as tr attributes

-z attributes String to insert as td or th attributes

-V Print version number and exit

-k Use HTTP KeepAlive feature

-h Display usage information (this message)

假設我們要對 echo.fcg 做測試,模擬 1000 次的聯機請求,而且同一時間有 20 個并行的 (concurrent) 聯機請求的情況,只要在命令列模式下執行

$ ab -n 1000 -c 20 http://localhost/fcgi-bin/echo.fcg

稍等一會,ApacheBench 會把結果秀出來,

This is ApacheBench, Version 1.3

Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/

Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/

Benchmarking localhost (be patient)... Server Software: Apache/1.3.6

Server Hostname: localhost

Server Port: 80

Document Path: /fcgi-bin/echo.fcg

Document Length: 995 bytes

Concurrency Level: 20

Time taken for tests: 6.859 seconds

Complete requests: 1000

Failed requests: 0

Total transferred: 1142000 bytes

HTML transferred: 995000 bytes

Requests per second: 145.79

Transfer rate: 166.50 kb/s received

Connnection Times (ms)

min avg max

Connect: 0 4 61

Processing: 62 128 771

Total: 62 132 832

以上結果指出,在同時間 20 個聯機請求 (Concurrency Level) 的情況下,完成 1000 次的聯機請求,共花了 6.859 秒 (Time taken for tests),因此這個程序每秒平均可以處理 (Requests per second) 145.79 個聯機請求。

在接下來的評比測試中,我們就以每秒可以處理的聯機請求數目來做為效能評比的依據。

4.2 CGI vs. FastCGI

前面提過利用 fcgi_stdio.h 函式庫編譯出來的 FastCGI 程序也兼容於 CGI 模式,因此我們只要把 fcgi-devkit-2.1 套件附的范常式序 echo.fcg 複製到 /cgi-bin 目錄下,並且把文件名改成 echo.cgi,這支范常式序就可分別以 CGI 模式和 FastCGI 模式來執行,並且做比較。

首先分別對 CGI模式執行的 http://localhost/cgi-bin/echo.cgi 以及 FastCGI 模式的 http://localhost/fcgi-bin/echo.fcg 連續送出 10, 100, 1000, 10000 次的聯機請求,得到的平均每秒可處理的請求 (Requests per second) 結果為:

聯機數目 10 100 1000 10000

CGI 52.63 53.08 52.24 51.49

FastCGI (static mode) 204.08 224.22 146.78 207.14

接下來再分別以 Concurrency Level 為 10, 50, 100 的情況下做測試,得到 Requests per second 結果為:

Concurrency 聯機數目 10 100 1000 10000

10 CGI 38.31 46.55 53.61 55.09

10 FastCGI 185.19 208.33 162.63 177.14

50 CGI 27.25 33.16 50.72 53.99

50 FastCGI 92.59 176.37 196.58 196.88

100 CGI 17.92 24.84 48.14 52.84

100 FastCGI 86.21 187.27 195.54 193.17

由上述數據看來,對同一支程序 (echo.c) 而言,使用 FastCGI 模式來執行,速度提升了 3-4 倍。可見得 FastCGI 對網站程序的效能提升上具有相當大的助益,尤其當使用量很大時,其效益更加明顯。

以上的測試條件並不是十分嚴謹,主要在讓你了解使用 FastCGI 之後對於效能及速度上一個概括的比較,也提供一個可供網站開發者可以評量的依據。

實際上,對一個初始化動作複雜,例如要先和資料庫建立聯機,或是配置內存,做變數初始化的程序來說,使用 FastCGI 可以比原先 CGI 在效能上增加更多,速度更快。而對一個有資料庫可聯機數目限制的系統來說,使用 FastCGI 就好象一個 Application Server 一樣,不用擔心 CGI 一次 fork 太多,超過聯機數目上限 (FastCGI 可以設定一次跑幾支)。

4.3 找出 Memory Leak

善用 ApacheBench 這個工具,還可以幫助網站程序發展人員找出在 FastCGI 程序中隱藏的 Memory Leak 臭蟲。

每一支 FastCGI 程序在處理完一個聯機請求后的狀況都應該相同,我們利用 ApacheBench 對欲測試的程序送出上百次或上千次的聯機請求以模擬實際上線的狀況,如果發現程序佔用的內存愈來愈多而且不會減少的話,表示程序有 Memory Leak 的問題。

雖然對 FastCGI 程序而言,當它一直不斷吃掉系統資源到資料耗盡後會自動退出 (core dump 或是 exit) ,釋放所佔用的資源然後再重新激活,但這將會影響系統其它功能的正常運作,所以撰寫 FastCGI 程序一定特別小心。

----------------------------------------------------------------------------

5. 參考

如果你想更進一步了解 FastCGI ,在 FastCGI 的首頁上可以找到相關的所有資料,包含 FastCGI 的發展規格,軟體下載,說明文件等。FastCGI 首頁的網址是 http://www.fastcgi.com/

FastCGI 也有一個郵件討論區 (mailing list),你可以寄 email 至

fastcgi-developers-request@idle.com

在郵件的主旨 (Subject) 處填上 subscribe 即可。但是在發問之前,請先把說明文件或安裝文件看清楚,或是到郵件討論區的檔案 (mail archive) 中先找找問題是否已經有人問過而且有人回答過了,以免浪費重複的網路資源。如果你發現了 mod_fastcgi 模塊或 fcgi-devkit 發展套件的錯誤或嚴重的問題,FastCGI 的發展小組會很熱心的幫你想辦法解決。

FastCGI 還有許多不同的用法,以及特別的應用,就有待您親自去探索了。希望本文能幫助有心使用 FastCGI 的朋友可以順利地安裝 FastCGI 相關模塊和程序,讓你的網站都能全速前進。

許明彥 (myhsu@cyberdude.com)




[火星人 via ] 實戰FastCGI已經有172次圍觀

http://www.coctec.com/docs/program/show-post-72451.html