使用 asyncawait

變更日誌

在版本 2.0 中新增。

路由、錯誤處理器、請求前、請求後和拆解函數,如果 Flask 安裝了 async 額外功能(pip install flask[async]),則都可以是協程函數。這允許視圖使用 async def 定義,並使用 await

@app.route("/get-data")
async def get_data():
    data = await async_db_query(...)
    return jsonify(data)

可插拔的基於類別的視圖也支援實作為協程的處理器。這適用於繼承自 flask.views.View 類別的視圖中的 dispatch_request() 方法,以及繼承自 flask.views.MethodView 類別的視圖中的所有 HTTP 方法處理器。

搭配 greenlet 使用 async

當使用 gevent 或 eventlet 來伺服應用程式或修補執行時,需要 greenlet>=1.0。當使用 PyPy 時,需要 PyPy>=7.3.7。

效能

Async 函數需要事件迴圈才能執行。Flask 作為 WSGI 應用程式,使用一個 worker 來處理一個請求/回應週期。當請求進入 async 視圖時,Flask 將在一個執行緒中啟動一個事件迴圈,在那裡執行視圖函數,然後返回結果。

即使對於 async 視圖,每個請求仍然佔用一個 worker。好處是您可以在視圖中執行 async 程式碼,例如進行多個並行的資料庫查詢、對外部 API 的 HTTP 請求等等。但是,您的應用程式一次可以處理的請求數量仍然相同。

Async 本身並不比同步程式碼更快。 Async 在執行並行的 IO 綁定任務時是有益的,但可能不會改善 CPU 綁定任務。傳統的 Flask 視圖仍然適用於大多數使用案例,但 Flask 的 async 支援使得編寫和使用以前無法原生實現的程式碼成為可能。

背景任務

Async 函數將在事件迴圈中執行,直到它們完成,在此階段事件迴圈將停止。這表示當 async 函數完成時,任何尚未完成的其他衍生任務都將被取消。因此,您無法衍生背景任務,例如透過 asyncio.create_task

如果您希望使用背景任務,最好使用任務佇列來觸發背景工作,而不是在視圖函數中衍生任務。考慮到這一點,您可以透過使用 ASGI 伺服器伺服 Flask 並利用 asgiref WsgiToAsgi 適配器來衍生 asyncio 任務,如 ASGI 中所述。這樣做是因為適配器建立了一個持續運行的事件迴圈。

何時改用 Quart

由於實作方式,Flask 的 async 支援不如 async-first 框架效能好。如果您主要有 async 程式碼庫,那麼考慮 Quart 會更有意義。Quart 是基於 ASGI 標準而不是 WSGI 的 Flask 重新實作。這使其能夠處理許多並行請求、長時間運行的請求和 WebSocket,而無需多個 worker 程序或執行緒。

使用 Gevent 或 Eventlet 運行 Flask 以獲得 async 請求處理的許多好處也已經是可能的。這些函式庫修補了底層 Python 函數來完成此操作,而 async/ await 和 ASGI 使用標準、現代的 Python 功能。決定您應該使用 Flask、Quart 還是其他東西最終取決於了解您專案的特定需求。

擴充套件

早於 Flask 的 async 支援的 Flask 擴充套件不期望有 async 視圖。如果它們提供裝飾器來向視圖添加功能,這些裝飾器可能不適用於 async 視圖,因為它們不會等待函數或成為可等待的。它們提供的其他函數也不會是可等待的,並且如果在 async 視圖中呼叫,則可能會是阻塞的。

擴充套件作者可以透過使用 flask.Flask.ensure_sync() 方法來支援 async 函數。例如,如果擴充套件提供視圖函數裝飾器,請在呼叫裝飾函數之前新增 ensure_sync

def extension(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ...  # Extension logic
        return current_app.ensure_sync(func)(*args, **kwargs)

    return wrapper

查看您要使用的擴充套件的變更日誌,以查看它們是否已實作 async 支援,或向它們發出功能請求或 PR。

其他事件迴圈

目前 Flask 僅支援 asyncio。可以覆寫 flask.Flask.ensure_sync() 以變更 async 函數的包裝方式,從而使用不同的函式庫。