文章轉(zhuǎn)載自網(wǎng)關(guān)協(xié)議學(xué)習(xí):CGI、FastCGI讼稚、WSGI
CGI
CGI即通用網(wǎng)關(guān)接口(Common Gateway Interface)括儒,是外部應(yīng)用程序(CGI程序)與Web服務(wù)器之間的接口標(biāo)準(zhǔn),是在CGI程序和Web服務(wù)器之間傳遞信息的規(guī)程锐想。CGI規(guī)范允許Web服務(wù)器執(zhí)行外部程序帮寻,并將它們的輸出發(fā)送給Web瀏覽器,CGI將Web的一組簡單的靜態(tài)超媒體文檔變成一個(gè)完整的新的交互式媒體赠摇。通俗的講CGI就像是一座橋固逗,把網(wǎng)頁和WEB服務(wù)器中的執(zhí)行程序連接起來浅蚪,它把HTML接收的指令傳遞給服務(wù)器的執(zhí)行程序,再把服務(wù)器執(zhí)行程序的結(jié)果返還給HTML頁抒蚜。CGI 的跨平臺(tái)性能極佳掘鄙,幾乎可以在任何操作系統(tǒng)上實(shí)現(xiàn)。
CGI方式在遇到連接請求(用戶請求)先要?jiǎng)?chuàng)建cgi的子進(jìn)程嗡髓,激活一個(gè)CGI進(jìn)程操漠,然后處理請求,處理完后結(jié)束這個(gè)子進(jìn)程饿这。這就是fork-and-execute模式浊伙。所以用cgi方式的服務(wù)器有多少連接請求就會(huì)有多少cgi子進(jìn)程,子進(jìn)程反復(fù)加載是cgi性能低下的主要原因长捧。當(dāng)用戶請求數(shù)量非常多時(shí)嚣鄙,會(huì)大量擠占系統(tǒng)的資源如內(nèi)存,CPU時(shí)間等串结,造成效能低下哑子。
CGI腳本工作流程:
- 瀏覽器通過HTML表單或超鏈接請求指向一個(gè)CGI應(yīng)用程序的URL。
- 服務(wù)器收發(fā)到請求肌割。
- 服務(wù)器執(zhí)行所指定的CGI應(yīng)用程序卧蜓。
- CGI應(yīng)用程序執(zhí)行所需要的操作,通常是基于瀏覽者輸入的內(nèi)容把敞。
- CGI應(yīng)用程序把結(jié)果格式化為網(wǎng)絡(luò)服務(wù)器和瀏覽器能夠理解的文檔(通常是HTML網(wǎng)頁)弥奸。
- 網(wǎng)絡(luò)服務(wù)器把結(jié)果返回到瀏覽器中。
FastCGI
FastCGI是一個(gè)可伸縮地奋早、高速地在HTTP server和動(dòng)態(tài)腳本語言間通信的接口盛霎。多數(shù)流行的HTTP server都支持FastCGI,包括Apache耽装、Nginx和lighttpd等愤炸,同時(shí),F(xiàn)astCGI也被許多腳本語言所支持掉奄,其中就有PHP规个。
FastCGI是從CGI發(fā)展改進(jìn)而來的。傳統(tǒng)CGI接口方式的主要缺點(diǎn)是性能很差挥萌,因?yàn)槊看蜨TTP服務(wù)器遇到動(dòng)態(tài)程序時(shí)都需要重新啟動(dòng)腳本解析器來執(zhí)行解析绰姻,然后結(jié)果被返回給HTTP服務(wù)器枉侧。這在處理高并發(fā)訪問時(shí)引瀑,幾乎是不可用的。FastCGI像是一個(gè)常駐(long-live)型的CGI榨馁,它可以一直執(zhí)行著憨栽,只要激活后,不會(huì)每次都要花費(fèi)時(shí)間去fork一次(這是CGI最為人詬病的fork-and-execute 模式)。CGI 就是所謂的短生存期應(yīng)用程序屑柔,F(xiàn)astCGI 就是所謂的長生存期應(yīng)用程序屡萤。由于 FastCGI 程序并不需要不斷的產(chǎn)生新進(jìn)程,可以大大降低服務(wù)器的壓力并且產(chǎn)生較高的應(yīng)用效率掸宛。它的速度效率最少要比CGI 技術(shù)提高 5 倍以上死陆。它還支持分布式的運(yùn)算, 即 FastCGI 程序可以在網(wǎng)站服務(wù)器以外的主機(jī)上執(zhí)行并且接受來自其它網(wǎng)站服務(wù)器來的請求。
FastCGI是語言無關(guān)的唧瘾、可伸縮架構(gòu)的CGI開放擴(kuò)展措译,其主要行為是將CGI解釋器進(jìn)程保持在內(nèi)存中并因此獲得較高的性能。眾所周知饰序,CGI解釋器的反復(fù)加載是CGI性能低下的主要原因领虹,如果CGI解釋器保持在內(nèi)存中并接受FastCGI進(jìn)程管理器調(diào)度,則可以提供良好的性能求豫、伸縮性塌衰、Fail-Over特性等等。FastCGI接口方式采用C/S結(jié)構(gòu)蝠嘉,可以將HTTP服務(wù)器和腳本解析服務(wù)器分開最疆,同時(shí)在腳本解析服務(wù)器上啟動(dòng)一個(gè)或者多個(gè)腳本解析守護(hù)進(jìn)程。當(dāng)HTTP服務(wù)器每次遇到動(dòng)態(tài)程序時(shí)是晨,可以將其直接交付給FastCGI進(jìn)程來執(zhí)行肚菠,然后將得到的結(jié)果返回給瀏覽器。這種方式可以讓HTTP服務(wù)器專一地處理靜態(tài)請求或者將動(dòng)態(tài)腳本服務(wù)器的結(jié)果返回給客戶端罩缴,這在很大程度上提高了整個(gè)應(yīng)用系統(tǒng)的性能蚊逢。
FastCGI的工作流程:
- Web Server啟動(dòng)時(shí)載入FastCGI進(jìn)程管理器(PHP-CGI或者PHP-FPM或者spawn-cgi)
- FastCGI進(jìn)程管理器自身初始化,啟動(dòng)多個(gè)CGI解釋器進(jìn)程(可見多個(gè)php-cgi)并等待來自Web Server的連接箫章。
- 當(dāng)客戶端請求到達(dá)Web Server時(shí)烙荷,F(xiàn)astCGI進(jìn)程管理器選擇并連接到一個(gè)CGI解釋器。Web server將CGI環(huán)境變量和標(biāo)準(zhǔn)輸入發(fā)送到FastCGI子進(jìn)程php-cgi檬寂。
- FastCGI子進(jìn)程完成處理后將標(biāo)準(zhǔn)輸出和錯(cuò)誤信息從同一連接返回Web Server终抽。當(dāng)FastCGI子進(jìn)程關(guān)閉連接時(shí),請求便告處理完成桶至。FastCGI子進(jìn)程接著等待并處理來自FastCGI進(jìn)程管理器(運(yùn)行在Web Server中)的下一個(gè)連接昼伴。 在CGI模式中,php-cgi在此便退出镣屹。
FastCGI 的特點(diǎn)
- 打破傳統(tǒng)頁面處理技術(shù)圃郊。傳統(tǒng)的頁面處理技術(shù),程序必須與 Web 服務(wù)器或 Application 服務(wù)器處于同一臺(tái)服務(wù)器中女蜈。這種歷史已經(jīng)早N年被FastCGI技術(shù)所打破持舆,F(xiàn)astCGI技術(shù)的應(yīng)用程序可以被安裝在服務(wù)器群中的任何一臺(tái)服務(wù)器色瘩,而通過 TCP/IP 協(xié)議與 Web 服務(wù)器通訊,這樣做既適合開發(fā)大型分布式 Web 群逸寓,也適合高效數(shù)據(jù)庫控制居兆。
- 明確的請求模式。CGI 技術(shù)沒有一個(gè)明確的角色竹伸,在 FastCGI 程序中泥栖,程序被賦予明確的角色(響應(yīng)器角色、認(rèn)證器角色勋篓、過濾器角色)聊倔。
ISAPI
ISAPI(Internet Server Application Program Interface)是微軟提供的一套面向WEB服務(wù)的API接口,它能實(shí)現(xiàn)CGI提供的全部功能生巡,并在此基礎(chǔ)上進(jìn)行了擴(kuò)展耙蔑,如提供了過濾器應(yīng)用程序接口。ISAPI應(yīng)用大多數(shù)以DLL動(dòng)態(tài)庫的形式使用孤荣,可以在被用戶請求后執(zhí)行甸陌,在處理完一個(gè)用戶請求后不會(huì)馬上消失,而是繼續(xù)駐留在內(nèi)存中等待處理別的用戶輸入盐股。此外,ISAPI的DLL應(yīng)用程序和WEB服務(wù)器處于同一個(gè)進(jìn)程中钱豁,效率要顯著高于CGI。(由于微軟的排他性疯汁,只能運(yùn)行于windows環(huán)境)
ISAPI服務(wù)器擴(kuò)展為使用 Internet 服務(wù)器的通用網(wǎng)關(guān)接口(CGI) 應(yīng)用程序提供了另一種選擇牲尺。與 CGI 應(yīng)用程序不同,ISA 在 HTTP服務(wù)器所在的同一地址空間運(yùn)行幌蚊,并且可以訪問可由 HTTP 服務(wù)器使用的所有資源谤碳。ISA 的系統(tǒng)開銷比 CGI 應(yīng)用程序低,因?yàn)樗鼈儾灰髣?chuàng)建其他進(jìn)程溢豆,也不執(zhí)行需要越過進(jìn)程邊界的通信蜒简,而這種通信非常耗時(shí)。如果內(nèi)存被其他進(jìn)程所需要漩仙,擴(kuò)展和篩選器DLL 都可能被卸載搓茬。ISAPI 允許在一個(gè) DLL 中有多個(gè)命令,這些命令作為 DLL 中CHttpServer對象的成員函數(shù)來實(shí)現(xiàn)队他。CGI 要求每個(gè)任務(wù)有一個(gè)單獨(dú)的名稱和一個(gè)到單獨(dú)的可執(zhí)行文件的 URL 映射卷仑。每個(gè)新的 CGI 請求啟動(dòng)一個(gè)新進(jìn)程,而每個(gè)不同的請求包含在各自的可執(zhí)行文件中麸折,這些文件根據(jù)每個(gè)請求加載和卸載锡凝,因此系統(tǒng)開銷高于 ISA。
PHP-CGI
PHP-CGI是PHP自帶的FastCGI管理器磕谅。PHP-CGI的不足:
- php-cgi變更php.ini配置后需重啟php-cgi才能讓新的php-ini生效私爷,不可以平滑重啟
- 直接殺死php-cgi進(jìn)程php就不能運(yùn)行了。(PHP-FPM和Spawn-FCGI就沒有這個(gè)問題,守護(hù)進(jìn)程會(huì)平滑從新生成新的子進(jìn)程膊夹。)
Spawn-FCGI
Spawn-FCGI是一個(gè)通用的FastCGI管理服務(wù)器衬浑,它是lighttpd中的一部份,很多人都用Lighttpd的Spawn-FCGI進(jìn)行FastCGI模式下的管理工作放刨,不過有不少缺點(diǎn)工秩。而PHP-FPM的出現(xiàn)多少緩解了一些問題,但PHP-FPM有個(gè)缺點(diǎn)就是要重新編譯进统,這對于一些已經(jīng)運(yùn)行的環(huán)境可能有不小的風(fēng)險(xiǎn))助币,在php 5.3.3中可以直接使用PHP-FPM了。Spawn-FCGI的代碼很少螟碎,全部才630行眉菱,用c語言編寫,最近一次提交是5年前掉分。代碼主頁:https://github.com/lighttpd/spawn-fcgi
Spawn-FCGI代碼分析如下:
- spawn-fcgi 首先create socket,bind,listen 3步創(chuàng)建服務(wù)器socket,(把這個(gè)socket叫做 fcgi_fd)
- 用dup2俭缓,把fcgi_fd 交換給 FCGI_LISTENSOCK_FILENO (FCGI_LISTENSOCK_FILENO數(shù)值上等于0,這是fastcgi協(xié)議當(dāng)中指定用來listen的socket id)
- 執(zhí)行execl ,replaces the current process image with a new process image. process image 進(jìn)程在運(yùn)行空間的代碼段
很顯然酥郭,Spawn-FCGI也是 pre-fork 模型华坦,只是用了上古C語言編寫,充滿了N多 unix下暗黑編程技巧不从。
Spawn-FCGI功能很單一:
- 只管fork進(jìn)程惜姐,子進(jìn)程掛了,主進(jìn)程僅僅log記錄一次椿息,根本不會(huì)重新fork歹袁。在2009年一段時(shí)間內(nèi),我曾經(jīng)用spawn-fcgi部署php-cgi寝优,當(dāng)跑一段時(shí)間就會(huì)全掛掉宇攻,只能用crontab定時(shí)重啟spawn-fcgi
- 不負(fù)責(zé)子進(jìn)程中的網(wǎng)絡(luò)IO,把socket放到指定位置就完了倡勇,接下來的事情由被spawn的程序處理
Spawn-FCGI是一個(gè)很早期的程序逞刷,瞻仰一下即可。另外有:1996年的一段代碼:http://www.fastcgi.com/om_archive/kit/cgi-fcgi/cgi-fcgi.c妻熊,和spawn-fcgi一個(gè)風(fēng)格
PHP-FPM
PHP-FPM是一個(gè)PHP FastCGI管理器夸浅,是只用于PHP的,可以在 http://php-fpm.org/download下載得到。PHP-FPM其實(shí)是PHP源代碼的一個(gè)補(bǔ)丁扔役,旨在將FastCGI進(jìn)程管理整合進(jìn)PHP包中帆喇。必須將它patch到你的PHP源代碼中,在編譯安裝PHP后才可以使用亿胸。FPM(FastCGI 進(jìn)程管理器)用于替換 PHP-CGI 的大部分附加功能坯钦,對于高負(fù)載網(wǎng)站是非常有用的预皇。它的功能包括:
- 支持平滑停止/啟動(dòng)的高級(jí)進(jìn)程管理功能;
- 可以工作于不同的 uid/gid/chroot 環(huán)境下婉刀,并監(jiān)聽不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的設(shè)置)吟温;
- stdout 和 stderr 日志記錄;
- 在發(fā)生意外情況的時(shí)候能夠重新啟動(dòng)并緩存被破壞的 opcode;
- 文件上傳優(yōu)化支持;
- “慢日志” – 記錄腳本(不僅記錄文件名,還記錄 PHP backtrace 信息突颊,可以使用 ptrace或者類似工具讀取和分析遠(yuǎn)程進(jìn)程的運(yùn)行數(shù)據(jù))運(yùn)行所導(dǎo)致的異常緩慢;
- fastcgi_finish_request() – 特殊功能:用于在請求完成和刷新數(shù)據(jù)后鲁豪,繼續(xù)在后臺(tái)執(zhí)行耗時(shí)的工作(錄入視頻轉(zhuǎn)換、統(tǒng)計(jì)處理等)律秃;
- 動(dòng)態(tài)/靜態(tài)子進(jìn)程產(chǎn)生爬橡;
- 基本 SAPI 運(yùn)行狀態(tài)信息(類似Apache的 mod_status);
- 基于 php.ini 的配置文件棒动。
WSGI
Web服務(wù)器網(wǎng)關(guān)接口(Python Web Server Gateway Interface糙申,縮寫為WSGI)是為Python語言定義的Web服務(wù)器和Web應(yīng)用程序或框架之間的一種簡單而通用的接口。自從WSGI被開發(fā)出來以后船惨,許多其它語言中也出現(xiàn)了類似接口郭宝。WSGI是作為Web服務(wù)器與Web應(yīng)用程序或應(yīng)用框架之間的一種低級(jí)別的接口,以提升可移植Web應(yīng)用開發(fā)的共同點(diǎn)掷漱。WSGI是基于現(xiàn)存的CGI標(biāo)準(zhǔn)而設(shè)計(jì)的粘室。
WSGI區(qū)分為兩個(gè)部份:一為“服務(wù)器”或“網(wǎng)關(guān)”,另一為“應(yīng)用程序”或“應(yīng)用框架”卜范。在處理一個(gè)WSGI請求時(shí)捂襟,服務(wù)器會(huì)為應(yīng)用程序提供環(huán)境資訊及一個(gè)回呼函數(shù)(Callback Function)盹憎。當(dāng)應(yīng)用程序完成處理請求后,透過前述的回呼函數(shù),將結(jié)果回傳給服務(wù)器哟忍。所謂的 WSGI 中間件同時(shí)實(shí)現(xiàn)了API的兩方宛篇,因此可以在WSGI服務(wù)和WSGI應(yīng)用之間起調(diào)解作用:從WSGI服務(wù)器的角度來說皆愉,中間件扮演應(yīng)用程序证杭,而從應(yīng)用程序的角度來說,中間件扮演服務(wù)器湾宙≌燎猓“中間件”組件可以執(zhí)行以下功能:
- 重寫環(huán)境變量后,根據(jù)目標(biāo)URL侠鳄,將請求消息路由到不同的應(yīng)用對象埠啃。
- 允許在一個(gè)進(jìn)程中同時(shí)運(yùn)行多個(gè)應(yīng)用程序或應(yīng)用框架。
- 負(fù)載均衡和遠(yuǎn)程處理伟恶,通過在網(wǎng)絡(luò)上轉(zhuǎn)發(fā)請求和響應(yīng)消息。
- 進(jìn)行內(nèi)容后處理潦牛,例如應(yīng)用XSLT樣式表巴碗。
以前,如何選擇合適的Web應(yīng)用程序框架成為困擾Python初學(xué)者的一個(gè)問題,這是因?yàn)槊鞴福话愣允醒剩琖eb應(yīng)用框架的選擇將限制可用的Web服務(wù)器的選擇施绎,反之亦然。那時(shí)的Python應(yīng)用程序通常是為CGI致稀,F(xiàn)astCGI俱尼,mod_python中的一個(gè)而設(shè)計(jì)遇八,甚至是為特定Web服務(wù)器的自定義的API接口而設(shè)計(jì)的。WSGI沒有官方的實(shí)現(xiàn), 因?yàn)閃SGI更像一個(gè)協(xié)議货矮。只要遵照這些協(xié)議,WSGI應(yīng)用(Application)都可以在任何服務(wù)器(Server)上運(yùn)行, 反之亦然囚玫。WSGI就是Python的CGI包裝读规,相對于Fastcgi是PHP的CGI包裝掖桦。
WSGI將 web 組件分為三類: web服務(wù)器,web中間件,web應(yīng)用程序涌穆, wsgi基本處理模式為 : WSGI Server -> (WSGI Middleware)* -> WSGI Application 宿稀。
1祝沸、WSGI Server/gateway
wsgi server可以理解為一個(gè)符合wsgi規(guī)范的web server,接收request請求奉狈,封裝一系列環(huán)境變量仁期,按照wsgi規(guī)范調(diào)用注冊的wsgi app跛蛋,最后將response返回給客戶端痊硕。文字很難解釋清楚wsgi server到底是什么東西岔绸,以及做些什么事情,最直觀的方式還是看wsgi server的實(shí)現(xiàn)代碼挡鞍。以python自帶的wsgiref為例预烙,wsgiref是按照wsgi規(guī)范實(shí)現(xiàn)的一個(gè)簡單wsgi server扁掸。它的代碼也不復(fù)雜谴分。
- 服務(wù)器創(chuàng)建socket牺蹄,監(jiān)聽端口,等待客戶端連接氓奈。
- 當(dāng)有請求來時(shí)舀奶,服務(wù)器解析客戶端信息放到環(huán)境變量environ中,并調(diào)用綁定的handler來處理請求但荤。
- handler解析這個(gè)http請求腹躁,將請求信息例如method潜慎,path等放到environ中蓖康。
- wsgi handler再將一些服務(wù)器端信息也放到environ中蒜焊,最后服務(wù)器信息科贬,客戶端信息榜掌,本次請求信息全部都保存到了環(huán)境變量environ中憎账。
- wsgi handler 調(diào)用注冊的wsgi app,并將environ和回調(diào)函數(shù)傳給wsgi app
- wsgi app 將reponse header/status/body 回傳給wsgi handler
- 最終handler還是通過socket將response信息塞回給客戶端邪意。
2雾鬼、WSGI Application
wsgi application就是一個(gè)普通的callable對象策菜,當(dāng)有請求到來時(shí)做入,wsgi server會(huì)調(diào)用這個(gè)wsgi app。這個(gè)對象接收兩個(gè)參數(shù)壶运,通常為environ,start_response浪秘。environ就像前面介紹的耸携,可以理解為環(huán)境變量夺衍,跟一次請求相關(guān)的所有信息都保存在了這個(gè)環(huán)境變量中,包括服務(wù)器信息河劝,客戶端信息赎瞎,請求信息务甥。start_response是一個(gè)callback函數(shù)喳篇,wsgi application通過調(diào)用start_response麸澜,將response headers/status 返回給wsgi server痰憎。此外這個(gè)wsgi app會(huì)return 一個(gè)iterator對象 铣耘,這個(gè)iterator就是response body蜗细。這么空講感覺很虛,對著下面這個(gè)簡單的例子看就明白很多了昆烁。
3静尼、WSGI MiddleWare
有些功能可能介于服務(wù)器程序和應(yīng)用程序之間鼠渺,例如拦盹,服務(wù)器拿到了客戶端請求的URL, 不同的URL需要交由不同的函數(shù)處理,這個(gè)功能叫做 URL Routing沼侣,這個(gè)功能就可以放在二者中間實(shí)現(xiàn)地熄,這個(gè)中間層就是 middleware端考。middleware對服務(wù)器程序和應(yīng)用是透明的,也就是說裂明,服務(wù)器程序以為它就是應(yīng)用程序闽晦,而應(yīng)用程序以為它就是服務(wù)器仙蛉。這就告訴我們荠瘪,middleware需要把自己偽裝成一個(gè)服務(wù)器哀墓,接受應(yīng)用程序后雷,調(diào)用它喷面,同時(shí)middleware還需要把自己偽裝成一個(gè)應(yīng)用程序惧辈,傳給服務(wù)器程序盒齿。
其實(shí)無論是服務(wù)器程序,middleware 還是應(yīng)用程序符匾,都在服務(wù)端瘩例,為客戶端提供服務(wù)焰坪,之所以把他們抽象成不同層,就是為了控制復(fù)雜度善绎,使得每一次都不太復(fù)雜炬守,各司其職驹止。
參考資料:
- http://blog.csdn.net/on_1y/article/details/18803563
- http://blog.kenshinx.me/blog/wsgi-research/
- http://blog.ez2learn.com/2010/01/27/introduction-to-wsgi/
uWSGI
uWSGI 項(xiàng)目旨在為部署分布式集群的網(wǎng)絡(luò)應(yīng)用開發(fā)一套完整的解決方案观蜗。uWSGI主要面向web及其標(biāo)準(zhǔn)服務(wù)抖仅,已經(jīng)成功的應(yīng)用于多種不同的語言。由于uWSGI的可擴(kuò)展架構(gòu),它能夠被無限制的擴(kuò)展用來支持更多的平臺(tái)和語言。目前惕澎,你可以使用C忍抽,C++和Objective-C來編寫插件唆阿。項(xiàng)目名稱中的“WSGI”是為了向同名的Python Web標(biāo)準(zhǔn)表示感謝,因?yàn)閃SGI為該項(xiàng)目開發(fā)了第一個(gè)插件阎姥。uWSGI是一個(gè)Web服務(wù)器呼巴,它實(shí)現(xiàn)了WSGI協(xié)議泽腮、uwsgi、http等協(xié)議衣赶。uWSGI诊赊,既不用wsgi協(xié)議也不用FastCGI協(xié)議,而是自創(chuàng)了一個(gè)uwsgi的協(xié)議府瞄,uwsgi協(xié)議是一個(gè)uWSGI服務(wù)器自有的協(xié)議碧磅,它用于定義傳輸信息的類型(type of information),每一個(gè)uwsgi packet前4byte為傳輸信息類型描述遵馆,它與WSGI相比是兩樣?xùn)|西鲸郊。據(jù)說該協(xié)議大約是fcgi協(xié)議的10倍那么快。
- uWSGI的主要特點(diǎn)如下:
- 超快的性能货邓。
- 低內(nèi)存占用(實(shí)測為apache2的mod_wsgi的一半左右)秆撮。
- 多app管理。
- 詳盡的日志功能(可以用來分析app性能和瓶頸)换况。
- 高度可定制(內(nèi)存大小限制像吻,服務(wù)一定次數(shù)后重啟等)。