在學習Python web開發時候,可能會遇到諸如uwsgi,wsgi等名詞,下面通過梳理總結探究它們之間的關系。
CGI
CGI,(Common Gateway Interface)通用網關接口,是一個協議,是外部應用程序(CGI程序)與WEB服務器之間的接口標准,該協議定義了Web服務器調用外部應用程序的時候需要輸入的參數和給Web服務器的返回結果。通俗來說,規定一個程序該如何與web服務器程序之間通信,從而可以讓這個程序跑在web服務器上
起源
最早的Web服務器簡單地響應瀏覽器發來的HTTP請求,並將存儲在服務器上的HTML文件返回給瀏覽器,也就是靜態html。這個場景下的服務器一般被稱為HTTP服務器,常見的有Apache的httpd和Nginx
事物總是不 斷發展,網站也越來越復雜,所以出現動態技術。但是服務器並不能直接運行 php,asp這樣的文件,自己不能做,外包給別人吧,但是要與第三做個約定,我給你什么,然后你給我什么,就是握把請求參數發送給你,然后我接收你的處 理結果給客戶端。那這個約定就是 common gateway interface,簡稱cgi。這個協議可以用vb,c,php,python 來實現。cgi只是接口協議,根本不是什么語言。
引入 CGI 以便客戶端請求能夠觸發 Web 服務器運行另一個外部程序,客戶端所輸入的數據也會傳給這個外部程序,該程序運行結束后會將生成的 HTML 和其他數據通過 Web 服務器再返回給客戶端(即動態請求,比如基於 PHP、Python、Java 實現的應用)。利用 CGI 可以針對用戶請求動態返回給客戶端各種各樣動態變化的信息
工作原理
Web服務器與CGI程序的交互
WEB服務器將根據CGI程序的類型決定數據向CGI程序的傳送方式,一般是通過標准輸入/輸出流和環境變量來與CGI程序間傳遞數據。 如下圖所示:
CGI程序通過標准輸入(STDIN)和標准輸出(STDOUT)來進行輸入輸出。此外CGI程序還通過環境變量來得到輸入,操作系統提供了許多環境變量,它們定義了程序的執行環境,應用程序可以存取它們。Web服務器和CGI接口又另外設置了一些環境變量,用來向CGI程序傳遞一些重要的參數。
**
常用CGI環境變量:
變量名 | 描述 |
---|---|
CONTENT_TYPE | 這個環境變量的值指示所傳遞來的信息的MIME類型。目前,環境變量CONTENT_TYPE一般都是:application/x-www-form-urlencoded,他表示數據來自於HTML表單。 |
CONTENT_LENGTH | 如果服務器與CGI程序信息的傳遞方式是POST,這個環境變量即使從標准輸入STDIN中可以讀到的有效數據的字節數。這個環境變量在讀取所輸入的數據時必須使用。 |
HTTP_COOKIE | 客戶機內的 COOKIE 內容。 |
HTTP_USER_AGENT | 提供包含了版本數或其他專有數據的客戶瀏覽器信息。 |
PATH_INFO | 這個環境變量的值表示緊接在CGI程序名之后的其他路徑信息。它常常作為CGI程序的參數出現。 |
QUERY_STRING | 如果服務器與CGI程序信息的傳遞方式是GET,這個環境變量的值即使所傳遞的信息。這個信息經跟在CGI程序名的后面,兩者中間用一個問號’?’分隔。 |
REMOTE_ADDR | 這個環境變量的值是發送請求的客戶機的IP地址,例如上面的192.168.1.67。這個值總是存在的。而且它是Web客戶機需要提供給Web服務器的唯一標識,可以在CGI程序中用它來區分不同的Web客戶機。 |
REMOTE_HOST | 這個環境變量的值包含發送CGI請求的客戶機的主機名。如果不支持你想查詢,則無需定義此環境變量。 |
REQUEST_METHOD | 提供腳本被調用的方法。對於使用 HTTP/1.0 協議的腳本,僅 GET 和 POST 有意義。 |
SCRIPT_FILENAME | CGI腳本的完整路徑 |
SCRIPT_NAME | CGI腳本的的名稱 |
SERVER_NAME | 這是你的 WEB 服務器的主機名、別名或IP地址。 |
SERVER_SOFTWARE | 這個環境變量的值包含了調用CGI程序的HTTP服務器的名稱和版本號。例如,上面的值為Apache/2.2.14(Unix) |
每當客戶請求CGI的時候,WEB服務器就請求操作系統生成一個新的CGI解釋器進程(如php-cgi.exe),CGI 的一個進程則處理完一個請求后退出,下一個請求來時再創建新進程。當然,這樣在訪問量很少沒有並發的情況也行。但當訪問量增大,並發存在,這種方式就不適合了,於是就有了FastCGI
FastCGI
FASTCGI是Web服務器(ex:nginx)和語言解釋器(ex:uWsgi)兩者底層的通信協議的規范,是對CGI的開放的擴展。
CGI的一個擴展,像是一個常駐(long-live)型的CGI ,廢除了 CGI fork-and-execute (來一個請求 fork 一個新進程處理,處理完再把進程 kill 掉)的工作方式,轉而使用一種長生存期的方法,減少了進程消耗,提升了性能。
而FastCGI 則會先 fork 一個 master 進程,解析配置文件,初始化執行環境,然后再 fork 多個 worker 進程(與 Nginx 有點像),當 HTTP 請求過來時,master 進程將其會傳遞給一個 worker 進程,然后立即可以接受下一個請求,這樣就避免了重復的初始化操作,效率自然也就提高了。而且當 worker 進程不夠用時,master 進程還可以根據配置預先啟動幾個 worker 進程等着;當空閑 worker 進程太多時,也會關掉一些,這樣不僅提高了性能,還節約了系統資源
php-fpm
FastCGI 只是一個協議規范,需要每個語言具體去實現,PHP-FPM 就是 PHP 版本的 FastCGI 協議實現,有了它,就是實現 PHP 腳本與 Web 服務器(通常是 Nginx)之間的通信,同時它也是一個 PHP SAPI,從而構建起 PHP 解釋器與 Web 服務器之間的橋梁
Php-fpm全稱是php fastcgi process manager即php fastcgi進程管理器,相比fastcgi靜態的喚起cgi,fpm能根據訪問的壓力動態的喚起cgi進程和銷毀以到達動態的調整cgi數量,這樣可以有效的使用內存。除此之外還有其它的一些優點,比如,fpm還可以平滑的重載php配置;由於fpm是使用Unix-Socket來和服務器通訊,所以也不用再配置cgi端口;fpm有更好的狀態輸出和slowlog日志,502的時候能給出更多的錯誤細節。
PHP-FPM 負責管理一個進程池來處理來自 Web 服務器的 HTTP 動態請求,在 PHP-FPM 中,master 進程負責與 Web 服務器進行通信,接收 HTTP 請求,再將請求轉發給 worker 進程進行處理,worker 進程主要負責動態執行 PHP 代碼,處理完成后,將處理結果返回給 Web 服務器,再由 Web 服務器將結果發送給客戶端。這就是 PHP-FPM 的基本工作原理
WSGI / uwsgi / uWSGI
在python web開發中,我們經常使用uwsgi配合nginx部署一個web框架,如Django或flask。同時我們又會說,框架和web服務器之間要符合WSGI協議
那就來厘清一下這幾個概念。
web服務器和web框架
在講uWSGI和WSGI之前,先要弄清楚web開發的兩大塊,web服務器和web框架。
web服務器即用來接受客戶端請求,建立連接,轉發響應的程序。至於轉發的內容是什么,交由web框架來處理,即處理這些業務邏輯。如查詢數據庫、生成實時信息等。Nginx就是一個web服務器,Django或flask就是web框架。
那么如何實現uWSGI和WSGI的配合呢?如何做到任意一個web服務器,都能搭配任意一個框架呢?這就產生了WSGI協議。只要web服務器和web框架滿足WSGI協議,它們就能相互搭配。所以WSGI只是一個協議,一個約定。而不是python的模塊、框架等具體的功能。
而uWSGI,則是實現了WSGI協議的一個web服務器。即用來接受客戶端請求,轉發響應的程序。實際上,一個uWSGI的web服務器,再加上Django這樣的web框架,就已經可以實現網站的功能了。
WSGI
WSGI,(WEB SERVER GATEWAY INTERFACE),Web服務器網關接口,是一種Web服務器網關接口,它是一個Web服務器(如Nginx,uWSGI等服務器)與web應用(如Flask框架寫的程序)通信的一種規范。當前運行在WSGI協議之上的Web框架有Bottle,Flask,Django
實現了python web程序與服務器之間交互的通用性。有了這個東西,web.py或者bottle或者django等等的python web開發框架,就可以輕松地部署在不同的web server上了,不需要做任何特殊配置(也需要一些小小的配置調整)
WSGI協議其實是定義了一種server與application解耦的規范,即可以有多個實現WSGI server的服務器,也可以有多個實現WSGI application的框架,那么就可以選擇任意的server和application組合實現自己的web應用。例如 uWSGI和 Gunicorn都是實現了WSGI server協議的服務器,Django,Flask是實現了WSGI application協議的web框架,可以根據項目實際情況搭配使用。
像Django,Flask框架都有自己實現的簡單的WSGI server,一般用於服務器調試,生產環境下建議用其他WSGI server,WSGI服務器的選擇很多,包括uWSGI和gunicorn
uwsgi
同WSGI一樣是一種通信協議
uwsgi協議是一個uWSGI服務器自有的協議,它用於定義傳輸信息的類型(type of information),每一個uwsgi packet前4byte為傳輸信息類型描述,它與WSGI相比是兩樣東西。
uWSGI (服務器)
它是一個Web服務器,它實現了WSGI協議、uwsgi、http等協議。用於接收前端服務器轉發的動態請求並處理后發給 web 應用程序。
因為apache也好,nginx也罷,它們自己都沒有解析動態語言如php的功能,而是分派給其他模塊來做,比如apache就可以說內置了php模塊,支持的非常爽,讓人感覺好像apache就支持php一樣。uwsgi實現了WSGI協議、uwsgi、http等協議。 Nginx中HttpUwsgiModule的作用是與uWSGI服務器進行交換。
uWSGI是使用C編寫的,顯示了自有的uwsgi協議的Web服務器。它自帶豐富的組件,其中核心組件包含進程管理、監控、IPC等功能,實現應用服務器接口的請求插件支持多種語言和平台,比如WSGI、Rack、Lua WSAPI,網管組件實現了負載均衡、代理和理由功能
uWSGI也可以當做中間件。
- 如果是Nginx+uWSGI+App,那uWSGI就是一個中間件
- 如果是uWSGI+App,那它就是服務器
Nginx+uWGSI
假設我們使用 python 的 Django 框架寫了一個網站,現在要將它掛在網上運行,我們一般需要:
- Nginx 做為代理服務器:負責靜態資源發送(js、css、圖片等)、動態請求轉發以及結果的回復。
- uWSGI 做為后端服務器:負責接收 Nginx 轉發的請求並處理后發給 Django 應用以及接收 Django 應用返回信息轉發給 Nginx。
- Django 應用收到請求后處理數據並渲染相應的返回頁面給 uWSGI 服務器。
一個Django應用,通過WSGI協議連接uWSGI服務器,uWSGI服務器實現WSGI、http等協議,通過uwsgi協議和Nginx服務器實現http的動態請求和轉發以及結果
問題:有uWGSI了Django為什么還需要Nginx?
一個普通的個人網站,訪問量不大的話,當然可以由uWSGI和Django構成。但是一旦訪問量過大,客戶端請求連接就要進行長時間的等待。這個時候就出來了分布式服務器,我們可以多來幾台web服務器,都能處理請求。但是誰來分配客戶端的請求連接和web服務器呢?Nginx就是這樣一個管家的存在,由它來分配。這也就是由Nginx實現反向代理,即代理服務器。
Nginx是一個HTTP和反向代理服務器
-
正向代理
正向的就是由瀏覽器主動的想代理服務器發出請求,經代理服務器做出處理后再轉給目標服務器 -
反向代理
反向的就是不管瀏覽器同不同意,請求都會經過代理服務器處理再發給目標服務器
使用Nginx作為反向代理服務器的好處:
- 安全
不管什么請求都要經過代理服務器,可以避免外部程序直接攻擊Web服務器
- 負載均衡
根據請求情況和服務器負載情況,將請求分配給不同的Web服務器,保證服務器性能
- 提高Web服務器的IO性能
請求從客戶端傳到Web服務器是需要時間的,傳遞多長時間就會讓這個進程阻塞多長時間,而通過反向代理,就可以由反向代理完整接受該請求,然后再傳給Web服務器,從而保證服務器性能,而且有的一些簡單的事情(比如靜態文件)可以直接由反向代理處理,不經過Web服務器
總結
- WSGI是一種通信協議
- uwsgi是一種通信協議,常用於在uWSGI服務器與其他網絡服務器的數據通信
- 而uWSGI是實現了uwsgi和WSGI兩種協議的Web服務器
百度百科上說uwsgi是一種線路協議而不是通信協議,個人更傾向於uwsgi是類似WSGI的通信協議的說法,uwsgi和WSGI都是基於CGI擴展出來的。
ASGI
異步網關協議接口,一個介於網絡協議服務和Python應用之間的標准接口,能夠處理多種通用的協議類型,包括HTTP,HTTP2和WebSocket。
然而目前的常用的WSGI主要是針對HTTP風格的請求響應模型做的設計,並且越來越多的不遵循這種模式的協議逐漸成為Web變成的標准之一,例如WebSocket。
ASGI嘗試保持在一個簡單的應用接口的前提下,提供允許數據能夠在任意的時候、被任意應用進程發送和接受的抽象。並且同樣描述了一個新的,兼容HTTP請求響應以及WebSocket數據幀的序列格式。允許這些協議能通過網絡或本地socket進行傳輸,以及讓不同的協議被分配到不同的進程中。
WSGI和ASGI的區別
WSGI是基於HTTP協議模式的,不支持WebSocket,而ASGI的誕生則是為了解決Python常用的WSGI不支持當前Web開發中的一些新的協議標准。同時,ASGI對於WSGI原有的模式的支持和WebSocket的擴展,即ASGI是WSGI的擴展。
參考
https://www.cnblogs.com/wanghetao/p/3934350.html
https://baike.baidu.com/item/fastcgi/10880685
https://www.jianshu.com/p/679dee0a4193
https://baijiahao.baidu.com/s?id=1590941335729952485&wfr=spider&for=pc
https://blog.csdn.net/qq_35318838/article/details/61198183