flask 源碼解析:應用啓動流程

文章屬於做者原創,原文發佈在我的博客python

WSGI

全部的 python web 框架都要遵循 WSGI 協議,若是對 WSGI 不清楚,能夠查看我以前的介紹文章nginx

在這裏仍是要簡單回顧一下 WSGI 的核心概念。web

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

WSGI

NOTE: 圖片來源flask

Server 和 Application 之間怎麼通訊,就是 WSGI 的功能。它規定了 app(environ, start_response) 的接口,server 會調用 application,並傳給它兩個參數:environ 包含了請求的全部信息,start_response 是 application 處理完以後須要調用的函數,參數是狀態碼、響應頭部還有錯誤信息。服務器

WSGI application 很是重要的特色是:它是能夠嵌套的。換句話說,我能夠寫個 application,它作的事情就是調用另一個 application,而後再返回(相似一個 proxy)。通常來講,嵌套的最後一層是業務應用,中間就是 middleware。這樣的好處是,能夠解耦業務邏輯和其餘功能,好比限流、認證、序列化等都實現成不一樣的中間層,不一樣的中間層和業務邏輯是不相關的,能夠獨立維護;並且用戶也能夠動態地組合不一樣的中間層來知足不一樣的需求。app

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

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() 內部某個地方了。函數

啓動流程

應用啓動的代碼是 app.run() ,這個方法的代碼以下:post

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 沒有指定,設置 host 和 port 的默認值 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

    # 調用 werkzeug.serving 模塊的 run_simple 函數,傳入收到的參數
    # 注意第三個參數傳進去的是 self,也就是要執行的 web application
    try:
        run_simple(host, port, self, **options)
    finally:
        self._got_first_request = False

NOTE:爲了閱讀方便,我刪除了註釋和不相干的部分,下面全部的代碼都會作相似的處理,再也不贅述。

這個方法的內容很是簡單:處理一下參數,而後調用 werkzeugrun_simple。須要注意的是:run_simple 的第三個參數是 self,也就是咱們建立的 Flask() application。由於 WSGI server 不是文章的重點,因此咱們就不深刻講解了。如今只須要知道它的功能就行:監聽在指定的端口,收到 HTTP 請求的時候解析爲 WSGI 格式,而後調用 app 去執行處理的邏輯。對應的執行邏輯在 werkzeug.serving:WSGIRequestHandlerrun_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) 就是調用代碼獲取結果的地方。

要調用 app 實例,那麼它就須要定義了 __call__ 方法,咱們找到 flask.app:Flask 對應的內容:

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.
    """
    # 建立請求上下文,並把它壓棧。這個在後面會詳細解釋
    ctx = self.request_context(environ)
    ctx.push()
    error = None

    try:
        try:
            # 正確的請求處理路徑,會經過路由找到對應的處理函數
            response = self.full_dispatch_request()
        except Exception as e:
            # 錯誤處理,默認是 InternalServerError 錯誤處理函數,客戶端會看到服務器 500 異常
            error = e
            response = self.handle_exception(e)
        return response(environ, start_response)
    finally:
        if self.should_ignore_error(error):
            error = None
        # 無論處理是否發生異常,都須要把棧中的請求 pop 出來
        ctx.auto_pop(error)

上面這段代碼只有一個目的:找處處理函數,而後調用它。除了異常處理以外,咱們還看到了 context 相關的內容(開始有 ctx.push(),最後有 ctx.auto_pop()的邏輯),它並不影響咱們的理解,如今能夠先不用管,後面會有一篇文章專門介紹。

繼續日後看,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)

這段代碼最核心的內容是 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 要作的就是找到咱們的處理函數,並返回調用的結果,也就是路由的過程。咱們下一篇文章來說!

相關文章
相關標籤/搜索