CGI是common gateway interface的縮寫,你們都譯做通用網關接口,但很不幸,咱們沒法見名知意。php
咱們知道,web服務器所處理的內容都是靜態的,要想處理動態內容,須要依賴於web應用程序,如php、jsp、python、perl等。可是web server如何將動態的請求傳遞給這些應用程序?它所依賴的就是cgi協議。沒錯,是協議,也就是web server和web應用程序交流時的規範。換句話說,經過cgi協議,再結合已搭建好的web應用程序,就可讓web server也能"處理"動態請求(或者說,當用戶訪問某個特定資源時,能夠觸發執行某個web應用程序來實現特定功能),你確定知道處理兩字爲何要加上雙引號。html
簡單版的cgi工做方式以下:java
例如,在谷歌搜索欄中搜索一個關鍵詞"http",對應的URL爲:python
https://www.google.com/search?q=http&oq=http&aqs=chrome..69i57j69i60l4j0.1136j0j8&sourceid=chrome&ie=UTF-8
當谷歌的web server收到該請求後,先分析該url,從中知道了要執行search程序,而且還知道了一系列要傳遞給search的參數及其對應的value。web server會將這些程序參數和其它一些環境變量根據cgi協議經過TCP或套接字等方式傳遞給已啓動的cgi程序(多是cgi進程,或者是已加載的模塊cgi模塊)。當cgi進程接收到web server的請求後,調用search程序並執行,同時還會傳遞參數給search程序。search執行結束後,cgi進程/線程將處理結果返回給web server,web server再返回給瀏覽器。nginx
有多種方式能夠執行cgi程序,但對http的請求方法來講,只有get和post兩種方法容許執行cgi腳本(即上面的search程序)。實際上post方法的內部本質仍是get方法,只不過在發送http請求時,get和post方法對url中的參數處理方式不同而已。web
任何一種語言都能編寫CGI,只不過有些語言比較擅長,有些語言則很是繁瑣,例如用bash shell開發,那麼須要用echo等打印語句將執行結果放在巨多無比的html的標籤中輸出給客戶端。經常使用於編寫CGI的語言有perl、php、python等,java也同樣能寫,但java的servlet徹底能實現CGI的功能,且更優化、更利於開發。chrome
說實話,對於一個沒接觸過編程語言的人來講,剛接觸cgi概念的時候確定會有一堆疑問,這究竟是什麼鬼,處理動態內容的東西不是像php同樣的應用程序嗎,跟cgi有幾毛錢關係,fastcgi又是什麼?我想,非科班出身的強迫症患者(包括我)必定會被這些概念折騰的死去活來。shell
以php爲例,我將一次動態請求相關的概念大體都簡單解釋一遍。apache
cgi
:它是一種協議。經過cgi協議,web server能夠將動態請求和相關參數發送給專門處理動態內容的應用程序。fastcgi
:也是一種協議,只不過是cgi的優化版。cgi的性能較爛,fastcgi則在其基礎上進行了改進。php-cgi
:fastcgi是一種協議,而php-cgi實現了這種協議。不過這種實現比較爛。它是單進程的,一個進程處理一個請求,處理結束後進程就銷燬。php-fmp
:是對php-cgi的改進版,它直接管理多個php-cgi進程/線程。也就是說,php-fpm是php-cgi的進程管理器所以它也算是fastcgi協議的實現。在必定程度上講,php-fpm與php的關係,和tomcat對java的關係是相似的。cgi進程/線程
:在php上,就是php-cgi進程/線程。專門用於接收web server的動態請求,調用並初始化zend虛擬機。cgi腳本
:被執行的php源代碼文件。zend虛擬機
:對php文件作詞法分析、語法分析、編譯成opcode,並執行。最後關閉zend虛擬機。cgi進程/線程和zend虛擬機的關係
:cgi進程調用並初始化zend虛擬機的各類環境。以php-fpm爲例,web server從轉發動態請求到結束的過程大體以下:編程
而每一個php-cgi進程的做用大體包括:(有些功能分類錯誤,請無視,知道大體功能就夠了)
注意,儘管php-fpm的全稱爲PHP FastCGI Process Manager,但嚴格地講,php-fpm不是fastcgi的進程管理器,而是php fastcgi即php-cgi的進程管理器。fastcgi只是一種協議,不是進程。就像http協議同樣,apache對它的實現是httpd,nginx對它的實現就叫nginx。
再次說明,cgi和fastcgi是一種協議。各類支持和WEB交互的編程語言對cgi/fastcgi協議都作了各自的實現(固然,任何一種語言都能寫cgi腳本),而php上的php-cgi和php-fpm正是php對fastcgi協議的實現。
web server對cgi進程/線程來講,它的做用就是發起動態處理請求,傳遞一些參數和環境變量,最後接收cgi的返回結果。再通俗而不嚴謹地說,web server經過cgi/fastcgi協議將動態請求轉發給執行cgi腳本的應用程序。經過下面httpd.conf中的轉發配置應該很容易理解(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接收到一個動態請求就fork一個cgi進程,cgi進程返回結果給httpd進程後自我銷燬。動態模塊模式
:將php-cgi的模塊(例如php5_module)編譯進httpd。在httpd啓動時會加載模塊,加載時也將對應的模塊激活,php-cgi也就啓動了。(注:糾正一個小小錯誤,不少人覺得動態編譯的模塊是能夠在須要的時候隨時加載調用,不須要的時候它們就中止了,實際上不是這樣的。和靜態編譯的模塊同樣,動態加載的模塊在被加載時就被加入到激活鏈表中,不管是否使用它,它都已經運行在apache httpd的內部。可參考LoadModule指令的官方手冊)php-fpm模式
:使用php-fpm管理php-cgi,此時httpd再也不控制php-cgi進程的啓動。能夠將php-fpm獨立運行在非web服務器上,實現所謂的動靜分離。實際上,藉助模塊mod_fastcgi還能夠實現fastcgi模式。同cgi同樣,管理模式的先天缺陷決定了這並非一種好方法。
使用CGI模式時,當動態請求到達,httpd臨時啓動一個cgi解釋器,並經過cgi協議轉發要運行的內容。當cgi腳本運行結束後,將結果返回給httpd,而後cgi解釋器進程自我銷燬。當多個動態請求到達時,將前後啓動多個cgi解釋器。所以,這種方法效率極低。
在註釋掉php5_module的LoadModule相關行後,使用action指令指定要使用cgi運行的類型。但注意,action指令是mod_action提供的,因此必須已經加載該模塊。
例如:指定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進程內部。當動態請求到達時,httpd不用再生成cgi解釋器,而是直接將動態請求轉發給它內部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
</IfModule>
前面說了,php-fpm是php-cgi的進程管理器。這種交互方式其實是讓php-cgi以獨立於httpd的方式存在,目前基本使用php-fpm的方式管理php-cgi進程。也就是說,這種模式下,php-cgi和httpd已經分離了,它們的分離意味着請求的動靜分離變爲可能:httpd和php-fpm分別運行在不一樣服務器上。動靜分離後,壓力也分散到各自的服務器上。
要讓php-fpm以這種方式運行,須要在編譯的./configure配置選項中添加"--enable-fpm"選項。固然,還得啓動php-fpm服務。例如:
service php-fpm start
這樣php-cgi進程就開放着端口(默認9000)等待httpd轉發動態請求。要讓httpd可以轉發請求到php-cgi上,須要在httpd.conf中關閉正向代理,並設置fastcgi協議代理參數。例如,轉發到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
# 在須要轉發的虛擬主機中配置轉發代理
ProxyRequests off
ProxyPassMatch ^/(.*\.php)$ fcgi://192.168.100.54:9000/usr/local/apache/htdocs/$1