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

image.png


本文目錄



  • 什麼是WSGI?web

  • 如何實現Application?瀏覽器

  • 如何實現Web Server?服務器

  • Web Server如何決擇?網絡


什麼是WSGI?

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

那麼這個規範是什麼?框架

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

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

  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,首次配置麻煩,性能較好

相關文章
相關標籤/搜索