成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

flask 源碼解析:應(yīng)用啟動流程

whatsns / 483人閱讀

摘要:中有一個非常重要的概念每個應(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)用 werkzeugrun_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: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) 就是調(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

相關(guān)文章

  • flask 源碼解析:響應(yīng)

    摘要:我們知道響應(yīng)分為三個部分狀態(tài)欄版本狀態(tài)碼和說明頭部以冒號隔開的字符對,用于各種控制和協(xié)商服務(wù)端返回的數(shù)據(jù)。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應(yīng)用啟動流程 flask 源碼解析:路由 flask 源碼解析:上下文 flask 源碼解析:請求 flask 源碼解析:響應(yīng) response 簡介 在 f...

    wslongchen 評論0 收藏0
  • flask源碼走讀

    摘要:另外,如果你對模板渲染部分的內(nèi)容感興趣,也可以考慮閱讀文檔文檔文檔源碼閱讀,可以參考下面的函數(shù)打斷點,再測試一個請求,理清過程。 Flask-Origin 源碼版本 一直想好好理一下flask的實現(xiàn),這個項目有Flask 0.1版本源碼并加了注解,挺清晰明了的,我在其基礎(chǔ)上完成了對Werkzeug的理解部分,大家如果想深入學(xué)習(xí)的話,可以參考werkzeug_flow.md. 閱讀前 為...

    Coly 評論0 收藏0
  • flask 源碼解析:簡介

    摘要:簡介官網(wǎng)上對它的定位是一個微開發(fā)框架。另外一個必須理解的概念是,簡單來說就是一套和框架應(yīng)用之間的協(xié)議。功能比較豐富,支持解析自動防止攻擊繼承變量過濾器流程邏輯支持代碼邏輯集成等等。那么,從下一篇文章,我們就正式開始源碼之旅了 文章屬于作者原創(chuàng),原文發(fā)布在個人博客。 flask 簡介 Flask 官網(wǎng)上對它的定位是一個微 python web 開發(fā)框架。 Flask is a micro...

    megatron 評論0 收藏0
  • flask 源碼解析:請求

    摘要:可以看到,雖然是同樣的請求數(shù)據(jù),在不同的階段和不同組件看來,是完全不同的形式。請求還有一個不那么明顯的特性它不能被應(yīng)用修改,應(yīng)用只能讀取請求的數(shù)據(jù)。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應(yīng)用啟動流程 flask 源碼解析:路由 flask 源碼解析:上下文 flask 源碼解析:請求 flask 源碼解...

    weizx 評論0 收藏0
  • flask 源碼解析:上下文

    摘要:但是這些對象和全局變量不同的是它們必須是動態(tài)的,因為在多線程或者多協(xié)程的情況下,每個線程或者協(xié)程獲取的都是自己獨特的對象,不會互相干擾。中有兩種上下文和。就是實現(xiàn)了類似的效果多線程或者多協(xié)程情況下全局變量的隔離效果。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應(yīng)用啟動流程 flask 源碼解析:路由 flas...

    Labradors 評論0 收藏0

發(fā)表評論

0條評論

whatsns

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<