Flask 框架理解(一)


Flask 框架理解(一)

web 服務器 , web 框架 以及 WSGI

這里說的 web 服務器特指純粹的 python HTTP 服務器(比如 Gunicorn,而不是 Apache,Nginx這些)
一般來說, web 服務器負責建立並管理監聽套接字,並且解析所有收到的 HTTP 請求。Web 框架負責 URL 分發,根據 HTTP 中請求路徑生成響應。 而 WSGI(pep 333) 則是 web 服務器調用 web 框架的一個調用慣例。用來解偶 web 服務器和 web 框架。

通過 flask 的源碼能比較清晰的看到這三者關系

    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

    def __call__(self, environ, start_response):
        """The WSGI server calls the Flask application object as the
        WSGI application. This calls :meth:`wsgi_app` which can be
        wrapped to applying middleware."""
        return self.wsgi_app(environ, start_response)

從這里可以看出, flask 的 application 是一個 WSGI 可調用對象。如果去看 Flask Appication 的 run 方法, 就能發現最終是啟動一個 Werkzeug serving.py 中的一個 基於BaseHTTPServer.HTTPServer 實現的 WSGIServer,也就是 web 服務器。證實 web 服務器通過 WSGI 接口調用 web 框架作出響應。這里也能很清楚的看到 web 框架的基礎功能就是分發請求,作出響應。

理解全局變量

在 flask 中有 4 個方便好用的全局變量 current_app, request, session, g。嚴格意義上來說是線程級別的全局變量(具體可看 Werkzeug 的 LocalStack 源碼),也就是只有當前線程能訪問。正因為用的多, 更應該花點時間去理解這幾個變量的含義以及生命周期。

# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))

對於請求-響應模式, 響應需要根據具體的請求來做出合理的響應。 這四個全局變量就是用來提供請求的具體信息,即請求上下文。從上面 wsgi_app 函數中也可以看出,有請求過來, 調用 wsig_app 時, 會構建一個當前請求的上下文實例(RequestContext),將 http 請求原始信息存到 request 屬性, 並將其 push 到當前線程的全局棧 _request_ctx_stack。接着將當前 flask 實例 push 到 _app_ctx_stack, 存儲到上下文實例的 app 屬性中。最后解析請求中的 cookie, 構建 Session 實例存到上下文實例的 session 屬性中。 這樣每個請求的上下文中就攜帶了, http 請求的原始信息, 當前 flask 實例上下文信息, 以及 cookie 提取的 session 信息。 一個請求所需要的信息基本就全部包含了。至於 g 只是當前 flask 實例上下文對象的一個屬性。在響應構造完成后, 會把當前上下文 session 信息存到 cookie,所以裸 flask 框架的 session 全部信息是存在客戶端的 cookie 里面。接着彈出當前請求的上下文, 彈出當前請求的 flask 實例上下文。

def push(self):
        """Binds the request context to the current context."""
        top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)

        # Before we push the request context we have to ensure that there
        # is an application context.
        app_ctx = _app_ctx_stack.top
        if app_ctx is None or app_ctx.app != self.app:
            app_ctx = self.app.app_context()
            app_ctx.push()
            self._implicit_app_ctx_stack.append(app_ctx)
        else:
            self._implicit_app_ctx_stack.append(None)

        if hasattr(sys, 'exc_clear'):
            sys.exc_clear()

        _request_ctx_stack.push(self)

        if self.session is None:
            session_interface = self.app.session_interface
            self.session = session_interface.open_session(
                self.app, self.request
            )

            if self.session is None:
                self.session = session_interface.make_null_session(self.app)

備注:

之前有提到過, web 服務器與 web 框架是分開的,而且 WSGI 也不支持異步服務器。當 web 服務器是異步服務器時,因為 flask 存在線程級全局變量, 且是基於 Werkzeug 中基於 greenlet 的 LocalStack 上實現,所以當異步服務器不是基於 greenlet 時,會存在一定問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM