1. 程式人生 > >CGI 與 WSGI

CGI 與 WSGI

1.
首先,CGI是外部應用程式與 Web 伺服器之間的介面標準,不同型別語言寫的程式只要符合 CGI 標準,就能作為一個 CGI 程式與 Web 伺服器互動,早期的 CGI 大多都是c或c++。php可以作為一個 CGI 使用,作為 CGI 時,它將作為獨立的程序執行,有請求過來就建立一個程序進行響應。當php作為apache的一個模組時,php將作為apache的一個子程序存在,接受apache呼叫。現在也有了php-fpm+nginx的組合,php-fpm是一個fast-cgi管理器,負責將接收到的請求分配給預先生成的php子程式,管理著php程序。


2.
CGI不是一種語言,也不是一種技術,而是一種模式。
CGI 的定義 Common Gateway Interface,“通用閘道器介面”,簡稱CGI。在物理上是一段程式,執行在伺服器上,提供同客戶端 HTML頁面的介面
換句話,只要是提供HTML的伺服器端程式都可以叫CGI,APS、PHP、JSP這些都是,你用C語言寫一個可以提供HTML的伺服器端EXE檔案,也叫CGI。


3.
PHP 網站部署很多都是通過 Nginx + FastCGI 方式
以下敘述僅限於是狹義上的 CGI,即不包含 FastCGI。
對一個 CGI 程式,做的工作其實只有:從環境變數(environment variables)和標準輸入(standard input)中讀取資料、處理資料、向標準輸出(standard output)輸出資料。
環境變數中儲存的叫 Request Meta-Variables,也就是諸如 QUERY_STRING、PATH_INFO 之類的東西,這些是由 Web Server 通過環境變數傳遞給 CGI 程式的,CGI 程式也是從環境變數中讀取的。
標準輸入中存放的往往是使用者通過 PUTS 或者 POST 提交的資料,這些資料也是由 Web Server 傳過來的。
就比如,我們剛學 C 語言時寫的 Hello World,也可以作為一個合法的 CGI 程式。
現在用 CGI 的已經很少了,因為每個 CGI 程序只處理一個請求,換句話說,每個請求都需要建立一個 CGI 程序處理,CGI 程式處理完畢後就退出了。
FastCGI 正是對 CGI 的改進,而且改進了不是一點點。從總體上看,一個 FastCGI 程序可以處理若干請求(一般 FastCGI 程序是駐留著的,但不排除 IIS 之類的 Web Server 限制其空閒時間,在一段時間內沒有請求就自動退出的可能),Web Server 或者 fpm 會控制 FastCGI 程序的數量。
細節方面,FastCGI 是一套協議,不再是通過簡單的環境變數、標準輸入和標準輸出來接收和傳遞資料了。一般來說,FastCGI 用 TCP 或者命名管道(Named Pipe)傳輸資料。現在絕大多數 PHP 網站都是在用 FastCGI 的。


4.
現在以CGI方式執行的伺服器應該已經沒有了,PHP是FastCGI,也是這個協議還能續到今天的最主要的原因。Servlet和Python的WSGI可以類比,它是一種程式設計介面,通過這種程式設計介面可以將Web Server和Web應用清晰地劃分開界限,每個進入的請求將會呼叫一次Servlet的介面,這樣Web應用就無需關心Web Server使用了多執行緒還是多程序還是多路複用等技術細節,而只需要實現這個介面就行了。既然是程式設計介面,那麼肯定是跟程式語言有關的,Servlet就只能用在Java中,其他語言是不能用Servlet程式設計的(除非有辦法做到和Java相容)
FastCGI是一種網路協議,它是工作在socket上、而與語言無關的。以前的CGI一般來說是每個請求進來時,從server fork出一個程序,僅僅處理這一個請求,處理完成就退出,處理的過程是從環境變數中獲取HTTP頭,從標準輸入中讀取POST資料,從標準輸出中輸出HTTP響應。由於需要不停地建立和銷燬程序,這種實現方式效能是比較低下的,功能也受到許多限制。FastCGI是CGI的一種改進,它在單個連線上接受連續的多個請求,一個一個進行處理,這樣就提高了吞吐量。FastCGI和CGI一樣與語言無關,任何語言只要遵循FastCGI的協議就可以使用FastCGI;但是它的處理模型是受限的,通常在單個連線上一次只能處理一個請求,這樣要達到一定的併發度,就必須建立許多程序(FastCGI也有multiplexing的功能,但不知道支援的如何)
但是其實從Web應用的角度來看,兩者的區別並不大,PHP雖然使用FastCGI,但也是對每個請求呼叫一次PHP指令碼。雖然許多PHP程式設計師喜歡把網頁和程式碼混在一起弄得很難維護,但PHP也是支援使用OOP的方式,像Java一樣使用MVC的;Java也同樣支援JSP這樣的可以嵌入程式碼的模板。區別有一些,比如Java在同一個程序中工作,可以呼叫全域性物件,可以使用額外的執行緒池等等,而PHP的物件的生命週期基本限制在單個請求範圍內之類,但許多東西還是類似的,都是面向Request進行程式設計。


5.
一個HTTP請求到達一臺伺服器的80埠的時候,需要有個程式來響應這個請求。所謂HTTP response,其實只是執行一個程式,它的輸入是HTTP request header,它的返回是HTTP response。程式執行方式的區別、HTTP request header的傳遞方式區別就是CGI、WSGI以及其他各種GI的區別。
如果是CGI,通常來說是一個web server(例如apache、nginx)接收到請求後,將請求中的HTTP request header按照一定規則設定成環境變數,然後啟動一個程式(Python指令碼),將stdout的輸出(其中包含HTTP response header)當成HTTP response返回給客戶端。
如果是Django跑在uWSGi、unicorn之類的容器裡,那麼程式是一個常駐程序,web server和Python程序用WSGI協議傳遞HTTP request header資訊,然後返回給使用者。
如果是Django的dev server,它使用Python自帶的wsgiref模組實現了一個簡單的HTTP server (https://github.com/django/django/blob/master/django/core/servers/basehttp.py),響應HTTP請求。


6.
Tornado: HTTP 伺服器、WSGI 框架(很少用)
CGI: 古老的動態 HTTP 服務方式,極其低效、容易出漏洞。Perl 盛行的時代很常用。
WSGI: Python 的 HTTP 介面協議
FastCGI: PHP 等的介面協議。PHP 也是唯一一個把 FastCGI 實現得像 CGI 的 FastCGI 實現(HTTP 頭可通過環境變數訪問)
uwsgi: 它是一個軟體。是 WSGI、PSGI 等等的容器。也就是它實現了這些協議。同樣實現了 WSGI 的還有 gunicorn 之類的。
nginx: 可以作為代理,把 HTTP 傳給 Tornado。也可以作為閘道器,把 HTTP 轉成 FastCGI、uwsgi 協議傳給後邊的程式。


server 和 application 直接按 WSGI 的約定呼叫,它們在一個程序內。
uwsgi 是 nginx 和 uwsgi 之間的通訊協議。


5.
Tornado可以當作HTTP server,直接TCP開始實現HTTP服務,這也就是為啥說Tornado可以不經過WSGI。實際上它也不是CGI,CGI是指通過stdin和stdout進行HTTP的請求處理,WSGI則是包裹成一個Python物件來傳遞請求和響應。
WSGI是寫入PEP的python的HTTP介面,然而缺點是這個藉口是完全同步的,Tornado最大的特點是可以非同步處理請求,然而如果使用WSGI,那就不能做到了。官方文件明確指出,將Tornado應用作為WSGI應用時非同步介面全部不可用。
相反,Tornado可以作為HTTP server向其它框架提供WSGI容器,所以可以用Tornado來做apache/nginx/guicorn之類的事情。
所以對Tornado而言,如果還要使用nginx之類做前置,正確的姿勢應當是通過反向代理來做負載均衡。


6.
作者:靈劍
連結:https://www.zhihu.com/question/46945479/answer/104066078


WSGI是一種程式設計介面,而uwsgi是一種傳輸協議,從作用上來講,的確跟fastcgi是最接近的。跟fastcgi的區別在於它是面向多併發的。我們知道fastcgi是CGI的替代品,它的工作方式仍然跟CGI是類似的,當一個請求進入的時候,通過socket傳送請求的環境變數,然後傳送POST資料(相當於CGI的stdin),然後等待程式輸出(相當於CGI的stdout),等輸出結束後,再發送下一個請求。這就導致fastcgi最大的併發量被限制為fastcgi後端的數量,顯然這樣的伺服器模式對於併發量很大、單個請求耗時比較長的服務是不合適的,因此很多時候我們不願意使用fastcgi部屬而是使用反向代理的方式配置。
但是跟反向代理比起來,fastcgi顯然也是有好處的,最重要的好處在於解析HTTP協議的部分被offload到了前端伺服器一級,後端伺服器不再解析HTTP協議,這樣就減輕了後端的壓力,由於前端是nginx這樣用C/C++高效能實現的伺服器,比起在後端的Python當中使用指令碼語言解析HTTP協議,效率要高不少。
uwsgi想要繼承fastcgi的這種好處,它通過將訊息分片的方式,可以在一個socket上併發傳輸多個請求,這樣就解決了一個連線上一次只能傳輸一個請求的問題。熟悉HTTP2.0的話會發現這個分片機制跟HTTP2.0很像。