asyncawaitの使用 Using ``async`` and ``await``

Changelog

バージョン 2.0 で追加.

もしFlaskがasync追加指定を使って(pip install flask[async]を使って)インストールされた場合、経路制御(Route)、エラー処理(error handler)、リクエスト前処理、リクエスト後処理、廃棄処理(teardown)の関数は全てコルーチンの関数にできます。これにより、viewはasync defを使って定義し、awaitが使えるようになります。(訳注: コルーチン、async、awaitはPythonの非同期・並列処理サポート機能に関する概念やキーワードです。コルーチンは関数のように呼び出せるもので、generatorや非同期用処理なども含んだ、関数よりも汎用的なものを指す概念です。async、awaitは非同期処理・並列処理をプログラミングするときに使えるキーワードです。Python公式の「コルーチンとTask」のドキュメントも参照ください) Routes, error handlers, before request, after request, and teardown functions can all be coroutine functions if Flask is installed with the ``async`` extra (``pip install flask[async]``). This allows views to be defined with ``async def`` and use ``await``.

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

差し込み可能(pluggable)なクラスベースのviewはコルーチンとして実装された処理器(handler)もサポートします。これはflask.views.MethodViewクラスを継承したview の中にある全てのHTTPメソッドのハンドラと同様、flask.views.Viewクラスから継承したviewの中のdispatch_request()メソッドにも当てはまります。(訳注: MethodViewクラスでは「get」「post」などのメソッドが対応するHTTPメソッドを処理するview関数に相当するハンドラになり、Viewクラスではdispatch_request()を実行するとview関数が返ります。「クラスに基づいたビュー(Class-based Views)」参照) Pluggable class-based views also support handlers that are implemented as coroutines. This applies to the :meth:`~flask.views.View.dispatch_request` method in views that inherit from the :class:`flask.views.View` class, as well as all the HTTP method handlers in views that inherit from the :class:`flask.views.MethodView` class.

WindowsのPython 3.8でのasyncの使用 Using ``async`` on Windows on Python 3.8

Windowsでは、Python 3.8はasyncioに関連するバグがあります。もしValueError: set_wakeup_fd only works in main threadのようなものに出くわしたら、Python 3.9にアップグレードしてください。 Python 3.8 has a bug related to asyncio on Windows. If you encounter something like ``ValueError: set_wakeup_fd only works in main thread``, please upgrade to Python 3.9.

greenletと合わせたasyncの使用 Using ``async`` with greenlet

geventまたはgreenletを使ってアプリケーションを提供したり実行環境に手を加えたりしているときは、greenlet>=1.0が必要です。PyPyを使っているときは、PyPy>=7.3.7が必要です。 When using gevent or eventlet to serve an application or patch the runtime, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is required.

性能 Performance

async関数を走らせるにはevent loopが必要です。WSGIアプリケーションであるFlaskは、リクエスト/レスポンスの1回のサイクルを処理するのに1つのworkerを使います。リクエストがasyncのviewに来たとき、Flaskはスレッドの中でevent loopを開始し、そこでview関数を走らせ、そして結果を返します。 Async functions require an event loop to run. Flask, as a WSGI application, uses one worker to handle one request/response cycle. When a request comes in to an async view, Flask will start an event loop in a thread, run the view function there, then return the result.

各リクエストは、asyncのviewであっても、変わらず1つのworkerと結びつきます。有利になる点はviewの中で、例えばデータベースへの問合せ、外部APIへのHTTPリクエストなどを複数並列に作成するような、非同期のコードを実行できることです。しかしながら、アプリケーションが同時に処理できるリクエストの数は同じままです。 Each request still ties up one worker, even for async views. The upside is that you can run async code within a view, for example to make multiple concurrent database queries, HTTP requests to an external API, etc. However, the number of requests your application can handle at one time will remain the same.

Asyncは同期処理するコードと比べて本質的に高速なわけではありません。 Asyncは並列な入出力処理に結び付けられるタスク(concurrent IO-bound tasks)の性能には恩恵がありますが、CPUに結び付けられるタスク(CPU-bound tasks)の改善にはおそらく影響はありません。従来のFlaskのviewでもほとんどの使用状況では適切でしょうが、Flaskのasyncサポートは、以前だと元々はできなかったコードを書いて使えるようにします。 **Async is not inherently faster than sync code.** Async is beneficial when performing concurrent IO-bound tasks, but will probably not improve CPU-bound tasks. Traditional Flask views will still be appropriate for most use cases, but Flask's async support enables writing and using code that wasn't possible natively before.

バックグラウンドのタスク Background tasks

asyncの関数は、完了するまでevent loopの中で走り、完了した段階でevent loopは停止します。これは、asyncの関数が完了したときにはまだ完了していない、追加で産み出された(spawn)あらゆる(子供の)タスクは中止されることを意味します。従って、例えばasyncio.create_taskをとおして、バックグラウンドの(子供の)タスクを生み出すことはできません。 Async functions will run in an event loop until they complete, at which stage the event loop will stop. This means any additional spawned tasks that haven't completed when the async function completes will be cancelled. Therefore you cannot spawn background tasks, for example via ``asyncio.create_task``.

もしバックグラウンドのタスクを使いたい願望がある場合、view関数の中で(子供の)タスクを産み出す(spawn)よりも、バックグラウンドのタスクを働かせる引き金を引く、タスクのキューを使うのが最良です。それに注意しながら、ASGIサーバと一緒にFlaskを提供し、ASGIで説明されているようにasgirefのWsgiToAsgiアダプタを利用することで、(子供の)asyncioのタスクを生み出すことができます。これは、そのアダプタが走り続けるevent loopを作るように働きます。 If you wish to use background tasks it is best to use a task queue to trigger background work, rather than spawn tasks in a view function. With that in mind you can spawn asyncio tasks by serving Flask with an ASGI server and utilising the asgiref WsgiToAsgi adapter as described in :doc:`deploying/asgi`. This works as the adapter creates an event loop that runs continually.

Quartを代わりに使うとき When to use Quart instead

Flaskのasyncのサポートは、その実装方法のために、async第一優先のフレームワークに比べれば高性能ではありません。もしasyncをふんだんに使ったコードがあれば、Quartを検討するのは合理的です。Qartは、WSGIの代わりにASGI標準をベースにした、Flaskの再実装です。これは、多くの並列なリクエスト、時間のかかるリクエスト、websocketsを、複数のworkerプロセスまたはスレッドを必要とせずに処理できるようにします。 Flask's async support is less performant than async-first frameworks due to the way it is implemented. If you have a mainly async codebase it would make sense to consider `Quart`_. Quart is a reimplementation of Flask based on the `ASGI`_ standard instead of WSGI. This allows it to handle many concurrent requests, long running requests, and websockets without requiring multiple worker processes or threads.

asyncなリクエスト処理の恩恵の多くを得るために、GeventまたはEventletを使ってFlaskを走らせることも既に可能です。これらのライブラリ(GeventやEventlet)はPythonの低レベルの機能に手を加えて(patch)達成しますが、asyncawaitとASGIは標準的な、最近のPythonの機能になります。Flask、Quart、もしくは他の何かを使うかどうかの決定は、自分のプロジェクトに固有なニーズの理解に最終的には依存します。 It has also already been possible to run Flask with Gevent or Eventlet to get many of the benefits of async request handling. These libraries patch low-level Python functions to accomplish this, whereas ``async``/ ``await`` and ASGI use standard, modern Python capabilities. Deciding whether you should use Flask, Quart, or something else is ultimately up to understanding the specific needs of your project.

Flask拡張 Extensions

Flaskのasyncサポートより前からあるFlask拡張は、asyncのviewを予想していません。もしそれらがviewに機能を追加するデコレータ(訳注: 例えば「def func1():」のような関数定義の行の前に「@aaa.bbb」のような行を書いて機能追加するもの)を提供している場合、それらは関数をawaitしたりawaitableであったりはしないため、おそらくasyncのviewと一緒では働きません。それらのFlask拡張が提供するデコレータ以外の機能もawaitableではなさそうであり、そしてasyncのviewの中で呼ばれた場合は、おそらく(非同期・並列処理はせず)ブロックするでしょう。 Flask extensions predating Flask's async support do not expect async views. If they provide decorators to add functionality to views, those will probably not work with async views because they will not await the function or be awaitable. Other functions they provide will not be awaitable either and will probably be blocking if called within an async view.

Flask拡張の作者は、flask.Flask.ensure_sync()メソッドを利用することでasyncの関数をサポートできます。例えば、もしFlask拡張がview関数のデコレータを提供する場合、修飾される関数を呼び出す前にensure_syncを加えます、 Extension authors can support async functions by utilising the :meth:`flask.Flask.ensure_sync` method. For example, if the extension provides a view function decorator add ``ensure_sync`` before calling the decorated function,

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

    return wrapper

自分が使いたいFlask拡張は、asyncのサポートを実装しているか見るためにchengelogをチェックするか、そのFlask拡張に機能要求もしくはPull Requestを出してください。 Check the changelog of the extension you want to use to see if they've implemented async support, or make a feature request or PR to them.

その他のevent loop Other event loops

現時点では、Flaskはasyncioだけをサポートします。他のライブラリを使うためにasyncの関数の包み方(how async functions are wrapped)を変えるために、flask.Flask.ensure_sync()を上書きすることは可能です。 At the moment Flask only supports :mod:`asyncio`. It's possible to override :meth:`flask.Flask.ensure_sync` to change how async functions are wrapped to use a different library.