flask 原始碼解析:應用啟動流程

發表於2017-02-09

文章屬於作者原創,原文釋出在個人部落格

這是 flask 原始碼解析系列文章的其中一篇,本系列所有文章列表:

WSGI

所有的 python web 框架都要遵循 WSGI 協議,如果對 WSGI 不清楚,可以檢視我之前的介紹文章

在這裡還是要簡單回顧一下 WSGI 的核心概念。

WSGI 中有一個非常重要的概念:每個 python web 應用都是一個可呼叫(callable)的物件。在 flask 中,這個物件就是 app = Flask(__name__) 建立出來的 app,就是下圖中的綠色 Application 部分。要執行 web 應用,必須有 web server,比如我們熟悉的 apache、nginx ,或者 python 中的 gunicorn ,我們下面要講到的 werkzeug 提供的 WSGIServer,它們是下圖的黃色 Server 部分。

flask 原始碼解析:應用啟動流程

NOTE: 圖片來源

Server 和 Application 之間怎麼通訊,就是 WSGI 的功能。它規定了 app(environ, start_response) 的介面,server 會呼叫 application,並傳給它兩個引數:environ 包含了請求的所有資訊,start_response 是 application 處理完之後需要呼叫的函式,引數是狀態碼、響應頭部還有錯誤資訊。

WSGI application 非常重要的特點是:它是可以巢狀的。換句話說,我可以寫個 application,它做的事情就是呼叫另外一個 application,然後再返回(類似一個 proxy)。一般來說,巢狀的最後一層是業務應用,中間就是 middleware。這樣的好處是,可以解耦業務邏輯和其他功能,比如限流、認證、序列化等都實現成不同的中間層,不同的中間層和業務邏輯是不相關的,可以獨立維護;而且使用者也可以動態地組合不同的中間層來滿足不同的需求。

WSGI 的內容就講這麼多,我們來看看 flask 的 hello world 應用:

這裡的 app = Flask(__name__) 就是上面提到的 Application 部分,但是我們並沒有看到 Server 的部分,那麼它一定是隱藏到 app.run() 內部某個地方了。

啟動流程

應用啟動的程式碼是 app.run() ,這個方法的程式碼如下:

NOTE:為了閱讀方便,我刪除了註釋和不相干的部分,下面所有的程式碼都會做類似的處理,不再贅述。

這個方法的內容非常簡單:處理一下引數,然後呼叫 werkzeugrun_simple。需要注意的是:run_simple 的第三個引數是 self,也就是我們建立的 Flask() application。因為 WSGI server 不是文章的重點,所以我們就不深入講解了。現在只需要知道它的功能就行:監聽在指定的埠,收到 HTTP 請求的時候解析為 WSGI 格式,然後呼叫 app 去執行處理的邏輯。對應的執行邏輯在 werkzeug.serving:WSGIRequestHandlerrun_wsgi 中有這麼一段程式碼:

可以看到 application_iter = app(environ, start_response) 就是呼叫程式碼獲取結果的地方。

要呼叫 app 例項,那麼它就需要定義了 __call__ 方法,我們找到 flask.app:Flask 對應的內容:

上面這段程式碼只有一個目的:找到處理函式,然後呼叫它。除了異常處理之外,我們還看到了 context 相關的內容(開始有 ctx.push(),最後有 ctx.auto_pop()的邏輯),它並不影響我們的理解,現在可以先不用管,後面會有一篇文章專門介紹。

繼續往後看,full_dsipatch_request 的程式碼如下:

這段程式碼最核心的內容是 dispatch_request,加上請求的 hooks 處理和錯誤處理的內容。

NOTE:self.dispatch_request() 返回的是處理函式的返回結果(比如 hello world 例子中返回的字串),finalize_request 會把它轉換成 Response 物件。

dispatch_request 之前我們看到 preprocess_request,之後看到 finalize_request,它們裡面包括了請求處理之前和處理之後的很多 hooks 。這些 hooks 包括:

  • 第一次請求處理之前的 hook 函式,通過 before_first_request 定義
  • 每個請求處理之前的 hook 函式,通過 before_request 定義
  • 每個請求正常處理之後的 hook 函式,通過 after_request 定義
  • 不管請求是否異常都要執行的 teardown_request hook 函式

dispatch_request 要做的就是找到我們的處理函式,並返回撥用的結果,也就是路由的過程。我們下一篇文章來講!

相關文章