當前位置:
首頁 > 知識 > python wsgi 簡介

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。幾點說明:






  1. environ 和 start_response 由 http server 提供並實現



  2. environ 變數是包含了環境信息的字典



  3. Application 內部在返回前調用 start_response



  4. start_response也是一個 callable,接受兩個必須的參數,status(HTTP狀態)和 response_headers(響應消息的頭)



  5. 可調用對象要返回一個值,這個值是可迭代的。




說了這麼多的概念,再來看看代碼的實現:





# 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大神】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

Python爬蟲之天氣預報
說說 Python 中的閉包

TAG:Python開發 |