簡單說明CGI和動態(tài)請求是什么
CGI是common gateway interface的縮寫,大家都譯作通用網(wǎng)關(guān)接口,但很不幸,我們無法見名知意。
我們知道,web服務器所處理的內(nèi)容都是靜態(tài)的,要想處理動態(tài)內(nèi)容,需要依賴于web應用程序,如php、jsp、python、perl等。但是web server如何將動態(tài)的請求傳遞給這些應用程序?它所依賴的就是cgi協(xié)議。沒錯,是協(xié)議,也就是web server和web應用程序交流時的規(guī)范。
換句話說,通過cgi協(xié)議,再結(jié)合已搭建好的web應用程序,就可以讓web server也能"處理"動態(tài)請求(或者說,當用戶訪問某個特定資源時,可以觸發(fā)執(zhí)行某個web應用程序來實現(xiàn)特定功能),你肯定知道處理兩字為什么要加上雙引號。
簡單版的cgi工作方式如下:
例如,在谷歌搜索欄中搜索一個關(guān)鍵詞"http",對應的URL為:
https://www.google.com/search?q=http&oq=http&aqs=chrome..69i57j69i60l4j0.1136j0j8&sourceid=chrome&ie=UTF-8
當谷歌的web server收到該請求后,先分析該url,從中知道了要執(zhí)行search程序,并且還知道了一系列要傳遞給search的參數(shù)及其對應的value。web server會將這些程序參數(shù)和其它一些環(huán)境變量根據(jù)cgi協(xié)議通過TCP或套接字等方式傳遞給已啟動的cgi程序(可能是cgi進程,或者是已加載的模塊cgi模塊)。當cgi進程接收到web server的請求后,調(diào)用search程序并執(zhí)行,同時還會傳遞參數(shù)給search程序。search執(zhí)行結(jié)束后,cgi進程/線程將處理結(jié)果返回給web server,web server再返回給瀏覽器。
有多種方式可以執(zhí)行cgi程序,但對http的請求方法來說,只有g(shù)et和post兩種方法允許執(zhí)行cgi腳本(即上面的search程序)。實際上post方法的內(nèi)部本質(zhì)還是get方法,只不過在發(fā)送http請求時,get和post方法對url中的參數(shù)處理方式不一樣而已。
任何一種語言都能編寫CGI,只不過有些語言比較擅長,有些語言則非常繁瑣,例如用bash shell開發(fā),那么需要用echo等打印語句將執(zhí)行結(jié)果放在巨多無比的html的標簽中輸出給客戶端。常用于編寫CGI的語言有perl、php、python等,java也一樣能寫,但java的servlet完全能實現(xiàn)CGI的功能,且更優(yōu)化、更利于開發(fā)。
說實話,對于一個沒接觸過編程語言的人來說,剛接觸cgi概念的時候肯定會有一堆疑問,這到底是什么鬼,處理動態(tài)內(nèi)容的東西不是像php一樣的應用程序嗎,跟cgi有幾毛錢關(guān)系,fastcgi又是什么?我想,非科班出身的強迫癥患者(包括我)一定會被這些概念折騰的死去活來。
以php為例,我將一次動態(tài)請求相關(guān)的概念大致都簡單解釋一遍。
cgi:它是一種協(xié)議。通過cgi協(xié)議,web server可以將動態(tài)請求和相關(guān)參數(shù)發(fā)送給專門處理動態(tài)內(nèi)容的應用程序。
fastcgi:也是一種協(xié)議,只不過是cgi的優(yōu)化版。cgi的性能較爛,fastcgi則在其基礎上進行了改進。
php-cgi:fastcgi是一種協(xié)議,而php-cgi實現(xiàn)了這種協(xié)議。不過這種實現(xiàn)比較爛。它是單進程的,一個進程處理一個請求,處理結(jié)束后進程就銷毀。
php-fpm:是對php-cgi的改進版,它直接管理多個php-cgi進程/線程。也就是說,php-fpm是php-cgi的進程管理器因此它也算是fastcgi協(xié)議的實現(xiàn)。在一定程度上講,php-fpm與php的關(guān)系,和tomcat對java的關(guān)系是類似的。
cgi進程/線程:在php上,就是php-cgi進程/線程。專門用于接收web server的動態(tài)請求,調(diào)用并初始化zend虛擬機。
cgi腳本:被執(zhí)行的php源代碼文件。
zend虛擬機:對php文件做詞法分析、語法分析、編譯成opcode,并執(zhí)行。最后關(guān)閉zend虛擬機。
cgi進程/線程和zend虛擬機的關(guān)系:cgi進程調(diào)用并初始化zend虛擬機的各種環(huán)境。
以php-fpm為例,web server從轉(zhuǎn)發(fā)動態(tài)請求到結(jié)束的過程大致如下:

而每個php-cgi進程的作用大致包括:(有些功能分類錯誤,請無視,知道大致功能就夠了)

注意,盡管php-fpm的全稱為PHP FastCGI Process Manager,但嚴格地講,php-fpm不是fastcgi的進程管理器,而是php fastcgi即php-cgi的進程管理器。fastcgi只是一種協(xié)議,不是進程。就像http協(xié)議一樣,apache對它的實現(xiàn)是httpd,nginx對它的實現(xiàn)就叫nginx。
再次說明,cgi和fastcgi是一種協(xié)議。各種支持和WEB交互的編程語言對cgi/fastcgi協(xié)議都做了各自的實現(xiàn)(當然,任何一種語言都能寫cgi腳本),而php上的php-cgi和php-fpm正是php對fastcgi協(xié)議的實現(xiàn)。
web server對cgi進程/線程來說,它的作用就是發(fā)起動態(tài)處理請求,傳遞一些參數(shù)和環(huán)境變量,最后接收cgi的返回結(jié)果。再通俗而不嚴謹?shù)卣f,web server通過cgi/fastcgi協(xié)議將動態(tài)請求轉(zhuǎn)發(fā)給執(zhí)行cgi腳本的應用程序。通過下面httpd.conf中的轉(zhuǎn)發(fā)配置應該很容易理解(httpd和php-fpm的交互):
ProxyRequests off
ProxyPassMatch ^/(.*\.php)$fcgi://127.0.0.1:9000/usr/local/apache/htdocs/$1
以最典型的apache httpd和php為例,對于httpd來說,web server和php-cgi有3種交互模式。
cgi模式:httpd接收到一個動態(tài)請求就fork一個cgi進程,cgi進程返回結(jié)果給httpd進程后自我銷毀。
動態(tài)模塊模式:將php-cgi的模塊(例如php5_module)編譯進httpd。在httpd啟動時會加載模塊,加載時也將對應的模塊激活,php-cgi也就啟動了。(注:糾正一個小小錯誤,很多人以為動態(tài)編譯的模塊是可以在需要的時候隨時加載調(diào)用,不需要的時候它們就停止了,實際上不是這樣的。和靜態(tài)編譯的模塊一樣,動態(tài)加載的模塊在被加載時就被加入到激活鏈表中,無論是否使用它,它都已經(jīng)運行在apache httpd的內(nèi)部。可參考LoadModule指令的官方手冊)
php-fpm模式:使用php-fpm管理php-cgi,此時httpd不再控制php-cgi進程的啟動。可以將php-fpm獨立運行在非web服務器上,實現(xiàn)所謂的動靜分離。
實際上,借助模塊mod_fastcgi還可以實現(xiàn)fastcgi模式。同cgi一樣,管理模式的先天缺陷決定了這并不是一種好方法。
使用CGI模式時,當動態(tài)請求到達,httpd臨時啟動一個cgi解釋器,并通過cgi協(xié)議轉(zhuǎn)發(fā)要運行的內(nèi)容。當cgi腳本運行結(jié)束后,將結(jié)果返回給httpd,然后cgi解釋器進程自我銷毀。當多個動態(tài)請求到達時,將先后啟動多個cgi解釋器。因此,這種方法效率極低。
在注釋掉php5_module的LoadModule相關(guān)行后,使用action指令指定要使用cgi運行的類型。但注意,action指令是mod_action提供的,所以必須已經(jīng)加載該模塊。
例如:指定MIME類型為image/gif的請求使用images.cgi運行。顯然,images.cgi腳本你必須先寫好。
Action image/gif /cgi-bin/images.cgi
還可以通過添加handler來復合文件類型,再使用某個cgi腳本去運行這個handler中的任意類型。
AddHandler my-file-type .xyz Action my-file-type "/cgi-bin/program.cgi"
對于php來說,則可以使用安裝php時bin目錄下提供的php-cgi程序作為cgi程序。
[root@xuexi php]# ls /usr/local/php/bin/
pear peardev pecl phar phar.phar php php-cgi php-config phpize
# 復制到apache默認的cgi-bin目錄下,方便管理
[root@xuexi php]# cp /usr/local/php/bin/php-cgi /usr/local/apache/cgi-bin/
# 在httpd.conf中添加以下行
Action application/x-httpd-php /usr/local/php/bin/cgi-bin/php-cgi
在編譯php時,將php5_module模塊編譯到apache中,例如在編譯php時在./configure配置中加上"--with-apxs2=/usr/local/apache/bin/apxs"。
這種交互模式下,httpd在啟動時加載并激活php_module。也就是說,php-cgi常駐在httpd進程內(nèi)部。當動態(tài)請求到達時,httpd不用再生成cgi解釋器,而是直接將動態(tài)請求轉(zhuǎn)發(fā)給它內(nèi)部php-cgi。
配置實用這種交互模式非常簡單,只需使用LoadModule加載php_module,再添加對應的MIME處理器即可。
LoadModule php5_module modules/libphp5.so
# 在mime模塊中添加對應的類型
<IfModule mime_module>
AddType application/x-httpd-php .php
AddType applicaiton/x-httpd-php-source .phps
前面說了,php-fpm是php-cgi的進程管理器。這種交互方式實際上是讓php-cgi以獨立于httpd的方式存在,目前基本使用php-fpm的方式管理php-cgi進程。也就是說,這種模式下,php-cgi和httpd已經(jīng)分離了,它們的分離意味著請求的動靜分離變?yōu)榭赡埽篽ttpd和php-fpm分別運行在不同服務器上。動靜分離后,壓力也分散到各自的服務器上。
要讓php-fpm以這種方式運行,需要在編譯的./configure配置選項中添加"--enable-fpm"選項。當然,還得啟動php-fpm服務。例如:
service php-fpm start
這樣php-cgi進程就開放著端口(默認9000)等待httpd轉(zhuǎn)發(fā)動態(tài)請求。要讓httpd能夠轉(zhuǎn)發(fā)請求到php-cgi上,需要在httpd.conf中關(guān)閉正向代理,并設置fastcgi協(xié)議代理參數(shù)。例如,轉(zhuǎn)發(fā)到192.168.100.54主機上的php-fpm。
# 加載代理模塊
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
# 添加MIME類型
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
# 在需要轉(zhuǎn)發(fā)的虛擬主機中配置轉(zhuǎn)發(fā)代理
ProxyRequests offProxyPassMatch ^/(.*\.php)$ fcgi://192.168.100.54:9000/usr/local/apache/htdocs/$1
來源:碼農(nóng)有道
工程師必備
- 項目客服
- 培訓客服
- 平臺客服
TOP




















