實戰 FastCGI(轉)三 - 中國WEB開發者網絡 (http://www.webasp.net) -- 技術教程 (http://www.webasp.net/article/) --- 實戰 FastCGI(轉)三 (http://www.webasp.net/article/8/7732.htm) |
| -- 作者:未知 -- 發佈日期: 2003-09-13 |
| 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 <stdlib.h> 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" "<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> " "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 <stdio.h> #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 行程的負擔。 |
| webasp.net |