Python進階開發之理解WSGI(上)

image.png


本文目錄



  • 什麼是WSGI?

  • 如何實現Application?

  • 如何實現Web Server?

  • Web Server如何決擇?


什麼是WSGI?

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

那麼這個規範是什麼?

  1. 服務器的請求處理要調用符合WSGI規範的網關接口;

  2. 由網關接口來調用應用程序,並且其要定義start_response(status, headers)函數,用於返回響應;

  3. 應用程序須是一個可調用對象(函數/類),webapp(environ, start_response)。接受兩個參數,environ是環境設置的字典,由服務器和WSGI網關接口設置,start_response是由網關接口定義的函數。

在這個規範中,有三個角色

  • web server:實現了WSGI server協議的服務器

  • Gateway Interface:網關接口

  • application:實現了WSGI application協議的框架

常見的web serveruWSGIGunicorn


常見的 Gateway Interface 有CGIWSGI


常見的application框架有 DjangoFlask

爲什麼要有WSGI規範?
網絡通信的完整流程,是這樣的

  1. 先創建一個web服務器,監聽端口,接收請求,並將請求路由轉發給對應的應用程序。

  2. 再創建一個web應用程序,用於接收到請求,經過必要的處理,返回響應給服務器。

  3. 服務器接收響應,返回給客戶端(瀏覽器)

試想一下,假如沒有這個規範,此時我們想開發一個網頁,我們需要做的就是,先搭建一個服務器,用於監聽端口,接收請求,再來創建一個用於接收請求的應用程序。

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

WSGI 就是來解決這個問題的,它解耦了服務器類與應用程序類。
意思就是,假如服務器類和應用程序類都嚴格遵守WSGI規範,那麼應用程序A可以隨便挑一個現成的服務器類(B,C,E都可以)來使用,而不需要其他任何的修改,只需要提供一個可以處理這些應用的請求處理類即可,不用擔心兼容問題。

我們的主人公WSGI ,在服務器和應用中間承擔一個“翻譯官”的角色。只要應用程序符合網關接口的標準,那麼服務器就只要做好服務器的角色,應用程序只要做好應用程序的作用,服務器和應用程序之間的通信全靠網關接口來協調。

如何實現Application?

WSGI規範 規定了,Application 必須是一個可調用的對象,它可以是函數,可以實現了__call__的類的實例對象,也可以是實現了__iter__的類對象。

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

  • 必須接收environstart_response兩個參數;

  • 必須返回 可迭代的對象

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

  • application是函數

 1def application(environ, start_response):
2
3   response_body = 'The request method was %s' % environ['REQUEST_METHOD']
4   status = '200 OK'
5
6   # 應答的頭部是一個列表,每對鍵值都必須是一個 tuple。
7   response_headers = [('Content-Type', 'text/plain'),
8                       ('Content-Length', str(len(response_body)))]
9
10   # 調用服務器程序提供的 start_response,填入兩個參數
11   start_response(status, response_headers)
12
13   # 返回必須是 iterable
14   return [response_body]













  • 實現了__call__的類的實例對象

 1class AppClass:
2    """這裏的可調用對象就是 AppClass 的實例,使用方法類似於:
3        app = AppClass()
4        for result in app(environ, start_response):
5             do_somthing(result)
6    """




7
8    def __init__(self):
9        pass
10
11    def __call__(self, environ, start_response):
12        status = '200 OK'
13        response_headers = [('Content-type', 'text/plain')]
14        self.start(status, response_headers)
15        yield "Hello world!\n"










  • 實現了__iter__的類對象

 1class AppClass:
2    """這裏的可調用對象就是 AppClass 這個類,調用它就能生成可以迭代的結果。
3        使用方法類似於:
4        for result in AppClass(env, start_response):
5             do_somthing(result)
6    """




7
8    def __init__(self, environ, start_response):
9        self.environ = environ
10        self.start = start_response
11
12    def __iter__(self):
13        status = '200 OK'
14        response_headers = [('Content-type', 'text/plain')]
15        self.start(status, response_headers)
16        yield "Hello world!\n"











如何實現Web Server?

上文說到,application 必須接收environstart_response兩個參數。

這兩個參數是什麼意思?

  • environ,:是 WSGI的環境信息。

  • start_response:是響應請求的函數。

其中start_response接收兩個參數,

  • status:HTTP狀態,譬如:"200 OK"

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

這兩個參數(environstart_response),都是由 Web Server來定義的。

所以我們要自己實現Web Server,也必須實現這兩個對象。定義完後,要調用application,將這兩個參數傳入。這是規定。

 1import os, sys
2
3def web_server(application):
4    # 構造environ 參數
5    environ = dict(os.environ.items())
6    environ['wsgi.input'] = sys.stdin
7    environ['wsgi.errors'] = sys.stderr
8    environ['wsgi.version'] = (1, 0)
9    environ['wsgi.multithread'] = False
10    environ['wsgi.multiprocess'] = True
11    environ['wsgi.run_once'] = True
12    environ['wsgi.url_scheme'] = 'http'
13
14    headers_set = []
15
16    # 定義響應函數
17    def start_response(status, response_headers, exc_info=None):
18        headers_set[:] = [status, response_headers]
19
20    # 調用application,並傳入參數
21    result = application(environ, start_response)
22
23    # 用for循環,就解釋了爲什麼application要返回可迭代對象
24    for data in result:
25        if data:
26            print(data)

























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

到了現在,誰也沒必要去重要寫web server了,使用Python最忌諱的就是重複造輪子。那是傻。

Web Server如何決擇?

首先要明白的是,生產環境和開發環境使用的Web Server是不一樣的。

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

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

  • 低可用:做爲服務啓動,只要某個地方ERROR,服務就掛掉了。

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

如此看來,Django自帶的server只能用於開發調試,並不適合用於生產環境。

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

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

意思是說,Django是一個專業的應用程序端框架,並不擅長於服務端。

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

當前市面上,已經出現了很多專業且優秀的Web Server,這裏也介紹一下。

  • Gunicorn

Gunicorn(從Ruby下面的Unicorn得到的啓發)應運而生:依賴Nginx的代理行爲,同Nginx進行功能上的分離。由於不需要直接處理用戶來的請求(都被Nginx先處理),Gunicorn不需要完成相關的功能,其內部邏輯非常簡單:接受從Nginx來的動態請求,處理完之後返回給Nginx,由後者返回給用戶。

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

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

  • uWSGI

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

  • bjoern

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

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

綜合網友們的回答,整理如下:

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

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章