本文目錄
什么是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,首次配置麻煩,性能較好
