Nginx + CGI/FastCGI + C/Cpp(编不过去,不搞了。。。)(Common Gateway Interface)
Nginx + CGI/FastCGI + C/Cpp
文章目錄
- 1.CGI
- 1.1.環境變量
- 1.2.標準輸入
- 總結:CGI使外部程序與Web服務器之間交互成為可能。CGI程式運行在獨立的進程中,并對每個Web請求建立一個進程,這種方法非常容易實現,但效率很差,難以擴展。面對大量請求,進程的大量建立和消亡使操作系統性能大大下降。此外,由于地址空間無法共享,也限制了資源重用。
- 2.FastCGI
- 3.nginx cgi/fastcgi
- 3.1. nginx + fastcgi
- 3.1.1. spawn-fcgi
- 3.1.2. 編寫fastcgi應用程序
- 3.1.3. nginx fastcgi配置
- 3.2. nginx + cgi
- 3.2.1. fastcgi-wrapper
- 3.2.2. nginx fcgiwrap配置
接著上篇《Nginx安裝與使用》,本篇介紹CGI/FASTCGI的原理、及如何使用C/C++編寫簡單的CGI/FastCGI,最后將CGI/FASTCGI部署到nginx。內容大綱如下:
1.CGI
通用網關接口(Common Gateway Interface/CGI)描述了客戶端和服務器程序之間傳輸數據的一種標準,可以讓一個客戶端,從網頁瀏覽器向執行在網絡服務器上的程序請求數據。CGI 獨立于任何語言的,CGI 程序可以用任何腳本語言或者是完全獨立編程語言實現,只要這個語言可以在這個系統上運行。Unix shell script, Python, Ruby, PHP, perl, Tcl, C/C++, 和 Visual Basic 都可以用來編寫 CGI 程序。(http://www.dwz.cn/yFFgQ)
最初,CGI 是在 1993 年由美國國家超級電腦應用中心(NCSA)為 NCSA HTTPd Web 服務器開發的。這個 Web 服務器使用了 UNIX shell 環境變量 來保存從 Web 服務器傳遞出去的參數,然后生成一個運行 CGI 的獨立的進程。cgi的處理流程如下圖所示:
-
step1. web 服務器收到客戶端(瀏覽器)的請求Http Request,啟動CGI程序,并通過環境變量、標準輸入傳遞數據
-
step2. cgi進程啟動解析器、加載配置(如業務相關配置)、連接其它服務器(如數據庫服務器)、邏輯處理等
-
step3. cgi進程將處理結果通過標準輸出、標準錯誤,傳遞給web 服務器
-
step4. web 服務器收到cgi返回的結果,構建Http Response返回給客戶端,并殺死cgi進程
web服務器與cgi通過環境變量、標準輸入、標準輸出、標準錯誤互相傳遞數據。
1.1.環境變量
GET請求,它將數據打包放置在環境變量QUERY_STRING中,CGI從環境變量QUERY_STRING中獲取數據。常見的環境變量如下表所示:
環境變數——內容
AUTH_TYPE存取認證類型。CONTENT_LENGTH由標準輸入傳遞給CGI程序的數據長度,以bytes或字元數來計算。CONTENT_TYPE請求的MIME類型。GATEWAY_INTERFACE服務器的CGI版本編號。HTTP_ACCEPT瀏覽器能直接接收的Content-types, 可以有HTTP Accept header定義.HTTP_USER_AGENT遞交表單的瀏覽器的名稱、版本 和其他平臺性的附加信息。HTTP_REFERER遞交表單的文本的 URL,不是所有的瀏覽器都發出這個信息,不要依賴它PATH_INFO傳遞給cgi程式的路徑信息。QUERY_STRING傳遞給CGI程式的請求參數,也就是用"?"隔開,添加在URL后面的字串。REMOTE_ADDRclient端的host名稱。REMOTE_HOSTclient端的IP位址。REMOTE_USERclient端送出來的使用者名稱。REMOTE_METHODclient端發出請求的方法(如get、post)。SCRIPT_NAMECGI程式所在的虛擬路徑,如/cgi-bin/echo。SERVER_NAMEserver的host名稱或IP地址。SERVER_PORT收到request的server端口。SERVER_PROTOCOL所使用的通訊協定和版本編號。SERVER_SOFTWAREserver程序的名稱和版本。1.2.標準輸入
環境變量的大小是有一定的限制的,當需要傳送的數據量大時,儲存環境變量的空間可能會不足,造成數據接收不完全,甚至無法執行CGI程序。因此后來又發展出另外一種方法:POST,也就是利用I/O重新導向的技巧,讓CGI程序可以由STDIN和STDOUT直接跟瀏覽器溝通。
當我們指定用這種方法傳遞請求的數據時,web 服務器收到數據后會先放在一塊輸入緩沖區中,并且將數據的大小記錄在CONTENT_LENGTH這個環境變數,然后調用CGI程式并將CGI程序的STDIN指向這塊緩沖區,于是我們就可以很順利的通過STDIN和環境變數CONTENT_LENGTH得到所有的資料,再沒有資料大小的限制了。
總結:CGI使外部程序與Web服務器之間交互成為可能。CGI程式運行在獨立的進程中,并對每個Web請求建立一個進程,這種方法非常容易實現,但效率很差,難以擴展。面對大量請求,進程的大量建立和消亡使操作系統性能大大下降。此外,由于地址空間無法共享,也限制了資源重用。
2.FastCGI
快速通用網關接口(Fast Common Gateway Interface/FastCGI)是通用網關接口(CGI)的改進,描述了客戶端和服務器程序之間傳輸數據的一種標準。FastCGI致力于減少Web服務器與CGI程式之間互動的開銷,從而使服務器可以同時處理更多的Web請求。與為每個請求創建一個新的進程不同,FastCGI使用持續的進程來處理一連串的請求。這些進程由FastCGI進程管理器管理,而不是web服務器。(http://www.dwz.cn/yFMap)
當進來一個請求時,Web 服務器把環境變量和這個頁面請求通過一個unix domain socket(都位于同一物理服務器)或者一個IP Socket(FastCGI部署在其它物理服務器)(就是我們的效果)傳遞給FastCGI進程。
-
step1. Web 服務器啟動時載入初始化FastCGI執行環境 。 例如IIS ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi
-
step2. FastCGI進程管理器自身初始化,啟動多個CGI解釋器進程并等待來自Web 服務器的連接。 啟動FastCGI進程時,可以配置以ip和UNIX 域socket兩種方式啟動。
UNIX 域套接字
1、UNIX 域套接字以 UNIX 路徑命名。例如,可以將套接字命名為 /tmp/foo。UNIX 域套接字只在一臺主機上的進程之間通信。UNIX 域中的套接字不會被視為網絡協議的一部分,因為它們只能用于在一臺主機上的進程之間通信。
2、套接字類型定義對于用戶可見的通信屬性。Internet 域套接字提供對 TCP/IP 傳輸協議的訪問。Internet 域由值 AF_INET 標識。套接字僅與同一域中的套接字交換數據。
參考文章:UNIX 域套接字
-
step3. 當客戶端請求到達Web 服務器時, Web 服務器將請求采用socket方式轉發到 FastCGI主進程,FastCGI主進程選擇并連接到一個CGI解釋器。Web 服務器將CGI環境變量和標準輸入發送到FastCGI子進程。
-
step4. FastCGI子進程完成處理后將標準輸出和錯誤信息從同一socket連接返回Web 服務器。當FastCGI子進程關閉連接時,請求便處理完成。
-
step5. FastCGI子進程接著等待并處理來自Web 服務器的下一個連接。
由于 FastCGI 程序并不需要不斷的產生新進程,可以大大降低服務器的壓力并且產生較高的應用效率。它的速度效率最少要比CGI 技術提高 5 倍以上。它還支持分布式的部署, 即 FastCGI 程序可以在web 服務器以外的主機上執行。(我們現在不就這么做的嗎?Web服務器是單獨主機,FastCGI燒在瑞芯微芯片攝像頭模組里)
總結:CGI 就是所謂的短生存期應用程序,FastCGI 就是所謂的長生存期應用程序。FastCGI像是一個常駐(long-live)型的CGI,它可以一直執行著,不會每次都要花費時間去fork一次(這是CGI最為人詬病的fork-and-execute 模式)。
3.nginx cgi/fastcgi
nginx 不能像apache那樣直接執行外部可執行程序,但nginx可以作為代理服務器,將請求轉發給后端服務器,這也是nginx的主要作用之一。其中nginx就支持FastCGI代理,接收客戶端的請求,然后將請求轉發給后端fastcgi進程。下面介紹如何使用C/C++編寫cgi/fastcgi,并部署到nginx中。
3.1. nginx + fastcgi
通過前面的介紹知道,fastcgi進程由FastCGI進程管理器管理,而不是nginx。這樣就需要一個FastCGI管理,管理我們編寫fastcgi程序。本文使用spawn-fcgi作為FastCGI進程管理器。
3.1.1. spawn-fcgi
spawn-fcgi是一個通用的FastCGI進程管理器,簡單小巧,原先是屬于lighttpd的一部分,后來由于使用比較廣泛,所以就遷移出來作為獨立項目了。spawn-fcgi使用pre-fork 模型,功能主要是打開監聽端口,綁定地址,然后fork-and-exec創建我們編寫的fastcgi應用程序進程,退出完成工作。fastcgi應用程序初始化,然后進入死循環偵聽socket的連接請求。
安裝spawn-fcgi:
-
獲取spawn-fcgi編譯安裝包,在http://redmine.lighttpd.net/projects/spawn-fcgi/wiki上可以獲取當前最新的版本。
-
解壓縮spawn-fcgi-x.x.x.tar.gz包。
-
進入解壓縮目錄,執行./configure。
-
make & make install
(如果編譯報錯先chmod 777 * -R試試)
如果遇到以下錯誤:“ ./autogen.sh: x: autoreconf: not found”,因為沒有安裝automake 工具,ubuntu用下面的命令安裝好就可以了:sudo apt-get install autoconf automake libtool 。
spawn-fcgi的幫助信息可以通過man spawn-fcgi或spawn-fcgi -h獲得,下面是部分常用spawn-fcgi參數信息:
-f <fcgiapp> 指定調用FastCGI的進程的執行程序位置-a <addr> 綁定到地址addr。-p <port> 綁定到端口port。-s <path> 綁定到unix domain socket-C <childs> 指定產生的FastCGI的進程數,默認為5。(僅用于PHP)-P <path> 指定產生的進程的PID文件路徑。-F <childs> 指定產生的FastCGI的進程數(C的CGI用這個)-u和-g FastCGI使用什么身份(-u 用戶 -g 用戶組)運行,CentOS下可以使用apache用戶,其他的根據情況配置,如nobody、www-data等。3.1.2. 編寫fastcgi應用程序
使用C/C++編寫fastcgi應用程序,可以使用FastCGI軟件開發套件或者其它開發框架,如fastcgi++。
本文使用FastCGI軟件開發套件——fcgi(http://www.fastcgi.com/drupal/node/6?q=node/21),通過此套件可以輕松編寫fastcgi應用程序,
安裝fcgi:
- 獲取fcgi編譯安裝包,在http://www.fastcgi.com/drupal/node/5上可以獲取當前最新的版本。(我試了試網站訪問不上。。。訪問這個?https://fastcgi-archives.github.io/)
https://github.com/FastCGI-Archives
http://web.archive.org/web/20160316080434/http://www.fastcgi.com/drupal/node/5
下不到,蛋疼!
不知道這個能用不?https://github.com/FastCGI-Archives/FastCGI.com/tree/master/original_snapshot
-
解壓縮fcgi-x.x.x.tar.gz包。
-
進入解壓縮目錄,執行./configure。
-
make & make install
如果編譯提示以下錯誤:
fcgio.cpp: In destructor 'virtual fcgi_streambuf::~fcgi_streambuf()':fcgio.cpp:50: error: 'EOF' was not declared in this scopefcgio.cpp: In member function 'virtual int fcgi_streambuf::overflow(int)':fcgio.cpp:70: error: 'EOF' was not declared in this scopefcgio.cpp:75: error: 'EOF' was not declared in this scopefcgio.cpp: In member function 'virtual int fcgi_streambuf::sync()':fcgio.cpp:86: error: 'EOF' was not declared in this scopefcgio.cpp:87: error: 'EOF' was not declared in this scopefcgio.cpp: In member function 'virtual int fcgi_streambuf::underflow()':fcgio.cpp:113: error: 'EOF' was not declared in this scopemake[2]: *** [fcgio.lo] Error 1make[2]: Leaving directory `/root/downloads/fcgi-2.4.1-SNAP-0910052249/libfcgi'make[1]: *** [all-recursive] Error 1make[1]: Leaving directory `/root/downloads/fcgi-2.4.1-SNAP-0910052249' make: *** [all] Error 2解決辦法:在/include/fcgio.h文件中加上 #include ,然后再編譯安裝就通過了。(嘗試加進去了,還報錯)
如果提示找不到動態庫,請在LD_LIBRARY_PATH或/etc/ld.so.conf中添加fcgi的安裝路徑,如/usr/local/lib,并執行ldconfig更新一下。
ar:我先把fcgi文件包拷貝到這個路徑下
[root@ubuntu /home/arnold/Arnold_test]1# mv 20211109_fcgi/ /usr/local/lib/ar:然后添加環境變量
方法三:這個沒有修改LD_LIBRARY_PATH但是效果是一樣的實現動態庫的查找,
參考文章:Linux環境變量設置方法總結 PATH、LD_LIBRARY_PATH
編不過去,不搞了!
#include "fcgi_stdio.h"#include <stdlib.h>int main(void){int count = 0;while (FCGI_Accept() >= 0)printf("Content-type: text/html\r\n""\r\n""<title>FastCGI Hello!</title>""<h1>FastCGI Hello!</h1>""Request number %d running on host <i>%s</i>\n",++count, getenv("SERVER_NAME"));return 0;}編譯g++ main.cpp -o demo –lfcgi,并將demo部署到/opt/nginx-1.7.7/cgi-bin/目錄
通過spawn-fcgi啟動c/c++編寫好的fastcgi程序:/opt/nginx-1.7.7/sbin/spawn-fcgi -a 127.0.0.1 -p 8081 -f /opt/nginx-1.7.7/cgi-bin/demo
3.1.3. nginx fastcgi配置
關于nginx的幾個配置文件解析,可以參閱《Nginx安裝與使用》http://www.cnblogs.com/skynet/p/4146083.html,在上篇的nginx.conf基礎上增加下面的fastcgi配置。
這樣nginx收到http://localhost/demo.cgi請求時,會匹配到location = /demo.cgi塊,將請求傳到后端的fastcgi應用程序處理。如下如所示:(注意其中number為80,是因為我請求了80次)
3.2. nginx + cgi
nginx 不能直接執行外部可執行程序,并且cgi是接收到請求時才會啟動cgi進程,不像fastcgi會在一開就啟動好,這樣nginx天生是不支持 cgi 的。nginx 雖然不支持cgi,但它支持 fastCGI。所以,我們可以考慮使用fastcgi包裝來支持 cgi。原理大致如下圖所示:pre-fork幾個通用的代理fastcgi程序——fastcgi-wrapper,fastcgi-wrapper啟動執行cgi然后將cgi的執行結果返回給nginx(fork-and-exec)。
明白原理之后,編寫一個fastcgi-warpper也比較簡單。網上流傳比較多的一個解決方案是,來自nginx wiki(http://wiki.nginx.org/SimpleCGI)上的使用perl的fastcgi包裝腳本cgiwrap-fcgi.pl。但我對perl不是很感冒,下面給出一個C/C++寫的fastcgi-wrapper。
3.2.1. fastcgi-wrapper
其實編寫C/C++的fastcgi-wrapper,就是寫一個C/C++的fastcgi,步驟和原理跟前面的小節(nginx+fastcgi)一樣。github上已經有人開源了,C寫的fastcgi-wrapper:
https://github.com/gnosek/fcgiwrap
安裝fcgiwrap:
- 下載(https://github.com/gnosek/fcgiwrap.git)
這依賴也太多了,我上面就是被這依賴搞暈的,我直接放棄。。。
-
解壓縮fcgiwrap,進入解壓目錄
-
autoreconf -i
-
./configure
-
make && make install
啟動fastcgi-wrapper:/opt/nginx-1.7.7/sbin/spawn-fcgi -f /usr/local/sbin/fcgiwrap -p 8081
3.2.2. nginx fcgiwrap配置
在nginx.conf中增加下面的loaction配置塊,這樣所有的xxx.cgi請求都會走到fcgiwrap,然后fcgiwrap會執行cgi-bin目錄下的cgi程序。
總結
以上是生活随笔為你收集整理的Nginx + CGI/FastCGI + C/Cpp(编不过去,不搞了。。。)(Common Gateway Interface)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WEB服务器和HTTP服务器和应用服务器
- 下一篇: windows10如何解除文件路径最大长