WSGI, 全稱爲 Web Server Gateway Interface。python
它不是什麼框架,它是一個規範,可是做爲一個規範,它實際上並無官方標準。
這裏要說明一下,PEP-0333並非用來規範WSGI用得,而是PEP-0333提出應該有那麼一個東西用來處理web服務器和應用服務器之間的關係,所以產生了WSGI。nginx
由於,就目前來看,Web服務通常按照以下方式部署:web
先部署一個web服務器(Apache etc.)用於處理協議層的業務,好比,單臺物理機多服務(多域名或者多端口)、負載均衡等。apache
而後部署一個應用服務器(Django etc.),它用於處理具體業務方面的事情,好比,滴滴打車的應用服務器就會處理打車訂單CRUD,處理完成以後呢再返回給web服務器,web服務器收到響應以後再返回給客戶端。服務器
經過上文,咱們能夠了解到WSGI無非作了兩件事:app
讓Web服務器知道如何調用Python應用程序並把從客戶端來的請求拿過來。負載均衡
讓Python應用程序知道客戶端的具體請求是什麼,以及如何返回結果給Web服務器並幫助Python應用程序把計算後的結果返回給Web服務器。框架
也就是說,WSGI是鏈接Web服務器和應用服務器的橋樑。測試
目前實現的WSGI中,有兩個角色,分別是Server/Gateway和application/framework。設計
當請求來臨的時候,server
調用application
,而後application
把結果返回給server
。
接下來,server
須要知道去哪可以找到application
。實現這一邏輯,須要在server
指定一個Python模塊(具體在server的哪一個位置保存這一路徑,那就得根據具體server類型來選擇了,如apache或nginx)該模塊必須包含一個名稱爲application
的可調用對象,這個對象的形式以下:
在PEP-0333中指出了,一個WSGI的application角色,應該是一個可調用對象:
def application(environ, start_response)
其中,environ
是一個包含了關於此次HTTP請求信息的字典,start_response
是一個可調用對象,用於在application
中執行,用以在返回響應內容前設置響應的狀態碼和響應頭,同時也意味着告訴server
,application
要開始返回http的body了。這兩個就是server
調用application
時候須要傳遞的全部參數了。
那麼,咱們還須要再說一下,
environ
和start_response()
是須要在server
端的生成和定義的
有了這兩個參數,application
就能知道用戶請求的是什麼資源,請求中帶了什麼數據,結果該如何返回給server
等等。其中,environ
包含了一些符合CGI規範的環境變量和WSGI規範新添加的變量,此外還可能有一些系統變量及Web服務器相關的環境變量。start_response
是一個可調用對象,它包含了一個表示HTTP響應狀態的字符串和一個HTTP響應headers的列表以及一個用於出錯返回的信息,具體參數包含及詳情請點這裏。
下面是一個完整的application demo,最終返回的body也能夠是一個可迭代對象,這樣server
也能夠經過遍歷這個對象來拼接成body。
def application(environ, start_response): start_response("200 OK", [("Content-type", "text/plain")]) return ["Hello World!",]
看上去不錯,那麼咱們該如何調用呢?
有幾個服務器組件也可以運行的WSGI應用程序,可是對於簡單的測試目的,咱們可使用包含在Python的標準庫參考實現,就像下面那樣:
if __name__ == '__main__': from wsgiref.simple_server import make_server server = make_server('localhost', 8080, application) server.serve_forever()
好了,還記得咱們剛纔提到的最終返回的body也能夠是一個可迭代對象嗎?在python中,建立一個迭代器的簡單辦法就是使用一個生成器,好比,咱們有一個用於靜態文件的服務,咱們能夠寫一個生成器,讓它一次生成一個固定大小的文件塊,這樣咱們就能夠隨時一次只存儲一個文件塊了,讓咱們來試試吧:
def send_file(file_path, size): with open(file_path) as f: block = f.read(BLOCK_SIZE) while block: yield block block = f.read(BLOCK_SIZE)
對應的 WSGI application 部分以下:
size = os.path.getsize(file_path) headers = [ ("Content-type", mimetype), ("Content-length", str(size)), ] start_response("200 OK", headers) return send_file(file_path, size)
注意,send_file
就是上文中所指的可迭代對象。
看得出來,WSGI應用結構很是簡潔,只需指定一個可調用的識別標記,這使得WSGI應用很容易實現調用其餘框架,無非就是修改到來的請求或者修改發出的響應,固然也能夠二者都有。那麼,接下來看看demo:
class Filter(object): def __init__(self, application): self.application = application def __call__(self, environ, start_response): # Do something here to modify request pass # Call the wrapped application app_iter = self.application(environ, self._sr_callback(start_response)) # Do something to modify the response body pass # Return modified response return app_iter def _sr_callback(self, start_response): def callback(status, headers, exc_info=None): # Do something to modify the response status or headers pass # Call upstream start_response start_response(status, headers, exc_info) return callback
像這樣應用一般被稱爲Middleware applications或Filter。 Filter能夠被鏈接在一塊兒,由此產生的鏈一般被稱爲pipeline。
最後呢,我想說明一下,WSGI是故意設計成最小的Web服務器實現輕鬆的應用,以便被更多的人採用。可是,幾乎沒有人真的喜歡直接操做environ
變量,也幾乎沒有人喜歡用start_response
這麼詭異的邏輯,雖然WSGI提供的API易於實現,但這不表明它的語義讓人滿意。也正是由於這一緣由,幾乎每個應用程序或者web框架都把environ
和start_response
封裝成了語義更完善容錯率更高的request和response對象。
Webob就是request和response對象的規範實現之一,它使得WSGI更容易和更滿意地被你們使用。
你能夠在這裏找到Webob的官方文檔,固然,下次有機會我也會簡單的說明一下Webob到底有多方便。