Flask瞭解一下

什麼是Flask

Flask 是一個微框架(Micro framework),所謂微框架,它就是很輕量級的,做者劃分出了Flask應該負責什麼(請求路由、處理請求、返回響應)、不該該負責什麼(數據庫抽象、表單驗證)。它倡導地是不要重複造輪子,結合社區優秀的庫,使得Flask更加靈活、定製性更強。python

Flask如何處理請求

Flask run起來

先寫一個簡單的Flask應用(main.py)shell

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World'

app.run("127.0.0.1", 80, debug=True)
複製代碼

執行測試數據庫

> python main.py
> curl http://127.0.0.1:80
Hello World
複製代碼

分析源碼

查看一下app.run()函數源碼flask

def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
    ...
    from werkzeug.serving import run_simple
    
    try:
        run_simple(host, port, self, **options)
    finally:
        self._got_first_request = False
複製代碼

核心邏輯是執行run_simple函數,而且第三個參數self是Flask對象安全

看一下Flask類的實現bash

class Flask:
    def wsgi_app(self, environ, start_response):
        # 將請求信息包裝爲一個ctx對象
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                # ctx壓棧
                ctx.push()
                # 分發 處理
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            # ctx 出棧
            ctx.auto_pop(error)

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)
複製代碼

當請求到來時,程序在調用app時,因爲實現了__call__函數,則經過該函數調用了wsgi_app()函數markdown

具體分析wsgi_app函數:多線程

  • 生成request請求對象和請求上下文(封裝在request_context函數裏)
  • 將生成的請求上下文(本次請求的環境)push入棧,存儲。
  • 請求進入預處理(例如before_request),錯誤處理及請求轉發到響應的過程(full_dispatch_request函數)

Local類

Local類是用werkzeug庫實現的一個用於類存儲數據的類,它支持多線程安全存儲。app

使用原理

使用__storage__(dict類型)存儲數據,可是經過獲取到的線程id做爲標識來進行隔離,讓每一個線程讀寫本身線程的數據 __storage__的結構是這樣的框架

{
    "線程id1": {"stack": [ctx,]},
    "線程id2": {"stack": []},
}
複製代碼

實際源碼

from thread import get_ident
class Local(object):
    __slots__ = ("__storage__", "__ident_func__")

    def __init__(self):
        object.__setattr__(self, "__storage__", {})
        object.__setattr__(self, "__ident_func__", get_ident)

    def __getattr__(self, name):
        # 重寫了該方法,首先獲取當前線程id,而後去讀取該線程id下的數據
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        # 重寫了該方法,首先獲取當前線程id,而後去寫入該線程id下的數據
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

    def __delattr__(self, name):
        # 重寫了該方法,首先獲取當前線程id,而後去刪除該線程id下的數據
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)
複製代碼

LocalStack類

LocalStack是基於Local類實現的棧類,因此它支持線程安全。 實際源碼

class LocalStack(object):
    def __init__(self):
        self._local = Local()

    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self._local, "stack", None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)
        return rv

    def pop(self):
        """Removes the topmost item from the stack, will return the old value or `None` if the stack was already empty. """
        stack = getattr(self._local, "stack", None)
        if stack is None:
            return None
        elif len(stack) == 1:
            release_local(self._local)
            return stack[-1]
        else:
            return stack.pop()

 @property
    def top(self):
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None

複製代碼
  1. LocalStack在init中建立了一個Local對象,此時storage是一個空字典
  2. 當調用push時,即傳入線程或進程對象時,先判斷是否已存在,不然新建立一個空間(列表,做爲棧),入棧
  3. 當調用top時,返回棧頂元素
  4. 調用pop時若棧中只剩一個元素,則取出後刪除該棧空間,不然pop棧頂元素
相關文章
相關標籤/搜索