WSGI協議
首先弄清下面幾個概念:
WSGI:全稱是Web Server Gateway Interface,WSGI不是服務器,python模塊,框架,API或者任何軟件,只是一種規范,描述web server如何與web application通信的規范。server和application的規范在PEP 3333中有具體描述。要實現WSGI協議,必須同時實現web server和web application,當前運行在WSGI協議之上的web框架有Bottle, Flask, Django。
uwsgi:與WSGI一樣是一種通信協議,是uWSGI服務器的獨占協議,用於定義傳輸信息的類型(type of information),每一個uwsgi packet前4byte為傳輸信息類型的描述,與WSGI協議是兩種東西,據說該協議是fcgi協議的10倍快。
uWSGI:是一個web服務器,實現了WSGI協議、uwsgi協議、http協議等。
WSGI協議主要包括server和application兩部分:
- WSGI server負責從客戶端接收請求,將request轉發給application,將application返回的response返回給客戶端;
- WSGI application接收由server轉發的request,處理請求,並將處理結果返回給server。application中可以包括多個棧式的中間件(middlewares),這些中間件需要同時實現server與application,因此可以在WSGI服務器與WSGI應用之間起調節作用:對服務器來說,中間件扮演應用程序,對應用程序來說,中間件扮演服務器。
WSGI協議其實是定義了一種server與application解耦的規范,即可以有多個實現WSGI server的服務器,也可以有多個實現WSGI application的框架,那么就可以選擇任意的server和application組合實現自己的web應用。例如uWSGI和Gunicorn都是實現了WSGI server協議的服務器,Django,Flask是實現了WSGI application協議的web框架,可以根據項目實際情況搭配使用。
像Django,Flask框架都有自己實現的簡單的WSGI server,一般用於服務器調試,生產環境下建議用其他WSGI server。
WSGI協議的實現
以Django為例,分析一下WSGI協議的具體實現過程。
django WSGI application
WSGI application應該實現為一個可調用對象,例如函數、方法、類(包含`call`方法)。需要接收兩個參數:
- 一個字典,該字典可以包含了客戶端請求的信息以及其他信息,可以認為是請求上下文,一般叫做environment(編碼中多簡寫為environ、env)
- 一個用於發送HTTP響應狀態(HTTP status )、響應頭(HTTP headers)的回調函數
通過回調函數將響應狀態和響應頭返回給server,同時返回響應正文(response body),響應正文是可迭代的、並包含了多個字符串。下面是Django中application的具體實現部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
class
WSGIHandler(base.BaseHandler):
initLock
=
Lock()
request_class
=
WSGIRequest
def
__call__(
self
, environ, start_response):
# 加載中間件
if
self
._request_middleware
is
None
:
with
self
.initLock:
try
:
# Check that middleware is still uninitialized.
if
self
._request_middleware
is
None
:
self
.load_middleware()
except
:
# Unload whatever middleware we got
self
._request_middleware
=
None
raise
set_script_prefix(get_script_name(environ))
# 請求處理之前發送信號
signals.request_started.send(sender
=
self
.__class__, environ
=
environ)
try
:
request
=
self
.request_class(environ)
except
UnicodeDecodeError:
logger.warning(
'Bad Request (UnicodeDecodeError)'
,
exc_info
=
sys.exc_info(),
extra
=
{
'status_code'
:
400
,})
response
=
http.HttpResponseBadRequest()
else
:
response
=
self
.get_response(request)
response._handler_class
=
self
.__class__
status
=
'%s %s'
%
(response.status_code, response.reason_phrase)
response_headers
=
[(
str
(k),
str
(v))
for
k, v
in
response.items()]
for
c
in
response.cookies.values():
response_headers.append((
str
(
'Set-Cookie'
),
str
(c.output(header
=
''))))
# server提供的回調方法,將響應的header和status返回給server
start_response(force_str(status), response_headers)
if
getattr
(response,
'file_to_stream'
,
None
)
is
not
None
and
environ.get(
'wsgi.file_wrapper'
):
response
=
environ[
'wsgi.file_wrapper'
](response.file_to_stream)
return
response
|
可以看出application的流程包括:
- 加載所有中間件,以及執行框架相關的操作,設置當前線程腳本前綴,發送請求開始信號;
- 處理請求,調用get_response()方法處理當前請求,該方法的的主要邏輯是通過urlconf找到對應的view和callback,按順序執行各種middleware和callback。
- 調用由server傳入的start_response()方法將響應header與status返回給server。
- 返回響應正文
django WSGI Server
負責獲取http請求,將請求傳遞給WSGI application,由application處理請求后返回response。以Django內建server為例看一下具體實現。
通過runserver運行django項目,在啟動時都會調用下面的run方法,創建一個WSGIServer的實例,之后再調用其serve_forever()方法啟動服務。
1
2
3
4
5
6
7
8
9
10
11
12
|
def
run(addr, port, wsgi_handler, ipv6
=
False
, threading
=
False
):
server_address
=
(addr, port)
if
threading:
httpd_cls
=
type
(
str
(
'WSGIServer'
), (socketserver.ThreadingMixIn, WSGIServer), {})
else
:
httpd_cls
=
WSGIServer
# 這里的wsgi_handler就是WSGIApplication
httpd
=
httpd_cls(server_address, WSGIRequestHandler, ipv6
=
ipv6)
if
threading:
httpd.daemon_threads
=
True
httpd.set_app(wsgi_handler)
httpd.serve_forever()
|
下面表示WSGI server服務器處理流程中關鍵的類和方法。
.WSGIServer
run()方法會創建WSGIServer實例,主要作用是接收客戶端請求,將請求傳遞給application,然后將application返回的response返回給客戶端。
- 創建實例時會指定HTTP請求的handler:WSGIRequestHandler類
- 通過set_app和get_app方法設置和獲取WSGIApplication實例wsgi_handler
- 處理http請求時,調用handler_request方法,會創建WSGIRequestHandler實例處理http請求。
- WSGIServer中get_request方法通過socket接受請求數據
.WSGIRequestHandler
- 由WSGIServer在調用handle_request時創建實例,傳入request、cient_address、WSGIServer三個參數,__init__方法在實例化同時還會調用自身的handle方法
- handle方法會創建ServerHandler實例,然后調用其run方法處理請求
.ServerHandler
- WSGIRequestHandler在其handle方法中調用run方法,傳入self.server.get_app()參數,獲取WSGIApplication,然后調用實例(__call__),獲取response,其中會傳入start_response回調,用來處理返回的header和status。
- 通過application獲取response以后,通過finish_response返回response
.WSGIHandler
- WSGI協議中的application,接收兩個參數,environ字典包含了客戶端請求的信息以及其他信息,可以認為是請求上下文,start_response用於發送返回status和header的回調函數
雖然上面一個WSGI server涉及到多個類實現以及相互引用,但其實原理還是調用WSGIHandler,傳入請求參數以及回調方法start_response(),並將響應返回給客戶端。
django simple_server
django的simple_server.py模塊實現了一個簡單的HTTP服務器,並給出了一個簡單的demo,可以直接運行,運行結果會將請求中涉及到的環境變量在瀏覽器中展示出來。
其中包括上述描述的整個http請求的所有組件:
ServerHandler, WSGIServer, WSGIRequestHandler,以及demo_app表示的簡易版的WSGIApplication。
可以看一下整個流程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
if
__name__
=
=
'__main__'
:
# 通過make_server方法創建WSGIServer實例
# 傳入建議application,demo_app
httpd
=
make_server('',
8000
, demo_app)
sa
=
httpd.socket.getsockname()
print
(
"Serving HTTP on"
, sa[
0
],
"port"
, sa[
1
],
"..."
)
import
webbrowser
# 調用WSGIServer的handle_request方法處理http請求
httpd.handle_request()
# serve one request, then exit
httpd.server_close()
def
make_server(
host, port, app, server_class
=
WSGIServer, handler_class
=
WSGIRequestHandler
):
"""Create a new WSGI server listening on `host` and `port` for `app`"""
server
=
server_class((host, port), handler_class)
server.set_app(app)
return
server
# demo_app可調用對象,接受請求輸出結果
def
demo_app(environ,start_response):
from
io
import
StringIO
stdout
=
StringIO()
print
(
"Hello world!"
,
file
=
stdout)
print
(
file
=
stdout)
h
=
sorted
(environ.items())
for
k,v
in
h:
print
(k,
'='
,
repr
(v),
file
=
stdout)
start_response(
"200 OK"
, [(
'Content-Type'
,
'text/plain; charset=utf-8'
)])
return
[stdout.getvalue().encode(
"utf-8"
)]
|
demo_app()表示一個簡單的WSGI application實現,通過make_server()方法創建一個WSGIServer實例,調用其handle_request()方法,該方法會調用demo_app()處理請求,並最終返回響應。
uWSGI
uWSGI旨在為部署分布式集群的網絡應用開發一套完整的解決方案。主要面向web及其標准服務。由於其可擴展性,能夠被無限制的擴展用來支持更多平台和語言。uWSGI是一個web服務器,實現了WSGI協議,uwsgi協議,http協議等。
uWSGI的主要特點是:
- 超快的性能
- 低內存占用
- 多app管理
- 詳盡的日志功能(可以用來分析app的性能和瓶頸)
- 高度可定制(內存大小限制,服務一定次數后重啟等)
uWSGI服務器自己實現了基於uwsgi協議的server部分,我們只需要在uwsgi的配置文件中指定application的地址,uWSGI就能直接和應用框架中的WSGI application通信。
參考閱讀:
- WSGI & uwsgi
- WSGI Tutorial
- 打造mvc框架之wsgi協議的優缺點及接口實現
- Nginx和uWSGI通信機制
- 理解Python WSGI
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。