使用 async
和 await
¶
變更日誌
在版本 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 函數的包裝方式,從而使用不同的函式庫。