本文目錄
什麼是WSGI?
如何實現Application?
如何實現Web Server?
Web Server如何決擇?
. 什麼是WSGI?
WSGI(Web Server Gateway Interface),顧名思義,它既不是服務器,也不是應用,而是一種接口(規範
),描述web server
如何與web application
通信的規範。
那麼這個規範是什麼?
服務器的請求處理要調用符合WSGI規範的網關接口;
由網關接口來調用應用程序,並且其要定義start_response(status, headers)函數,用於返回響應;
應用程序須是一個可調用對象(函數/類),webapp(environ, start_response)。接受兩個參數,environ是環境設置的字典,由服務器和WSGI網關接口設置,start_response是由網關接口定義的函數。
在這個規範中,有三個角色
web server
:實現了WSGI server協議的服務器Gateway Interface
:網關接口application
:實現了WSGI application協議的框架
常見的web server
有uWSGI
、Gunicorn
常見的 Gateway Interface
有CGI
, WSGI
常見的application
框架有 Django
、Flask
爲什麼要有WSGI規範?
網絡通信的完整流程,是這樣的
先創建一個web服務器,監聽端口,接收請求,並將請求路由轉發給對應的應用程序。
再創建一個web應用程序,用於接收到請求,經過必要的處理,返回響應給服務器。
服務器接收響應,返回給客戶端(瀏覽器)
試想一下,假如沒有這個規範,此時我們想開發一個網頁,我們需要做的就是,先搭建一個服務器,用於監聽端口,接收請求,再來創建一個用於接收請求的應用程序。
聽起來好像沒什麼問題。但是你今天開發一個網站,要寫一個server,明天又要開發一個網站,又要寫一個server。全中國多少web開發人員,如果按照這種模式下去,開發效率可想而知,嚴重浪費時間人力。
WSGI 就是來解決這個問題的,它解耦了服務器類與應用程序類。
意思就是,假如服務器類和應用程序類都嚴格遵守WSGI規範,那麼應用程序A可以隨便挑一個現成的服務器類(B,C,E都可以)來使用,而不需要其他任何的修改,只需要提供一個可以處理這些應用的請求處理類即可,不用擔心兼容問題。
我們的主人公WSGI
,在服務器和應用中間承擔一個“翻譯官”的角色。只要應用程序符合網關接口的標準,那麼服務器就只要做好服務器的角色,應用程序只要做好應用程序的作用,服務器和應用程序之間的通信全靠網關接口來協調。
. 如何實現Application?
WSGI規範 規定了,Application 必須是一個可調用的對象,它可以是函數,可以實現了__call__
的類的實例對象,也可以是實現了__iter__
的類對象。
不管是哪種方式的可調用對象,都要遵循兩個原則
必須接收
environ
,start_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
必須接收environ
, start_response
兩個參數。
這兩個參數是什麼意思?
environ
,:是 WSGI的環境信息。start_response
:是響應請求的函數。
其中start_response
接收兩個參數,
status
:HTTP狀態,譬如:"200 OK
"response_headers
:響應消息的頭,譬如:[('Content-Type', 'text/plain')]
,以list的形式,每個元素是一個tuple,而一個tuple裏有兩個元素,一個是key,一個是value。
這兩個參數(environ
,start_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,首次配置麻煩,性能較好