python wsgi 簡介
點擊上方「
Python開發
」,選擇「置頂公眾號」
關鍵時刻,第一時間送達!
基礎知識
Python 知識
iterator 和 generator
函數的高級用法:嵌套函數,作為參數傳遞等等
了解 decorator 會對理解 wsgi 有很大的幫助
python 的 callable 概念
classmethod 和 staticmethod 的概念
web 編程的基礎
HTTP 基礎
對於 web 應用程序來說,最基本的概念就是客戶端發送請求(request),收到伺服器端的響應(response)。
下面是簡單的 HTTP 請求:
GET
/
Index
.
html
HTTP
/
1.1
r
n
Connection
:
Keep
-
Alive
r
n
Accept
: */*
r
n
User
-
Agent
:
Sample
Application
r
n
Host
:
www
.
microsoft
.
com
r
n
r
n
內容包括了 method、 url、 protocol version 以及頭部的信息。而 HTTP 響應(不包括數據)可能是如下的內容:
HTTP
/
1.1
200
OK
Server
:
Microsoft
-
IIS
/
5.0
r
n
Content
-
Location
:
http
:
//www.microsoft.com/default.htm
Date
:
Tue
,
25
Jun
2002
19
:
33
:
18
GMT
r
n
Content
-
Type
:
text
/
html
r
n
Accept
-
Ranges
:
bytes
r
n
Last
-
Modified
:
Mon
,
24
Jun
2002
20
:
27
:
23
GMT
r
n
Content
-
Length
:
26812
r
n
實際生產中,python 程序是放在伺服器的 http server(比如 apache, nginx 等)上的。現在的問題是
伺服器程序怎麼把接受到的請求傳遞給 python 呢,怎麼在網路的數據流和 python 的結構體之間轉換呢?
這就是 wsgi 做的事情:一套關於程序端和伺服器端的規範,或者說統一的介面。WSGI
我們先看一下面向 http 的 python 程序需要關心哪些內容:
請求
請求的方法 method
請求的地址 url
請求的內容
請求的頭部 header
請求的環境信息
響應
狀態碼 status_code
響應的數據
響應的頭部
WSGI(Web Server Gateway Interface) 的任務就是把上面的數據在 http server 和 python 程序之間簡單友好地傳遞。它是一個標準,被定義在PEP 333。需要 http server 和 python 程序都要遵守一定的規範,實現這個標準的約定內容,才能正常工作。
應用程序端
WSGI 規定每個 python 程序(Application)必須是一個可調用的對象(實現了__call__ 函數的方法或者類),接受兩個參數 environ(WSGI 的環境信息) 和 start_response(開始響應請求的函數),並且返回 iterable。幾點說明:
environ 和 start_response 由 http server 提供並實現
environ 變數是包含了環境信息的字典
Application 內部在返回前調用 start_response
start_response也是一個 callable,接受兩個必須的參數,status(HTTP狀態)和 response_headers(響應消息的頭)
可調用對象要返回一個值,這個值是可迭代的。
說了這麼多的概念,再來看看代碼的實現:
# 1. 可調用對象是一個函數
def
application
(
environ
,
start_response
)
:
response_body
=
"The request method was %s"
%
environ
[
"REQUEST_METHOD"
]
# HTTP response code and message
status
=
"200 OK"
# 應答的頭部是一個列表,每對鍵值都必須是一個 tuple。
response_headers
=
[(
"Content-Type"
,
"text/plain"
),
(
"Content-Length"
,
str
(
len
(
response_body
)))]
# 調用伺服器程序提供的 start_response,填入兩個參數
start_response
(
status
,
response_headers
)
# 返回必須是 iterable
return
[
response_body
]
# 2. 可調用對象是一個類
class
AppClass
:
"""這裡的可調用對象就是 AppClass 這個類,調用它就能生成可以迭代的結果。
使用方法類似於:
for result in AppClass(env, start_response):
do_somthing(result)
"""
def
__init__
(
self
,
environ
,
start_response
)
:
self
.
environ
=
environ
self
.
start
=
start_response
def
__iter__
(
self
)
:
status
=
"200 OK"
response_headers
=
[(
"Content-type"
,
"text/plain"
)]
self
.
start
(
status
,
response_headers
)
yield
"Hello world! "
# 3. 可調用對象是一個實例
class
AppClass
:
"""這裡的可調用對象就是 AppClass 的實例,使用方法類似於:
app = AppClass()
for result in app(environ, start_response):
do_somthing(result)
"""
def
__init__
(
self
)
:
pass
def
__call__
(
self
,
environ
,
start_response
)
:
status
=
"200 OK"
response_headers
=
[(
"Content-type"
,
"text/plain"
)]
self
.
start
(
status
,
response_headers
)
yield
"Hello world! "
伺服器程序端
上面已經說過,標準要能夠確切地實行,必須要求程序端和伺服器端共同遵守。上面提到, envrion 和 start_response 都是伺服器端提供的。下面就看看,伺服器端要履行的義務。
準備 environ 參數
定義 start_response 函數
調用程序端的可調用對象
PEP 333 里給出了一個 wsgi server 的簡單實現,我又簡化了一下——去除一些異常處理和判斷,添加了一點注釋:
import
os
,
sys
def
run_with_cgi
(
application
)
:
# application 是程序端的可調用對象
# 準備 environ 參數,這是一個字典,裡面的內容是一次 HTTP 請求的環境變數
environ
=
dict
(
os
.
environ
.
items
())
environ
[
"wsgi.input"
]
=
sys
.
stdin
environ
[
"wsgi.errors"
]
=
sys
.
stderr
environ
[
"wsgi.version"
]
=
(
1
,
0
)
environ
[
"wsgi.multithread"
]
=
False
environ
[
"wsgi.multiprocess"
]
=
True
environ
[
"wsgi.run_once"
]
=
True
environ
[
"wsgi.url_scheme"
]
=
"http"
headers_set
=
[]
headers_sent
=
[]
# 把應答的結果輸出到終端
def
write
(
data
)
:
sys
.
stdout
.
write
(
data
)
sys
.
stdout
.
flush
()
# 實現 start_response 函數,根據程序端傳過來的 status 和 response_headers 參數,
# 設置狀態和頭部
def
start_response
(
status
,
response_headers
,
exc_info
=
None
)
:
headers_set
[
:
]
=
[
status
,
response_headers
]
return
write
# 調用客戶端的可調用對象,把準備好的參數傳遞過去
result
=
application
(
environ
,
start_response
)
# 處理得到的結果,這裡簡單地把結果輸出到標準輸出。
try
:
for
data
in
result
:
if
data
:
# don"t send headers until body appears
write
(
data
)
finally
:
if
hasattr
(
result
,
"close"
)
:
result
.
close
()
中間層 middleware
有些程序可能處於伺服器端和程序端兩者之間:對於伺服器程序,它就是應用程序;而對於應用程序,它就是伺服器程序。這就是中間層 middleware。middleware 對伺服器程序和應用是透明的,它像一個代理/管道一樣,把接收到的請求進行一些處理,然後往後傳遞,一直傳遞到客戶端程序,最後把程序的客戶端處理的結果再返回。如下圖所示:
middleware 做了兩件事情:
被伺服器程序(有可能是其他 middleware)調用,返回結果回去
調用應用程序(有可能是其他 middleware),把參數傳遞過去
PEP 333 上面給出了 middleware 的可能使用場景:
根據 url 把請求給到不同的客戶端程序(url routing)
允許多個客戶端程序/web 框架同時運行,就是把接到的同一個請求傳遞給多個程序。
負載均衡和遠程處理:把請求在網路上傳輸
應答的過濾處理
那麼簡單地 middleware 實現是怎麼樣的呢?下面的代碼實現的是一個簡單地 url routing 的 middleware:
class
Router
(
object
)
:
def
__init__
(
self
)
:
self
.
path_info
=
{}
def
route
(
self
,
environ
,
start_response
)
:
application
=
self
.
path_info
[
environ
[
"PATH_INFO"
]]
return
application
(
environ
,
start_response
)
def
__call__
(
self
,
path
)
:
def
wrapper
(
application
)
:
self
.
path_info
[
path
]
=
application
return
wrapper
router
=
Router
()
怎麼在程序裡面使用呢?
#here is the application
@
router
(
"/hello"
)
#調用 route 實例,把函數註冊到 paht_info 字典
def
hello
(
environ
,
start_response
)
:
status
=
"200 OK"
output
=
"Hello"
response_headers
=
[(
"Content-type"
,
"text/plain"
),
(
"Content-Length"
,
str
(
len
(
output
)))]
write
=
start_response
(
status
,
response_headers
)
return
[
output
]
@
router
(
"/world"
)
def
world
(
environ
,
start_response
)
:
status
=
"200 OK"
output
=
"World!"
response_headers
=
[(
"Content-type"
,
"text/plain"
),
(
"Content-Length"
,
str
(
len
(
output
)))]
write
=
start_response
(
status
,
response_headers
)
return
[
output
]
#here run the application
result
=
router
.
route
(
environ
,
start_response
)
for
value
in
result
:
write
(
value
)
註:上面的代碼來自這篇博客(http://linluxiang.iteye.com/blog/799163)。
了解更多?
對於普通的開發者來說,了解到上面的知識已經足夠,並不需要掌握每一個細節。
Only authors of web servers and programming frameworks need to know every detail and corner case of the WSGI design. You don』t need to understand every detail of WSGI just to install a WSGI application or to write a web application using an existing framework.
想要更多的話,就去看 PEP333,文檔里還有下面更多的知識:
錯誤處理
environ 變數包含哪些值,都是什麼意思。
輸入和輸出的細節
start_response 的更多規範
content-length 等頭部規範
緩存和文本流
unicode 和多語言處理
……
參考資料
官方文檔 PEP333
Getting Started with WSGI
一篇很簡潔易懂的 wsgi 介紹
wsgiref:官方的 wsgi 實現,包括客戶端和伺服器端
來源:Cizixs
http://cizixs.com/2014/11/08/understand-wsgi
Python開發整理髮布,轉載請聯繫作者獲得授權
【點擊成為Java大神】


TAG:Python開發 |