WSGI簡介

WSGI是什麼

WSGI的全稱是Web Server Gateway Interface,翻譯過來就是Web服務器網關接口。具體的來講,WSGI是一個規範,定義了Web服務器如何與Python應用程序進行交互,使得使用Python寫的Web應用程序能夠和Web服務器對接起來。WSGI一開始是在PEP-0333中定義的,最新版本是在Python的PEP-3333定義的。python

對於初學者來講,上面那段就是廢話,說了跟沒說同樣。本文的主要內容就是說清楚,WSGI究竟是如何工做的。nginx

爲何須要WSGI這個規範

在Web部署的方案上,有一個方案是目前應用最普遍的:web

  • 首先,部署一個Web服務器專門用來處理HTTP協議層面相關的事情,好比如何在一個物理機上提供多個不一樣的Web服務(單IP多域名,單IP多端口等)這種事情。瀏覽器

  • 而後,部署一個用各類語言編寫(Java, PHP, Python, Ruby等)的應用程序,這個應用程序會從Web服務器上接收客戶端的請求,處理完成後,再返回響應給Web服務器,最後由Web服務器返回給客戶端。服務器

那麼,要採用這種方案,Web服務器和應用程序之間就要知道如何進行交互。爲了定義Web服務器和應用程序之間的交互過程,就造成了不少不一樣的規範。這種規範裏最先的一個是CGI][3,1993年開發的。後來又出現了不少這樣的規範。好比改進CGI性能的FasgCGI,Java專用的Servlet規範,還有Python專用的WSGI規範等。提出這些規範的目的就是爲了定義統一的標準,提高程序的可移植性。在WSGI規範的最開始的PEP-333中一開始就描述了爲何須要WSGI規範。架構

WSGI如何工做

從上文能夠知道,WSGI至關因而Web服務器和Python應用程序之間的橋樑。那麼這個橋樑是如何工做的呢?首先,咱們明確橋樑的做用,WSGI存在的目的有兩個:app

  1. 讓Web服務器知道如何調用Python應用程序,而且把用戶的請求告訴應用程序。框架

  2. 讓Python應用程序知道用戶的具體請求是什麼,以及如何返回結果給Web服務器。函數

WSGI中的角色

在WSGI中定義了兩個角色,Web服務器端稱爲server或者gateway,應用程序端稱爲application或者framework(由於WSGI的應用程序端的規範通常都是由具體的框架來實現的)。咱們下面統一使用server和application這兩個術語。性能

server端會先收到用戶的請求,而後會根據規範的要求調用application端,以下圖所示:

WSGI

調用的結果會被封裝成HTTP響應後再發送給客戶端。

server如何調用application

首先,每一個application的入口只有一個,也就是全部的客戶端請求都同一個入口進入到應用程序。

接下來,server端須要知道去哪裏找application的入口。這個須要在server端指定一個Python模塊,也就是Python應用中的一個文件,而且這個模塊中須要包含一個名稱爲application的可調用對象(函數和類均可以),這個application對象就是這個應用程序的惟一入口了。WSGI還定義了application對象的形式:

def simple_app(environ, start_response):
      pass

上面代碼中的environstart_response就是server端調用application對象時傳遞的兩個參數。

咱們來看具體的例子。假設咱們的應用程序的入口文件是/var/www/index.py,那麼咱們就須要在server端配置好這個路徑(如何配置取決於server端的實現),而後在index.py中的代碼以下所示:

使用標準庫(這個只是demo)

import wsgiref

application = wsgiref.simple_server.demo_app

使用web.py框架

import web

urls = (
    '/.*', 'hello',
)

class hello(object):
    def GET(self):
        return "Hello, world."

application = web.application(urls, globals()).wsgifunc()

你能夠看到,文件中都須要有一個application對象,server端會找到這個文件,而後調用這個對象。因此支持WSGI的Python框架最終都會有這麼一個application對象,不過框架的使用者不須要關心這個application對象內部是如何工做的,只須要關心路由定義、請求處理等具體的業務邏輯。

由於application對象是惟一的入口,因此無論客戶端請求的路徑和數據是什麼,server都是調用這個application對象,具體的客戶端請求的處理有application對象完成。

application對象須要作什麼

上面已經提到了,application對象須要是一個可調用對象,並且其定義須要知足以下形式:

def simple_app(environ, start_response):
      pass

當server按照WSGI的規範調用了application以後,application就能夠開始處理客戶端的請求了,處理請求以後,application對象須要返回處理結果給server端。處理請求和返回結果這兩個事情,都和server調用application對象時傳遞的兩個參數有關。

environ參數

environ參數是一個Python的字典,裏面存放了全部和客戶端相關的信息,這樣application對象就能知道客戶端請求的資源是什麼,請求中帶了什麼數據等。environ字典包含了一些CGI規範要求的數據,以及WSGI規範新增的數據,還可能包含一些操做系統的環境變量以及Web服務器相關的環境變量。咱們來看一些environ中經常使用的成員:

首先是CGI規範中要求的變量:

  • REQUEST_METHOD: 請求方法,是個字符串,'GET', 'POST'等

  • SCRIPT_NAME: HTTP請求的path中的用於查找到application對象的部分,好比Web服務器能夠根據path的一部分來決定請求由哪一個virtual host處理

  • PATH_INFO: HTTP請求的path中剩餘的部分,也就是application要處理的部分

  • QUERY_STRING: HTTP請求中的查詢字符串,URL中?後面的內容

  • CONTENT_TYPE: HTTP headers中的content-type內容

  • CONTENT_LENGTH: HTTP headers中的content-length內容

  • SERVER_NAMESERVER_PORT: 服務器名和端口,這兩個值和前面的SCRIPT_NAME, PATH_INFO拼起來能夠獲得完整的URL路徑

  • SERVER_PROTOCOL: HTTP協議版本,HTTP/1.0或者HTTP/1.1

  • HTTP_: 和HTTP請求中的headers對應。

WSGI規範中還要求environ包含下列成員:

  • wsgi.version:表示WSGI版本,一個元組(1, 0),表示版本1.0

  • wsgi.url_scheme:http或者https

  • wsgi.input:一個類文件的輸入流,application能夠經過這個獲取HTTP request body

  • wsgi.errors:一個輸出流,當應用程序出錯時,能夠將錯誤信息寫入這裏

  • wsgi.multithread:當application對象可能被多個線程同時調用時,這個值須要爲True

  • wsgi.multiprocess:當application對象可能被多個進程同時調用時,這個值須要爲True

  • wsgi.run_once:當server指望application對象在進程的生命週期內只被調用一次時,該值爲True

上面列出的這些內容已經包括了客戶端請求的全部數據,足夠application對象處理客戶端請求了。

start_resposne參數

start_response是一個可調用對象,接收兩個必選參數和一個可選參數:

  • status: 一個字符串,表示HTTP響應狀態字符串

  • response_headers: 一個列表,包含有以下形式的元組:(header_name, header_value),用來表示HTTP響應的headers

  • exc_info(可選): 用於出錯時,server須要返回給瀏覽器的信息

當application對象根據environ參數的內容執行完業務邏輯後,就須要返回結果給server端。咱們知道HTTP的響應須要包含status,headers和body,因此在application對象將body做爲返回值return以前,須要先調用start_response(),將status和headers的內容返回給server,這同時也是告訴server,application對象要開始返回body了。

application對象的返回值

application對象的返回值用於爲HTTP響應提供body,若是沒有body,那麼能夠返回None。若是有body的化,那麼須要返回一個可迭代的對象。server端經過遍歷這個可迭代對象能夠得到body的所有內容。

application demo

PEP-3333中有一個application的實現demo,我把它再簡化以後以下:

def simple_app(environ, start_response):
      status = '200 OK'
      response_headers = [('Content-type', 'text/plain')]
      start_response(status, response_headers)
      return ['hello, world']

能夠將這段代碼和前面的說明對照起來理解。

再談server如何調用application

前面已經知道server如何定位到application的入口了,也知道了application的入口的形式以及application對象內部須要完成的工做。那麼,咱們還須要再說一下,environstart_response()是須要在server端的生成和定義的,其中關於start_response()的部分在規範中也有明確的要求。這部份內容太長了,不適合放在本文中,有興趣的讀者能夠去看下PEP-3333,裏面有一段server端的demo實現。

WSGI中間件

WSGI Middleware(中間件)也是WSGI規範的一部分。上一章咱們已經說明了WSGI的兩個角色:server和application。那麼middleware是一種運行在server和application中間的應用(通常都是Python應用)。middleware同時具有server和application角色,對於server來講,它是一個application;對於application來講,它是一個server。middleware並不修改server端和application端的規範,只是同時實現了這兩個角色的功能而已。

咱們能夠經過下圖來講明middleware是如何工做的:

WSGImiddleware

上圖中最上面的三個彩色框表示角色,中間的白色框表示操做,操做的發生順序按照1 ~ 5進行了排序,咱們直接對着上圖來講明middleware是如何工做的:

  1. Server收到客戶端的HTTP請求後,生成了environ_s,而且已經定義了start_response_s

  2. Server調用Middleware的application對象,傳遞的參數是environ_sstart_response_s

  3. Middleware會根據environ執行業務邏輯,生成environ_m,而且已經定義了start_response_m

  4. Middleware決定調用Application的application對象,傳遞參數是environ_mstart_response_m。Application的application對象處理完成後,會調用start_response_m而且返回結果給Middleware,存放在result_m中。

  5. Middleware處理result_m,而後生成result_s,接着調用start_response_s,並返回結果result_s給Server端。Server端獲取到result_s後就能夠發送結果給客戶端了。

從上面的流程能夠看出middleware應用的幾個特色:

  1. Server認爲middleware是一個application。

  2. Application認爲middleware是一個server。

  3. Middleware能夠有多層。

由於Middleware能過處理全部通過的request和response,因此要作什麼均可以,沒有限制。好比能夠檢查request是否有非法內容,檢查response是否有非法內容,爲request加上特定的HTTP header等,這些都是能夠的。

WSGI的實現和部署

要使用WSGI,須要分別實現server角色和application角色。

Application端的實現通常是由Python的各類框架來實現的,好比Django, web.py等,通常開發者不須要關心WSGI的實現,框架會會提供接口讓開發者獲取HTTP請求的內容以及發送HTTP響應。

Server端的實現會比較複雜一點,這個主要是由於軟件架構的緣由。通常經常使用的Web服務器,如Apache和nginx,都不會內置WSGI的支持,而是經過擴展來完成。好比Apache服務器,會經過擴展模塊mod_wsgi來支持WSGI。Apache和mod_wsgi之間經過程序內部接口傳遞信息,mod_wsgi會實現WSGI的server端、進程管理以及對application的調用。Nginx上通常是用proxy的方式,用nginx的協議將請求封裝好,發送給應用服務器,好比uWSGI,應用服務器會實現WSGI的服務端、進程管理以及對application的調用。

相關文章
相關標籤/搜索