WSGI的理解

黄舟
發布: 2016-12-16 11:43:54
原創
1412 人瀏覽過

Python web開發中,服務端程式可分為2個部分:

伺服器程式(用來接收、整理客戶端發送的請求)

應用程式(處理伺服器程式傳遞過來的請求)
在開發應用程式的時候,我們會把常用的功能封裝起來,成為各種框架,例如Flask,Django,Tornado(使用某框架進行web開發,相當於開發服務端的應用程序,處理後台邏輯)
但是,伺服器程式和應用程式互相配合才能提供給使用者服務,而不同應用程式(不同框架)會有不同的函數、功能。 此時,我們需要一個標準,讓伺服器程式和應用程式都支援這個標準,那麼,二者就能很好的配合了

WSGI:wsgi是python web開發的標準,類似於協定。它是伺服器程式和應用程式的一個約定,規定了各自使用的介面和功能,以便二和互相配合

WSGI應用程式的部分規定

應用程式是一個可呼叫的物件
可呼叫的物件有三種:

一個函數

一個類,必須實現__call__()方法

一個類的實例

這個物件接收兩個參數
從源碼中,我們可以看到,這兩個參數是environ , start_response. 以可呼叫物件為一個類別為例:

class application:
   def __call__(self, environ, start_response):
       以可調用物件為一個類別為例:

class application:

   def __call__(self, environ, start_response):

       return [xxx]

):
       return [xxx]
def run(application):     #伺服器程式呼叫應用程式
   environ = {}     #設定參數

   def start_response(xxx):    response)          #呼叫應用程式的_ _call__函數(這裡應用程式是一個類別)

   def write(data):

              pass

   def data in ult: 程式設計以下的事:

    1. 設定應用程式所需的參數
    2. 呼叫應用程式
    3. 迭代存取應用程式的回傳結果,並傳給客戶端


Middleware

middlewareware是介於伺服器程式和應用程式
Middleware

middlewarewareware和應用程式是透明的。

對於伺服器程式來說,middleware就是應用程序,middleware需要偽裝成應用程序,傳遞給伺服器程式
對於應用程式來說,middleware就是伺服器程序,middleware需要偽裝成伺服器程序,接受並調用應用程式

伺服器程式取得到了客戶請求的URL,需要把URL交給不同的函式處理,這個功能可以使用middleware實作:

# URL Routing middleware

def urlrouting(url_app_mapping):  

#函數可調用,包含2個參數,返回可迭代的值
         url = environ['PATH_INFO']      
        result = app(environ, start_response)       #呼叫應用程式

         return result      

    return midware_app


函數midware_app所以對於應用程式來說,它是一個伺服器程式
另一方面,midware_app函數是一個可呼叫的對象,接收兩個參數,同時可呼叫對象傳回了一個可迭代的值。所以對於伺服器程式來說,它是一個應用程式

寫入中間件(middleware)的邏輯:
1. middleware需要偽裝成應用程式—> WSGI應用程式的要求—> 1. 可呼叫 2. 兩個參數3. 傳回可迭代的值
2. middleware需要偽裝成伺服器程式—> WSGI伺服器程式的要求—> 呼叫應用程式

我們要了解environ這個變數。在WSGI中, 應用程式需要兩個參數:environ和start_response, 在伺服器程式呼叫應用程式之前, 需要先設定這兩個參數。 其中start_response通常是個可呼叫的方法, 而environ則是一個字典, 它是在CGI中定義的, 查看CGI文檔The Common Gateway Interface Specification, 可以找到關於environ的定義。
以下是environ中的參數:

 AUTH_TYPE
 CONTENT_LENGTH #HTTP請求中Content-Length的部分
 CONTENT_TYPE #HTTP _ACCEPT等
PATH_INFO #URL路徑除了起始部分後的剩餘部分,用於找到對應的應用程式物件,如果請求的路徑就是根路徑,這個值為空字串
 PATH_TRANSLATED
 QUERY_STRING #URL路徑中?後面的部分
 REMOTE_ADDR
 REMOTE_HOST
 REMOTE_IDENT
 REMOTE_USER
 REQUEST_METHOD #HTTP 請求方法,例如"GET", "POST"
 SCRipT_NAME #URL路徑的起始部分對應的應用程序對象,如果應用程序對像對應服務器的根,那麼這個值可以為空字串
 SERVER_NAME
 SERVER_PORT
 SERVER_PROTOCOL #客戶端請求的協定(HTTP/1.1 HTTP/1.0)
 SERVER_SOFTWARE

值舉例:

REQUEST_METHOD='GET'

SCRIPT_NAME=''

SERVER_NAME='localhost'

SERVER_PORT='5000'
PATH_INFO='/aaa]f/SQUEf/SS世外PE=' text/plain'
CONTEN_LENGTH=''

HTTP_HOST = 'localhost:8000'
HTTP_ACCEPT = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/; 0.8'
HTTP_ACCEPT_ENCODING = 'gzip,deflate,sdch'
HTTP_ACCEPT_LANGUAGE = 'en-US,en;q=0.8,zh;q=0.6,zh-CNNEC;q=0.4,CONzh-TW; = 'keep-alive'
HTTP_USER_AGENT = 'Mozilla/5.0 (X11; linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36'回應的開始, 它的形式為:start_response(status, response_headers, exc_info=None)
status表示HTTP狀態碼, response_headers, exc_info=None)
status表示HTTP狀態碼, 例如200 OK
response_headers是一個列表,列表元素是個tuple:(header_name, header_value)是一個列表,列表元素是個tuple:(header_name, header_value)_info參數, 當處理請求的過程中發生錯誤時, 會設定該參數, 同時會調用start_response
舉一個werkzeug官方文檔上的例子,我稍作改進,以進行分析(這段建議看完wsgi.py第二部分的SharedDataMiddleware類別再看):

class Shortly(object):

   def __init__(self, config):

       self.redis = redis.

   def dispatch_request(self, request):
       return Response('Hello World!')     #初始化Responseclass  = Request(environ)
       response = self.dispatch_request(request )   #response的類型為Response類別
       print('%%%%%%%%%%%%%%%%%%%%%%%%%%%')
       re%%%%%%%%')
       return response Response的__call__函數就是應用程序,回傳迭代物件

   def __call__(self, environ, start_response):
    print(self.wsgi_app)
 f. wsgi_app(environ, start_response)


def create_app(redis_host='localhost', redis_port=6379, with_static=True):
   app = Shortly({
       'red_hosts': 問題      redis_port
   })
   if with_static:
    print(' yes')
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
           '/static':  os.path.join(os.path . 3333333333333')
   return app
#開啟本機伺服器
if __name__ == '__main__':
   from werkzeug.serving import run_simple
   5000, app, use_debugger =True, use_reloader=True)

我們查看Response的源碼(Response繼承了BaseResponse,查看BaseResponse的源碼即可)可以知道:函數dispatch_request()傳回的值是Request的建構函數,即傳回了一個Response類別, 在函數wsgi_app()中,request的值的類型就是Response, 所以wsgi_app()的回傳值response(environ, start_response)其實是呼叫了Response類別的__call__()函式。
看了原始碼我們可以發現,__call__()是一個WSGI的應用程式!

當執行這個程式的時候:

2222222222222222
yes
333333333333333333
* Running on http://127.0.0.1:500097:500002 70505030050050050005000500005000050000 40505050000) 450505050500050000500050005000 4050505000) 405050050050050050050000) 405050500500005) 液222222222222222
yes
333333333333333333


我們先不糾結為什麼讀取了2次。

當我們打開這個網頁,控制台的輸出為:


   #說明wsgi_app是SharedDataMiddleware的實例!  

erghrgheoghegoierge

%%%%%%%%%%%%%%%%%%%%%%%%%%% #說明執行了原wsgi_app函數中的內容!
127.0.0.1 - - [22/May/2015 21:01:25] "GET / HTTP/1.1" 200 -


可以注意到,在本例中,app.wsgi_app這個方法已經變成了一個SharedDataM.類別的實例,我很好奇當伺服器把environ和start_response傳遞給app後,為什麼wsgi_pp還會執行原wsgi_app中的內容呢?

當我們訪問主機位址的時候,伺服器程式接受使用者請求,然後會把environ和start_response傳遞給應用程式app,app會執行__call__函數,在該函數中,會執行app.wsgi_app這個函數。 然後wsgi_app會執行ShareDataMiddleware的__call__()函數

這裡需要我們看SharedDataMiddleware類別的__call__()的源碼。看了原始碼我們可以發現,由於使用者沒有請求靜態文件,所以會執行return self.app(environ, start_response),在本例中,我們可以看到在create_app()中,我們定義的ShareDataMiddleware的應用程式是app.wsgi_app,所以這裡回傳的是原wsgi_app函數!所以當然會執行原函數了~
同時,我們也可以在static資料夾中放​​置一個文件,然後訪問試試看:127.0.0.1/static/文件名,此時就能看到那個文件了!
透過本例,我們可以更深刻的了解Middleware的作用:

Middleware介於伺服器程式和應用程式之間,它會接收伺服器發送的訊息(environ和

start_response),並做一定的處理,然後把需要應用程式處理的部分傳遞給應用程式處理



另外, 伺服器程式也需要定義WSGI的相關變數:

wsgi.version                 

值的形式為(1, 0) 表示WSGI 版本1.0                            

wsgi.url_scheme
表示url 的模式,例如"https" 或"http"                        
wsgi.input                    
輸入流,HTTP請求的body 部分可從這裡讀取                                                 
輸出流,如果有錯誤,可寫往這裡
wsgi .multithread
如果應用程式物件可以被同一進程中的另一個執行緒同時調用,這個值為True
wsgi.multiprocess
如果應用程式物件可以同時被另一個進程調用,這個值為True
wsgi.run_once
如果伺服器希望應用程式物件在包含它的進程中只被呼叫一次,那麼這個值為True


 以上就是WSGI的理解的內容,更多相關文章請關注PHP中文網(www.php.cn)!


相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板