什麼是WSGI?web
如何實現Application?瀏覽器
如何實現Web Server?服務器
Web Server如何決擇?網絡
WSGI(Web Server Gateway Interface),顧名思義,它既不是服務器,也不是應用,而是一種接口(規範
),描述web server
如何與web application
通訊的規範。app
那麼這個規範是什麼?框架
服務器的請求處理要調用符合WSGI規範的網關接口;webapp
由網關接口來調用應用程序,而且其要定義start_response(status, headers)函數,用於返回響應;ide
應用程序須是一個可調用對象(函數/類),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
,在服務器和應用中間承擔一個「翻譯官」的角色。只要應用程序符合網關接口的標準,那麼服務器就只要作好服務器的角色,應用程序只要作好應用程序的做用,服務器和應用程序之間的通訊全靠網關接口來協調。
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"
上文說到,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
是不同的。
就拿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,首次配置麻煩,性能較好