摘要:中有一個非常重要的概念每個應(yīng)用都是一個可調(diào)用的對象。它規(guī)定了的接口,會調(diào)用,并傳給它兩個參數(shù)包含了請求的所有信息,是處理完之后需要調(diào)用的函數(shù),參數(shù)是狀態(tài)碼響應(yīng)頭部還有錯誤信息。一般來說,嵌套的最后一層是業(yè)務(wù)應(yīng)用,中間就是。
文章屬于作者原創(chuàng),原文發(fā)布在個人博客。
WSGI所有的 python web 框架都要遵循 WSGI 協(xié)議,如果對 WSGI 不清楚,可以查看我之前的介紹文章。
在這里還是要簡單回顧一下 WSGI 的核心概念。
WSGI 中有一個非常重要的概念:每個 python web 應(yīng)用都是一個可調(diào)用(callable)的對象。在 flask 中,這個對象就是 app = Flask(__name__) 創(chuàng)建出來的 app,就是下圖中的綠色 Application 部分。要運行 web 應(yīng)用,必須有 web server,比如我們熟悉的 apache、nginx ,或者 python 中的 gunicorn ,我們下面要講到的 werkzeug 提供的 WSGIServer,它們是下圖的黃色 Server 部分。
NOTE: 圖片來源。
Server 和 Application 之間怎么通信,就是 WSGI 的功能。它規(guī)定了 app(environ, start_response) 的接口,server 會調(diào)用 application,并傳給它兩個參數(shù):environ 包含了請求的所有信息,start_response 是 application 處理完之后需要調(diào)用的函數(shù),參數(shù)是狀態(tài)碼、響應(yīng)頭部還有錯誤信息。
WSGI application 非常重要的特點是:它是可以嵌套的。換句話說,我可以寫個 application,它做的事情就是調(diào)用另外一個 application,然后再返回(類似一個 proxy)。一般來說,嵌套的最后一層是業(yè)務(wù)應(yīng)用,中間就是 middleware。這樣的好處是,可以解耦業(yè)務(wù)邏輯和其他功能,比如限流、認(rèn)證、序列化等都實現(xiàn)成不同的中間層,不同的中間層和業(yè)務(wù)邏輯是不相關(guān)的,可以獨立維護(hù);而且用戶也可以動態(tài)地組合不同的中間層來滿足不同的需求。
WSGI 的內(nèi)容就講這么多,我們來看看 flask 的 hello world 應(yīng)用:
from flask import Flask app = Flask(__name__) @app.route("/") def hello_world(): return "Hello, World!" if __name__ == "__main__": app.run()
這里的 app = Flask(__name__) 就是上面提到的 Application 部分,但是我們并沒有看到 Server 的部分,那么它一定是隱藏到 app.run() 內(nèi)部某個地方了。
啟動流程應(yīng)用啟動的代碼是 app.run() ,這個方法的代碼如下:
def run(self, host=None, port=None, debug=None, **options): """Runs the application on a local development server.""" from werkzeug.serving import run_simple # 如果host 和 port 沒有指定,設(shè)置 host 和 port 的默認(rèn)值 127.0.0.1 和 5000 if host is None: host = "127.0.0.1" if port is None: server_name = self.config["SERVER_NAME"] if server_name and ":" in server_name: port = int(server_name.rsplit(":", 1)[1]) else: port = 5000 # 調(diào)用 werkzeug.serving 模塊的 run_simple 函數(shù),傳入收到的參數(shù) # 注意第三個參數(shù)傳進(jìn)去的是 self,也就是要執(zhí)行的 web application try: run_simple(host, port, self, **options) finally: self._got_first_request = False
NOTE:為了閱讀方便,我刪除了注釋和不相干的部分,下面所有的代碼都會做類似的處理,不再贅述。
這個方法的內(nèi)容非常簡單:處理一下參數(shù),然后調(diào)用 werkzeug 的 run_simple。需要注意的是:run_simple 的第三個參數(shù)是 self,也就是我們創(chuàng)建的 Flask() application。因為 WSGI server 不是文章的重點,所以我們就不深入講解了?,F(xiàn)在只需要知道它的功能就行:監(jiān)聽在指定的端口,收到 HTTP 請求的時候解析為 WSGI 格式,然后調(diào)用 app 去執(zhí)行處理的邏輯。對應(yīng)的執(zhí)行邏輯在 werkzeug.serving:WSGIRequestHandler 的 run_wsgi 中有這么一段代碼:
def execute(app): application_iter = app(environ, start_response) try: for data in application_iter: write(data) if not headers_sent: write(b"") finally: if hasattr(application_iter, "close"): application_iter.close() application_iter = None
可以看到 application_iter = app(environ, start_response) 就是調(diào)用代碼獲取結(jié)果的地方。
要調(diào)用 app 實例,那么它就需要定義了 __call__ 方法,我們找到 flask.app:Flask 對應(yīng)的內(nèi)容:
def __call__(self, environ, start_response): """Shortcut for :attr:`wsgi_app`.""" return self.wsgi_app(environ, start_response) def wsgi_app(self, environ, start_response): """The actual WSGI application. """ # 創(chuàng)建請求上下文,并把它壓棧。這個在后面會詳細(xì)解釋 ctx = self.request_context(environ) ctx.push() error = None try: try: # 正確的請求處理路徑,會通過路由找到對應(yīng)的處理函數(shù) response = self.full_dispatch_request() except Exception as e: # 錯誤處理,默認(rèn)是 InternalServerError 錯誤處理函數(shù),客戶端會看到服務(wù)器 500 異常 error = e response = self.handle_exception(e) return response(environ, start_response) finally: if self.should_ignore_error(error): error = None # 不管處理是否發(fā)生異常,都需要把棧中的請求 pop 出來 ctx.auto_pop(error)
上面這段代碼只有一個目的:找到處理函數(shù),然后調(diào)用它。除了異常處理之外,我們還看到了 context 相關(guān)的內(nèi)容(開始有 ctx.push(),最后有 ctx.auto_pop()的邏輯),它并不影響我們的理解,現(xiàn)在可以先不用管,后面會有一篇文章專門介紹。
繼續(xù)往后看,full_dsipatch_request 的代碼如下:
def full_dispatch_request(self): """Dispatches the request and on top of that performs request pre and postprocessing as well as HTTP exception catching and error handling. """ self.try_trigger_before_first_request_functions() try: request_started.send(self) rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) return self.finalize_request(rv)
這段代碼最核心的內(nèi)容是 dispatch_request,加上請求的 hooks 處理和錯誤處理的內(nèi)容。
NOTE:self.dispatch_request() 返回的是處理函數(shù)的返回結(jié)果(比如 hello world 例子中返回的字符串),finalize_request 會把它轉(zhuǎn)換成 Response 對象。
在 dispatch_request 之前我們看到 preprocess_request,之后看到 finalize_request,它們里面包括了請求處理之前和處理之后的很多 hooks 。這些 hooks 包括:
第一次請求處理之前的 hook 函數(shù),通過 before_first_request 定義
每個請求處理之前的 hook 函數(shù),通過 before_request 定義
每個請求正常處理之后的 hook 函數(shù),通過 after_request 定義
不管請求是否異常都要執(zhí)行的 teardown_request hook 函數(shù)
dispatch_request 要做的就是找到我們的處理函數(shù),并返回調(diào)用的結(jié)果,也就是路由的過程。我們下一篇文章來講!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/38372.html
摘要:我們知道響應(yīng)分為三個部分狀態(tài)欄版本狀態(tài)碼和說明頭部以冒號隔開的字符對,用于各種控制和協(xié)商服務(wù)端返回的數(shù)據(jù)。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應(yīng)用啟動流程 flask 源碼解析:路由 flask 源碼解析:上下文 flask 源碼解析:請求 flask 源碼解析:響應(yīng) response 簡介 在 f...
摘要:簡介官網(wǎng)上對它的定位是一個微開發(fā)框架。另外一個必須理解的概念是,簡單來說就是一套和框架應(yīng)用之間的協(xié)議。功能比較豐富,支持解析自動防止攻擊繼承變量過濾器流程邏輯支持代碼邏輯集成等等。那么,從下一篇文章,我們就正式開始源碼之旅了 文章屬于作者原創(chuàng),原文發(fā)布在個人博客。 flask 簡介 Flask 官網(wǎng)上對它的定位是一個微 python web 開發(fā)框架。 Flask is a micro...
摘要:可以看到,雖然是同樣的請求數(shù)據(jù),在不同的階段和不同組件看來,是完全不同的形式。請求還有一個不那么明顯的特性它不能被應(yīng)用修改,應(yīng)用只能讀取請求的數(shù)據(jù)。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應(yīng)用啟動流程 flask 源碼解析:路由 flask 源碼解析:上下文 flask 源碼解析:請求 flask 源碼解...
摘要:但是這些對象和全局變量不同的是它們必須是動態(tài)的,因為在多線程或者多協(xié)程的情況下,每個線程或者協(xié)程獲取的都是自己獨特的對象,不會互相干擾。中有兩種上下文和。就是實現(xiàn)了類似的效果多線程或者多協(xié)程情況下全局變量的隔離效果。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應(yīng)用啟動流程 flask 源碼解析:路由 flas...
閱讀 4320·2021-09-24 09:47
閱讀 1192·2021-09-03 10:33
閱讀 2077·2019-08-30 11:13
閱讀 1039·2019-08-30 10:49
閱讀 1762·2019-08-29 16:13
閱讀 2052·2019-08-29 11:28
閱讀 3102·2019-08-26 13:31
閱讀 3638·2019-08-23 17:14