国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

WSGI的理解

2019-11-14 17:07:58
字體:
來源:轉載
供稿:網友

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

  1. 服務器程序(用來接收、整理客戶端發送的請求)
  2. 應用程序(處理服務器程序傳遞過來的請求)
    在開發應用程序的時候,我們會把常用的功能封裝起來,成為各種框架,比如Flask,Django,Tornado(使用某框架進行web開發,相當于開發服務端的應用程序,處理后臺邏輯)
    但是,服務器程序和應用程序互相配合才能給用戶提供服務,而不同應用程序(不同框架)會有不同的函數、功能。 此時,我們就需要一個標準,讓服務器程序和應用程序都支持這個標準,那么,二者就能很好的配合了

    WSGI:wsgi是python web開發的標準,類似于協議。它是服務器程序和應用程序的一個約定,規定了各自使用的接口和功能,以便二和互相配合

WSGI應用程序的部分規定

  1. 應用程序是一個可調用的對象
    可調用的對象有三種:

    1. 一個函數
    2. 一個類,必須實現__call__()方法
    3. 一個類的實例
  2. 這個對象接收兩個參數
    源碼中,我們可以看到,這兩個參數是environ, start_response. 以可調用對象為一個類為例:

    class application:    def __call__(self, environ, start_response):        pass
  3. 可調用對象需要返回一個可迭代的值。以可調用對象為一個類為例:

    class application:    def __call__(self, environ, start_response):        return [xxx]

WSGI服務器程序的部分規定

  1. 服務器程序需要調用應用程序

    def run(application):     #服務器程序調用應用程序    environ = {}     #設定參數    def start_response(xxx):     #設定參數        pass    result = application(environ, start_response)          #調用應用程序的__call__函數(這里應用程序是一個類)    def write(data):               pass    def data in result:          #迭代訪問        write(data)
    服務器程序主要做了以下的事:     1. 設定應用程序所需要的參數     2. 調用應用程序     3. 迭代訪問應用程序的返回結果,并傳給客戶端 

Middleware

middleware是介于服務器程序和應用程序中間的部分,middleware對服務器程序和應用程序是透明的。

對于服務器程序來說,middleware就是應用程序,middleware需要偽裝成應用程序,傳遞給服務器程序對于應用程序來說,middleware就是服務器程序,middleware需要偽裝成服務器程序,接受并調用應用程序

服務器程序獲取到了客戶端請求的URL,需要把URL交給不同的函數處理,這個功能可以使用middleware實現:

# URL Routing middlewaredef urlrouting(url_app_mapping):        def midware_app(environ, start_response):       #函數可調用,包含2個參數,返回可迭代的值          url = environ['PATH_INFO']                 app = url_app_mapping[url]       #獲得對應url的應用程序          result = app(environ, start_response)       #調用應用程序          return result            return midware_app

函數midware_app就是middleware:
一方面,midware_app函數設置了應用程序所需要的變量,并調用了應用程序。所以對于應用程序來說,它是一個服務器程序
另一方面,midware_app函數是一個可調用的對象,接收兩個參數,同時可調用對象返回了一個可迭代的值。所以對于服務器程序來說,它是一個應用程序

寫中間件(middleware)的邏輯:1. middleware需要偽裝成應用程序—> WSGI應用程序的要求 —> 1. 可調用  2. 兩個參數  3. 返回可迭代的值2. middleware需要偽裝成服務器程序 —> WSGI服務器程序的要求 —> 調用應用程序

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

  AUTH_TYPE  CONTENT_LENGTH		#HTTP請求中Content-Length的部分  CONTENT_TYPE			#HTTP請求中Content-Tpye的部分  GATEWAY_INTERFACE  HTTP_*					#包含一系列變量, 如HTTP_HOST,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

舉例:http://localhost:5000/aaa?666, 變量值為:

REQUEST_METHOD=‘GET’SCRIPT_NAME=''SERVER_NAME='localhost'SERVER_PORT=‘5000’PATH_INFO='/aaa'QUERY_STRING='666'SERVER_PROTOCOL='HTTP/1.1'CONTENT_TYPE='text/plain'CONTEN_LENGTH='' HTTP_HOST = 'localhost:8000'HTTP_ACCEPT = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'HTTP_ACCEPT_ENCODING = 'gzip,deflate,sdch'HTTP_ACCEPT_LANGUAGE = 'en-US,en;q=0.8,zh;q=0.6,zh-CN;q=0.4,zh-TW;q=0.2'HTTP_CONNECTION = '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()函數:
start_response是HTTP響應的開始, 它的形式為:start_response(status, response_headers, exc_info=None)
status表示HTTP狀態碼, 比如200 OK
response_headers是一個列表,列表元素是個tuple:(header_name, header_value)
exc_info是個可選參數, 當處理請求的過程中發生錯誤時, 會設置該參數, 同時會調用start_response
舉一個werkzeug官方文檔上的例子,我稍作改進,以進行分析(這段建議看完wsgi.py第二部分的SharedDataMiddleware類再看):

class Shortly(object):    def __init__(self, config):        self.redis = redis.Redis(config['redis_host'], config['redis_port'])    def dispatch_request(self, request):        return Response('Hello World!')     #初始化Response類    def wsgi_app(self, environ, start_response):        request = Request(environ)        response = self.dispatch_request(request)   #response的類型為Response類        print('%%%%%%%%%%%%%%%%%%%%%%%%%%%')        return response(environ, start_response)   #Response的__call__函數就是應用程序,返回迭代對象    def __call__(self, environ, start_response):    	print(self.wsgi_app)        print('erghrgheoghegoierge')        return self. wsgi_app(environ, start_response)def create_app(redis_host='localhost', redis_port=6379, with_static=True):    app = Shortly({        'redis_host':       redis_host,        'redis_port':       redis_port    })    if with_static:    	print('yes')		app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {            '/static':  os.path.join(os.path.dirname(__file__), 'static')        })    print('33333333333333333')    return app
#開啟本地服務器if __name__ == '__main__':    from werkzeug.serving import run_simple    app = create_app()  #創建應用程序的實例    run_simple('127.0.0.1', 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的應用程序!

當運行這個程序的時候:

22222222222222222yes33333333333333333 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat22222222222222222yes33333333333333333	

我們先不糾結為什么讀取了2次。
當我們打開這個網頁,控制臺的輸出為:

<werkzeug.wsgi.SharedDataMiddleware object at 0x1007be7b8>    #說明wsgi_app是SharedDataMiddleware的實例!  erghrgheoghegoierge%%%%%%%%%%%%%%%%%%%%%%%%%%%			#說明執行了原wsgi_app函數中的內容!127.0.0.1 - - [22/May/2015 21:01:25] "GET / HTTP/1.1" 200 -

可以注意到,在本例中,app.wsgi_app這個方法已經變成了一個SharedDataMiddleware類的實例,我很好奇當服務器把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.erros                                                    輸出流,如果出現錯誤,可以寫往這里wsgi.multithread如果應用程序對象可以被同一進程中的另一線程同時調用,這個值為Truewsgi.multiprocess如果應用程序對象可以同時被另一個進程調用,這個值為Truewsgi.run_once如果服務器希望應用程序對象在包含它的進程中只被調用一次,那么這個值為True

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 廊坊市| 靖安县| 海晏县| 江华| 清河县| 德格县| 长丰县| 沾益县| 库伦旗| 铅山县| 绍兴市| 鲁甸县| 和硕县| 拜泉县| 黄石市| 渝北区| 宣恩县| 舒城县| 双辽市| 双鸭山市| 萨迦县| 荣昌县| 新余市| 醴陵市| 常熟市| 瑞安市| 互助| 兰考县| 康乐县| 桃园市| 喜德县| 古浪县| 佛坪县| 迭部县| 龙门县| 普洱| 集安市| 海城市| 卫辉市| 枝江市| 犍为县|