Nginx + CGI/FastCGI + C/Cpp
http://www.cnblogs.com/skynet/p/4173450.html
Nginx + CGI/FastCGI + C/Cpp
2014-12-19 11:05 by 吳秦,?19794?閱讀,?6?評(píng)論,?收藏,?編輯
接著上篇《Nginx安裝與使用》,本篇介紹CGI/FASTCGI的原理、及如何使用C/C++編寫(xiě)簡(jiǎn)單的CGI/FastCGI,最后將CGI/FASTCGI部署到nginx。內(nèi)容大綱如下:
1.?????CGI
1.1.?????環(huán)境變量
1.2.?????標(biāo)準(zhǔn)輸入
2.?????FastCGI
3. nginx cgi/fastcgi
3.1. nginx + fastcgi
3.1.1. spawn-fcgi
3.1.2.?編寫(xiě)fastcgi應(yīng)用程序
3.1.3. nginx fastcgi配置
3.2. nginx + cgi
3.2.1 fastcgi-wrapper
3.2.2. nginx?fcgiwrap配置
3.2.3.?編寫(xiě)cgi應(yīng)用程序
參考鏈接
?
1.CGI
通用網(wǎng)關(guān)接口(Common?Gateway?Interface/CGI)描述了客戶端和服務(wù)器程序之間傳輸數(shù)據(jù)的一種標(biāo)準(zhǔn),可以讓一個(gè)客戶端,從網(wǎng)頁(yè)瀏覽器向執(zhí)行在網(wǎng)絡(luò)服務(wù)器上的程序請(qǐng)求數(shù)據(jù)。CGI?獨(dú)立于任何語(yǔ)言的,CGI?程序可以用任何腳本語(yǔ)言或者是完全獨(dú)立編程語(yǔ)言實(shí)現(xiàn),只要這個(gè)語(yǔ)言可以在這個(gè)系統(tǒng)上運(yùn)行。Unix shell?script,?Python,?Ruby,?PHP, perl,?Tcl,?C/C++,和?Visual Basic?都可以用來(lái)編寫(xiě)?CGI?程序。(http://www.dwz.cn/yFFgQ)
最初,CGI?是在?1993?年由美國(guó)國(guó)家超級(jí)電腦應(yīng)用中心(NCSA)為?NCSA HTTPd?Web?服務(wù)器開(kāi)發(fā)的。這個(gè)?Web?服務(wù)器使用了?UNIX?shell?環(huán)境變量?來(lái)保存從?Web?服務(wù)器傳遞出去的參數(shù),然后生成一個(gè)運(yùn)行?CGI?的獨(dú)立的進(jìn)程。cgi的處理流程如下圖所示:
?
l???step1.?web?服務(wù)器收到客戶端(瀏覽器)的請(qǐng)求Http Request,啟動(dòng)CGI程序,并通過(guò)環(huán)境變量、標(biāo)準(zhǔn)輸入傳遞數(shù)據(jù)
l???step2. cgi進(jìn)程啟動(dòng)解析器、加載配置(如業(yè)務(wù)相關(guān)配置)、連接其它服務(wù)器(如數(shù)據(jù)庫(kù)服務(wù)器)、邏輯處理等
l???step3. cgi程將處理結(jié)果通過(guò)標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯(cuò)誤,傳遞給web?服務(wù)器
l???step4. web?服務(wù)器收到cgi返回的結(jié)果,構(gòu)建Http Response返回給客戶端,并殺死cgi進(jìn)程
web服務(wù)器與cgi通過(guò)環(huán)境變量、標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯(cuò)誤互相傳遞數(shù)據(jù)。
1.1.環(huán)境變量
GET請(qǐng)求,它將數(shù)據(jù)打包放置在環(huán)境變量QUERY_STRING中,CGI從環(huán)境變量QUERY_STRING中獲取數(shù)據(jù)。常見(jiàn)的環(huán)境變量如下表所示:
| 環(huán)境變數(shù) | 內(nèi)容 |
| AUTH_TYPE | 存取認(rèn)證類型。 |
| CONTENT_LENGTH | 由標(biāo)準(zhǔn)輸入傳遞給CGI程序的數(shù)據(jù)長(zhǎng)度,以bytes或字元數(shù)來(lái)計(jì)算。 |
| CONTENT_TYPE | 請(qǐng)求的MIME類型。 |
| GATEWAY_INTERFACE | 服務(wù)器的CGI版本編號(hào)。 |
| HTTP_ACCEPT | 瀏覽器能直接接收的Content-types,?可以有HTTP Accept header定義. |
| HTTP_USER_AGENT | 遞交表單的瀏覽器的名稱、版本?和其他平臺(tái)性的附加信息。 |
| HTTP_REFERER | 遞交表單的文本的?URL,不是所有的瀏覽器都發(fā)出這個(gè)信息,不要依賴它 |
| PATH_INFO | 傳遞給cgi程式的路徑信息。 |
| QUERY_STRING | 傳遞給CGI程式的請(qǐng)求參數(shù),也就是用"?"隔開(kāi),添加在URL后面的字串。 |
| REMOTE_ADDR | client端的host名稱。 |
| REMOTE_HOST | client端的IP位址。 |
| REMOTE_USER | client端送出來(lái)的使用者名稱。 |
| REMOTE_METHOD | client端發(fā)出請(qǐng)求的方法(如get、post)。 |
| SCRIPT_NAME | CGI程式所在的虛擬路徑,如/cgi-bin/echo。 |
| SERVER_NAME | server的host名稱或IP地址。 |
| SERVER_PORT | 收到request的server端口。 |
| SERVER_PROTOCOL | 所使用的通訊協(xié)定和版本編號(hào)。 |
| SERVER_SOFTWARE | server程序的名稱和版本。 |
1.2.標(biāo)準(zhǔn)輸入
環(huán)境變量的大小是有一定的限制的,當(dāng)需要傳送的數(shù)據(jù)量大時(shí),儲(chǔ)存環(huán)境變量的空間可能會(huì)不足,造成數(shù)據(jù)接收不完全,甚至無(wú)法執(zhí)行CGI程序。因此后來(lái)又發(fā)展出另外一種方法:POST,也就是利用I/O重新導(dǎo)向的技巧,讓CGI程序可以由STDIN和STDOUT直接跟瀏覽器溝通。 當(dāng)我們指定用這種方法傳遞請(qǐng)求的數(shù)據(jù)時(shí),web 服務(wù)器收到數(shù)據(jù)后會(huì)先放在一塊輸入緩沖區(qū)中,并且將數(shù)據(jù)的大小記錄在CONTENT_LENGTH這個(gè)環(huán)境變數(shù),然后調(diào)用CGI程式并將CGI程序的STDIN指向這塊緩沖區(qū),于是我們就可以很順利的通過(guò)STDIN和環(huán)境變數(shù)CONTENT_LENGTH得到所有的資料,再?zèng)]有資料大小的限制了。 ?總結(jié):CGI使外部程序與Web服務(wù)器之間交互成為可能。CGI程式運(yùn)行在獨(dú)立的進(jìn)程中,并對(duì)每個(gè)Web請(qǐng)求建立一個(gè)進(jìn)程,這種方法非常容易實(shí)現(xiàn),但效率很差,難以擴(kuò)展。面對(duì)大量請(qǐng)求,進(jìn)程的大量建立和消亡使操作系統(tǒng)性能大大下降。此外,由于地址空間無(wú)法共享,也限制了資源重用。
2.FastCGI
快速通用網(wǎng)關(guān)接口(Fast Common Gateway Interface/FastCGI)是通用網(wǎng)關(guān)接口(CGI)的改進(jìn),描述了客戶端和服務(wù)器程序之間傳輸數(shù)據(jù)的一種標(biāo)準(zhǔn)。FastCGI致力于減少Web服務(wù)器與CGI程式之間互動(dòng)的開(kāi)銷(xiāo),從而使服務(wù)器可以同時(shí)處理更多的Web請(qǐng)求。與為每個(gè)請(qǐng)求創(chuàng)建一個(gè)新的進(jìn)程不同,FastCGI使用持續(xù)的進(jìn)程來(lái)處理一連串的請(qǐng)求。這些進(jìn)程由FastCGI進(jìn)程管理器管理,而不是web服務(wù)器。(http://www.dwz.cn/yFMap)
?
當(dāng)進(jìn)來(lái)一個(gè)請(qǐng)求時(shí),Web?服務(wù)器把環(huán)境變量和這個(gè)頁(yè)面請(qǐng)求通過(guò)一個(gè)unix domain socket(都位于同一物理服務(wù)器)或者一個(gè)IP Socket(FastCGI部署在其它物理服務(wù)器)傳遞給FastCGI進(jìn)程。
?
l??step1. Web?服務(wù)器啟動(dòng)時(shí)載入初始化FastCGI執(zhí)行環(huán)境?。?例如IIS ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi
l??step2. FastCGI進(jìn)程管理器自身初始化,啟動(dòng)多個(gè)CGI解釋器進(jìn)程并等待來(lái)自Web?服務(wù)器的連接。啟動(dòng)FastCGI進(jìn)程時(shí),可以配置以ip和UNIX?域socket兩種方式啟動(dòng)。?
l??step3.?當(dāng)客戶端請(qǐng)求到達(dá)Web?服務(wù)器時(shí),?Web?服務(wù)器將請(qǐng)求采用socket方式轉(zhuǎn)發(fā)到?FastCGI主進(jìn)程,FastCGI主進(jìn)程選擇并連接到一個(gè)CGI解釋器。Web?服務(wù)器將CGI環(huán)境變量和標(biāo)準(zhǔn)輸入發(fā)送到FastCGI子進(jìn)程。
l??step4. FastCGI子進(jìn)程完成處理后將標(biāo)準(zhǔn)輸出和錯(cuò)誤信息從同一socket連接返回Web?服務(wù)器。當(dāng)FastCGI子進(jìn)程關(guān)閉連接時(shí),請(qǐng)求便處理完成。
l??step5. FastCGI子進(jìn)程接著等待并處理來(lái)自Web?服務(wù)器的下一個(gè)連接。
由于?FastCGI?程序并不需要不斷的產(chǎn)生新進(jìn)程,可以大大降低服務(wù)器的壓力并且產(chǎn)生較高的應(yīng)用效率。它的速度效率最少要比CGI?技術(shù)提高?5?倍以上。它還支持分布式的部署,?即?FastCGI程序可以在web?服務(wù)器以外的主機(jī)上執(zhí)行。
總結(jié):CGI?就是所謂的短生存期應(yīng)用程序,FastCGI?就是所謂的長(zhǎng)生存期應(yīng)用程序。FastCGI像是一個(gè)常駐(long-live)型的CGI,它可以一直執(zhí)行著,不會(huì)每次都要花費(fèi)時(shí)間去fork一次(這是CGI最為人詬病的fork-and-execute?模式)。
3.nginx cgi/fastcgi
nginx?不能像apache那樣直接執(zhí)行外部可執(zhí)行程序,但nginx可以作為代理服務(wù)器,將請(qǐng)求轉(zhuǎn)發(fā)給后端服務(wù)器,這也是nginx的主要作用之一。其中nginx就支持FastCGI代理,接收客戶端的請(qǐng)求,然后將請(qǐng)求轉(zhuǎn)發(fā)給后端fastcgi進(jìn)程。下面介紹如何使用C/C++編寫(xiě)cgi/fastcgi,并部署到nginx中。
3.1.?nginx + fastcgi
通過(guò)前面的介紹知道,fastcgi進(jìn)程由FastCGI進(jìn)程管理器管理,而不是nginx。這樣就需要一個(gè)FastCGI管理,管理我們編寫(xiě)fastcgi程序。本文使用spawn-fcgi作為FastCGI進(jìn)程管理器。
3.1.1.?spawn-fcgi
spawn-fcgi是一個(gè)通用的FastCGI進(jìn)程管理器,簡(jiǎn)單小巧,原先是屬于lighttpd的一部分,后來(lái)由于使用比較廣泛,所以就遷移出來(lái)作為獨(dú)立項(xiàng)目了。spawn-fcgi使用pre-fork?模型,功能主要是打開(kāi)監(jiān)聽(tīng)端口,綁定地址,然后fork-and-exec創(chuàng)建我們編寫(xiě)的fastcgi應(yīng)用程序進(jìn)程,退出完成工作。fastcgi應(yīng)用程序初始化,然后進(jìn)入死循環(huán)偵聽(tīng)socket的連接請(qǐng)求。
安裝spawn-fcgi:
l??獲取spawn-fcgi編譯安裝包,在http://redmine.lighttpd.net/projects/spawn-fcgi/wiki上可以獲取當(dāng)前最新的版本。
l??解壓縮spawn-fcgi-x.x.x.tar.gz包。
l??進(jìn)入解壓縮目錄,執(zhí)行./configure。
l??make & make install
如果遇到以下錯(cuò)誤:“?./autogen.sh: x: autoreconf: not found”,因?yàn)闆](méi)有安裝automake?工具,ubuntu用下面的命令安裝好就可以了:sudo apt-get install autoconf automake libtool?。
spawn-fcgi的幫助信息可以通過(guò)man spawn-fcgi或spawn-fcgi?–h獲得,下面是部分常用spawn-fcgi參數(shù)信息:
| -f <fcgiapp>?指定調(diào)用FastCGI的進(jìn)程的執(zhí)行程序位置 -a <addr>?綁定到地址addr。 -p <port>?綁定到端口port。 -s <path>?綁定到unix domain socket -C <childs>?指定產(chǎn)生的FastCGI的進(jìn)程數(shù),默認(rèn)為5。(僅用于PHP) -P <path>?指定產(chǎn)生的進(jìn)程的PID文件路徑。 -F <childs>?指定產(chǎn)生的FastCGI的進(jìn)程數(shù)(C的CGI用這個(gè)) -u和-g FastCGI使用什么身份(-u?用戶?-g?用戶組)運(yùn)行,CentOS下可以使用apache用戶,其他的根據(jù)情況配置,如nobody、www-data等。 |
3.1.2.?編寫(xiě)fastcgi應(yīng)用程序
使用C/C++編寫(xiě)fastcgi應(yīng)用程序,可以使用FastCGI軟件開(kāi)發(fā)套件或者其它開(kāi)發(fā)框架,如fastcgi++。
本文使用FastCGI軟件開(kāi)發(fā)套件——fcgi(http://www.fastcgi.com/drupal/node/6?q=node/21),通過(guò)此套件可以輕松編寫(xiě)fastcgi應(yīng)用程序,安裝fcgi:
l??獲取fcgi編譯安裝包,在http://www.fastcgi.com/drupal/node/5上可以獲取當(dāng)前最新的版本。
l??解壓縮fcgi-x.x.x.tar.gz包。
l??進(jìn)入解壓縮目錄,執(zhí)行./configure。
l??make & make install
如果編譯提示一下錯(cuò)誤:
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 scope
fcgio.cpp:75: error: 'EOF' was not declared in this scope
fcgio.cpp: In member function 'virtual int fcgi_streambuf::sync()':
fcgio.cpp:86: error: 'EOF' was not declared in this scope
fcgio.cpp:87: error: 'EOF' was not declared in this scope
fcgio.cpp: In member function 'virtual int fcgi_streambuf::underflow()':
fcgio.cpp:113: error: 'EOF' was not declared in this scope
make[2]: *** [fcgio.lo] Error 1
make[2]: Leaving directory `/root/downloads/fcgi-2.4.1-SNAP-0910052249/libfcgi'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/root/downloads/fcgi-2.4.1-SNAP-0910052249'
make: *** [all] Error 2
解決辦法:在/include/fcgio.h文件中加上?#include <cstdio>,然后再編譯安裝就通過(guò)了。
如果提示找不到動(dòng)態(tài)庫(kù),請(qǐng)?jiān)贚D_LIBRARY_PATH或/etc/ld.so.conf中添加fcgi的安裝路徑,如/usr/local/lib,并執(zhí)行l(wèi)dconfig更新一下。
#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/目錄
通過(guò)spawn-fcgi啟動(dòng)c/c++編寫(xiě)好的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配置
關(guān)于nginx的幾個(gè)配置文件解析,可以參閱《Nginx安裝與使用》http://www.cnblogs.com/skynet/p/4146083.html,在上篇的nginx.conf基礎(chǔ)上增加下面的fastcgi配置。
?
這樣nginx收到http://localhost/demo.cgi請(qǐng)求時(shí),會(huì)匹配到location = /demo.cgi塊,將請(qǐng)求傳到后端的fastcgi應(yīng)用程序處理。如下如所示:(注意其中number為80,是因?yàn)槲艺?qǐng)求了80次)
?
3.2.?nginx + cgi
nginx?不能直接執(zhí)行外部可執(zhí)行程序,并且cgi是接收到請(qǐng)求時(shí)才會(huì)啟動(dòng)cgi進(jìn)程,不像fastcgi會(huì)在一開(kāi)就啟動(dòng)好,這樣nginx天生是不支持?cgi?的。nginx?雖然不支持cgi,但它支持fastCGI。所以,我們可以考慮使用fastcgi包裝來(lái)支持?cgi。原理大致如下圖所示:pre-fork幾個(gè)通用的代理fastcgi程序——fastcgi-wrapper,fastcgi-wrapper啟動(dòng)執(zhí)行cgi然后將cgi的執(zhí)行結(jié)果返回給nginx(fork-and-exec)。
?
明白原理之后,編寫(xiě)一個(gè)fastcgi-warpper也比較簡(jiǎn)單。網(wǎng)上流傳比較多的一個(gè)解決方案是,來(lái)自nginx wiki(http://wiki.nginx.org/SimpleCGI)上的使用perl的fastcgi包裝腳本cgiwrap-fcgi.pl。但我對(duì)perl不是很感冒,下面給出一個(gè)C/C++寫(xiě)的fastcgi-wrapper。
3.2.1.?fastcgi-wrapper
其實(shí)編寫(xiě)C/C++的fastcgi-wrapper,就是寫(xiě)一個(gè)C/C++的fastcgi,步驟和原理跟前面的小節(jié)(nginx+fastcgi)一樣。github上已經(jīng)有人開(kāi)源了,C寫(xiě)的fastcgi-wrapper:https://github.com/gnosek/fcgiwrap。
安裝fcgiwrap:
l??下載(https://github.com/gnosek/fcgiwrap.git)
l??解壓縮fcgiwrap,進(jìn)入解壓目錄
l??autoreconf -i
l??./configure
l??make && make install
啟動(dòng)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請(qǐng)求都會(huì)走到fcgiwrap,然后fcgiwrap會(huì)執(zhí)行cgi-bin目錄下的cgi程序。
?
?
3.2.3.?編寫(xiě)cgi應(yīng)用程序
?
#include?<stdio.h>
#include?<stdlib.h>
?
int?main(void)
{
????int?count?= 0;
????printf("Content-type: text/html\r\n"
????????"\r\n"
????????"<title>CGI Hello!</title>"
?
????????"<h1>CGI Hello!</h1>"
????????"Request number %d running on host <i>%s</i>\n",
????????++count,?getenv("SERVER_NAME"));
????return?0;
}
?
tyler@ubuntu:~/ClionProjects/HelloFastCGI$ g++ cgi.cpp -o cgidemo -lfcgi
tyler@ubuntu:~/ClionProjects/HelloFastCGI$ sudo cp cgidemo /opt/nginx-1.7.7/cgi-bin/
?
?
注意圖中的請(qǐng)求次數(shù)一直都是1,因?yàn)閏gi的模式是fork-and-exec,每次都是一個(gè)新的進(jìn)程。
?
?
?
總結(jié)
以上是生活随笔為你收集整理的Nginx + CGI/FastCGI + C/Cpp的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: nginx实现tomcat的负载均衡及企
- 下一篇: Nginx Kafka数据生产接口