年薪20萬Python工程師進階(3):Python開發(fā)之理解WSGI(上)

本文目錄


什么是WSGI?

如何實現(xiàn)Application?

如何實現(xiàn)Web Server?

Web Server如何決擇?

.?什么是WSGI?

WSGI(Web Server Gateway Interface),顧名思義,它既不是服務(wù)器,也不是應(yīng)用,而是一種接口(規(guī)范),描述web server如何與web application通信的規(guī)范。

那么這個規(guī)范是什么?

服務(wù)器的請求處理要調(diào)用符合WSGI規(guī)范的網(wǎng)關(guān)接口;

由網(wǎng)關(guān)接口來調(diào)用應(yīng)用程序,并且其要定義start_response(status, headers)函數(shù),用于返回響應(yīng);

應(yīng)用程序須是一個可調(diào)用對象(函數(shù)/類),webapp(environ, start_response)。接受兩個參數(shù),environ是環(huán)境設(shè)置的字典,由服務(wù)器和WSGI網(wǎng)關(guān)接口設(shè)置,start_response是由網(wǎng)關(guān)接口定義的函數(shù)。

在這個規(guī)范中,有三個角色

web server:實現(xiàn)了WSGI server協(xié)議的服務(wù)器

Gateway Interface:網(wǎng)關(guān)接口

application:實現(xiàn)了WSGI application協(xié)議的框架

常見的web server有uWSGI、Gunicorn

常見的?Gateway Interface?有CGI,?WSGI

常見的application框架有?Django、Flask

為什么要有WSGI規(guī)范?

網(wǎng)絡(luò)通信的完整流程,是這樣的

先創(chuàng)建一個web服務(wù)器,監(jiān)聽端口,接收請求,并將請求路由轉(zhuǎn)發(fā)給對應(yīng)的應(yīng)用程序。

再創(chuàng)建一個web應(yīng)用程序,用于接收到請求,經(jīng)過必要的處理,返回響應(yīng)給服務(wù)器。

服務(wù)器接收響應(yīng),返回給客戶端(瀏覽器)

試想一下,假如沒有這個規(guī)范,此時我們想開發(fā)一個網(wǎng)頁,我們需要做的就是,先搭建一個服務(wù)器,用于監(jiān)聽端口,接收請求,再來創(chuàng)建一個用于接收請求的應(yīng)用程序。

聽起來好像沒什么問題。但是你今天開發(fā)一個網(wǎng)站,要寫一個server,明天又要開發(fā)一個網(wǎng)站,又要寫一個server。全中國多少web開發(fā)人員,如果按照這種模式下去,開發(fā)效率可想而知,嚴重浪費時間人力。

WSGI 就是來解決這個問題的,它解耦了服務(wù)器類與應(yīng)用程序類。

意思就是,假如服務(wù)器類和應(yīng)用程序類都嚴格遵守WSGI規(guī)范,那么應(yīng)用程序A可以隨便挑一個現(xiàn)成的服務(wù)器類(B,C,E都可以)來使用,而不需要其他任何的修改,只需要提供一個可以處理這些應(yīng)用的請求處理類即可,不用擔(dān)心兼容問題。

我們的主人公WSGI?,在服務(wù)器和應(yīng)用中間承擔(dān)一個“翻譯官”的角色。只要應(yīng)用程序符合網(wǎng)關(guān)接口的標準,那么服務(wù)器就只要做好服務(wù)器的角色,應(yīng)用程序只要做好應(yīng)用程序的作用,服務(wù)器和應(yīng)用程序之間的通信全靠網(wǎng)關(guān)接口來協(xié)調(diào)。

.?如何實現(xiàn)Application?

WSGI規(guī)范 規(guī)定了,Application 必須是一個可調(diào)用的對象,它可以是函數(shù),可以實現(xiàn)了__call__的類的實例對象,也可以是實現(xiàn)了__iter__的類對象。

不管是哪種方式的可調(diào)用對象,都要遵循兩個原則

必須接收environ,?start_response兩個參數(shù);

必須返回?可迭代的對象。

下面來分別看下這三個例子。

application是函數(shù)

1defapplication(environ, start_response):

2

3response_body ='The request method was %s'% environ['REQUEST_METHOD']

4status ='200 OK'

5

6# 應(yīng)答的頭部是一個列表,每對鍵值都必須是一個 tuple。

7response_headers = [('Content-Type','text/plain'),

8('Content-Length', str(len(response_body)))]

9

10# 調(diào)用服務(wù)器程序提供的 start_response,填入兩個參數(shù)

11start_response(status, response_headers)

12

13# 返回必須是 iterable

14return[response_body]

實現(xiàn)了__call__的類的實例對象

1classAppClass:

2"""這里的可調(diào)用對象就是 AppClass 的實例,使用方法類似于:

3app = AppClass()

4for result in app(environ, start_response):

5do_somthing(result)

6"""

7

8def__init__(self):

9pass

10

11def__call__(self, environ, start_response):

12status ='200 OK'

13response_headers = [('Content-type','text/plain')]

14self.start(status, response_headers)

15yield"Hello world!\n"

實現(xiàn)了__iter__的類對象

1classAppClass:

2"""這里的可調(diào)用對象就是 AppClass 這個類,調(diào)用它就能生成可以迭代的結(jié)果。

3使用方法類似于:

4for result in AppClass(env, start_response):

5do_somthing(result)

6"""

7

8def__init__(self, environ, start_response):

9self.environ = environ

10self.start = start_response

11

12def__iter__(self):

13status ='200 OK'

14response_headers = [('Content-type','text/plain')]

15self.start(status, response_headers)

16yield"Hello world!\n"

.?如何實現(xiàn)Web Server?

上文說到,application?必須接收environ,start_response兩個參數(shù)。

這兩個參數(shù)是什么意思?

environ,:是 WSGI的環(huán)境信息。

start_response:是響應(yīng)請求的函數(shù)。

其中start_response接收兩個參數(shù),

status:HTTP狀態(tài),譬如:"200 OK"

response_headers:響應(yīng)消息的頭,譬如:[('Content-Type', 'text/plain')],以list的形式,每個元素是一個tuple,而一個tuple里有兩個元素,一個是key,一個是value。

這兩個參數(shù)(environ,start_response),都是由Web Server來定義的。

所以我們要自己實現(xiàn)Web Server,也必須實現(xiàn)這兩個對象。定義完后,要調(diào)用application,將這兩個參數(shù)傳入。這是規(guī)定。

1importos, sys

2

3defweb_server(application):

4# 構(gòu)造environ 參數(shù)

5environ = dict(os.environ.items())

6environ['wsgi.input'] = sys.stdin

7environ['wsgi.errors'] = sys.stderr

8environ['wsgi.version'] = (1,0)

9environ['wsgi.multithread'] =False

10environ['wsgi.multiprocess'] =True

11environ['wsgi.run_once'] =True

12environ['wsgi.url_scheme'] ='http'

13

14headers_set = []

15

16# 定義響應(yīng)函數(shù)

17defstart_response(status, response_headers, exc_info=None):

18headers_set[:] = [status, response_headers]

19

20# 調(diào)用application,并傳入?yún)?shù)

21result = application(environ, start_response)

22

23# 用for循環(huán),就解釋了為什么application要返回可迭代對象

24fordatainresult:

25ifdata:

26print(data)

好啦。這里只是簡單舉個例子。

到了現(xiàn)在,誰也沒必要去重要寫web server了,使用Python最忌諱的就是重復(fù)造輪子。那是傻。

.?Web Server如何決擇?

首先要明白的是,生產(chǎn)環(huán)境和開發(fā)環(huán)境使用的Web Server是不一樣的。

就拿Django來說,其自帶的Web Server有如下局限性

低性能:運行起來,只有一個實例,性能可見一斑。

低可用:做為服務(wù)啟動,只要某個地方ERROR,服務(wù)就掛掉了。

自帶server只有在debug模式下可用映射靜態(tài)文件,而debug模式下運行會不斷留存debug信息,跑久了內(nèi)存要爆。

如此看來,Django自帶的server只能用于開發(fā)調(diào)試,并不適合用于生產(chǎn)環(huán)境。

就連Django官方也是這么說的。

It’s intended only for use while developing. (We’re in the business of making Web frameworks, not Web servers.)

意思是說,Django是一個專業(yè)的應(yīng)用程序端框架,并不擅長于服務(wù)端。

果然,專業(yè)的事還是得依靠專業(yè)的軟件來做。

當前市面上,已經(jīng)出現(xiàn)了很多專業(yè)且優(yōu)秀的Web Server,這里也介紹一下。

Gunicorn

Gunicorn(從Ruby下面的Unicorn得到的啟發(fā))應(yīng)運而生:依賴Nginx的代理行為,同Nginx進行功能上的分離。由于不需要直接處理用戶來的請求(都被Nginx先處理),Gunicorn不需要完成相關(guān)的功能,其內(nèi)部邏輯非常簡單:接受從Nginx來的動態(tài)請求,處理完之后返回給Nginx,由后者返回給用戶。

由于功能定位很明確,Gunicorn得以用純Python開發(fā):大大縮短了開發(fā)時間的同時,性能上也不會很掉鏈子。同時,它也可以配合Nginx的代理之外的別的Proxy模塊工作,其配置也相應(yīng)比較簡單。

配置上的簡單,大概是它流行的最大的原因。

uWSGI

因為使用C語言開發(fā),會和底層接觸的更好,配置也是比較方便,目前和gunicorn兩個算是部署時的唯二之選。

bjoern

Python WSGI界最牛逼性能的Server其中一個是bjoern,純C,小于1000行代碼,就是看不慣uWSGI的冗余自寫的。

介紹完了,那么如何選擇呢?

綜合網(wǎng)友們的回答,整理如下:

Gunicorn,配置簡單,快速上手,阻塞較多建議選擇

uWSGI,首次配置麻煩,性能較好

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容