async/await への道#
彭総の記事「この Python はダメだ」を見て、いろいろな感慨がわいてきましたので、私も水を差してみます。
まず、私たちの会社は新しいものに挑戦することに勇気を持っていると言えるかもしれません。具体的な表れは、コミュニティの関連する基本的なサービスのアップデートに追従し、新しいものに挑戦することです。ええ、去年の 6 月から現在まで、私たちの会社ではかなり長い間 async/await を本番環境で使用しており、新しいフレームワークである Sanic を導入していますが、残念ながら、私たちは今 async/await に一時的にさよならを言わなければなりません。
なぜ async/await を選んだのか?#
私たちのチームの具体的なシナリオに関係しています。私たちのチームでは、さまざまな URL に基づいて異なるサブサービスからデータをリクエストし、それを組み合わせて次の統一処理を行うというシナリオがあります。そのため、データソースがますます複雑になる状況では、従来の同期的な方法は非常に無力です。
当時、次の選択肢がありました。
- プロセス / スレッドプールを維持し、一般的なプロセス / スレッドを使用してリクエストを処理する
- Gevent などのサードパーティの coroutine+EventLoop ソリューションを使用する
- async/await + asyncio の組み合わせを使用する
まず、1 は私たちにとっては除外されました。理由は非常に単純です。重すぎるからです。2 も最初は一時的に除外されました。当時、見た目があまりにも不明瞭な monkey-patch の方法に対して恐怖心を抱いていました。
そこで、私たちは喜んで 3 を選びました。async/await + asyncio の組み合わせを使用することにしました。
実際、最初の効果は素晴らしかったです。しかし、後でこの操作が実際には大変なものであることに気付くでしょう。
async/await をやめる#
なぜ async/await をやめるのか?#
実は、古い問題がいくつかあります。
1. コードの感染性#
Python の公式の coroutine の実装は、yield/yield from をベースにした変更です。つまり、async/await の方法に従ってエントリーポイントから下に進んでいく必要があります。したがって、同じプロジェクト内には同期 / 非同期のコードが溢れており、信じてください、メンテナンスはある意味で災難です。
2. エコシステムと互換性#
現在の async/await の互換性は本当に大変です。現在の async/await は、純粋な Python コードにのみ適用されます。ここで問題があります。私たちがプロジェクトで使用している mysqlclient などの C 拡張など、async/await はカバーできません。
同時に、現在の状況では、async/await の周りは本当に大きな問題です。バグがいたるところにあり、修正されない状態です。たとえば、aiohttp の https リンクに関するリークの問題などです。また、Sanic の混乱した設計構造などもあります。
私たちは新しい技術を調査するとき、通常、新しいものが既存の技術をカバーできるかどうか、周辺の要件を満たすことができるかどうかを重点的に調査します。現時点では、async/await の周辺はまだ満たされていません。
3. パフォーマンスの問題#
現時点では、PEP 3156 で提案された asyncio は、async/await の公式推奨イベントループです。しかし、現在の公式の実装は多くの点で不足しています。たとえば、aiohttp の https リンクのリーク問題は、実際には asyncio の SSL 関連の実装に遡ることができます。したがって、使用する場合は、通常、サードパーティのループを選択します。現時点では、サードパーティのループは libuv/libev をベースにした実装方法が主流です。このため、性能は Gevent と同等であり、さらに低い場合もあります(PyFrameObject のメンテナンスコストを回避しているためです)。
したがって、私たちの髪のために、現在、私たちは徐々に async/await をオンラインコードから退役させることにしました。最も遅くとも今年の年末までに、async/await を廃止する操作を完了します。
代替案は何ですか?#
現時点では、代替として Gevent を使用する準備をしています(うまいこといきそうですね)。
理由は非常に単純です:
- 現在、成熟しており、明らかな大きなバグはありません
- 周辺の開発が成熟しており、純粋な Python コードには Monkey-Patch を使用して既存のコードを移行できます。C 拡張には、豆瓣内部で検証された Greenify が解決策として提供されます
- 基礎となる Greenlet は、必要な場合に簡単にコンテキストトレースを行うための API を提供しています。
async/await に関するその他のコメント#
まず、async/await は素晴らしいものですが、現在は実用的ではありません。これについては、コミュニティが関連する使用方法をさらに探求する必要があります。
ここで、多くの人が私に尋ねたいと思うでしょう。「ASGI と Django Channel のようなものについてはどう思いますか?」
まず、ASGI は実際には async/await のために設計されたものではありません。最初の設計思想は、PEP333/PEP3333 WSGI プロトコルがますます複雑なネットワークプロトコルモデルに対応できなくなった問題を解決することでした。そして、Django Channel も同じ問題を解決するために、ASGI の実装として作成されました(最初は WebSocket を解決するためです)。これらのソリューションは多くの問題を解決しています。たとえば、Django Channel 2.0 では、WebSocket のボードキャストを簡単に実現できます。しかし、彼らは async/await とはあまり関係がありません。
今年の PyCon 2018 で、Django チームのコアメンバーが ASGI と Django Channel 2.0 の async/await サポートについて紹介しました。将来的には、Django も対応する可能性があります。しかし、問題は、async/await を使用すると、現在のエコシステム全体がまだ心配であり、最も脆弱な点です。
だから、こんにちは async/await、さようなら async/await!