2024.12.19
システムの穴を運用でカバーしようとしてミス多発… バグが大量発生、決算が合わない状態から業務効率化を実現するまで
リンクをコピー
記事をブックマーク
福田隼也氏:ここまで並行には処理をしていないので、上のループの中で、もう少しawaitの動きを見ていきたいと思います。
考え中のお客さま3人から注文を受けてみましょう。先ほどのコルーチンです。上のところで注文を受けて、下のところでオーダーを受け付けるというところ。それぞれのコルーチンにawaitを付けて、menuを受け取ってみたいと思います。3人は考える時間が異なるので、引数を受け取るように変えて実行しています。
並行実行できていれば、3秒で終わります。結果、6秒かかっています。順番に実行されているようです。さらに動きを確認するために、orderのmenuの出力をそれぞれ受け取った直後にprintしてみましょう。
すると、逐次実行されていることがわかります。1つ目の注文を聞いて、聞き終わってから2つ目の注文を受けているのがわかると思います。コルーチンをawaitするだけでは、並行にはなりません。
ここで登場するのがタスクです。「Taskオブジェクト」というものがあります。コルーチンをラップして実行状態をもちます。イベントループにコルーチンを登録します。
作る方法は大きく2つです。Python3.7で追加された「asyncio.create_task」。あとは並列実行を助ける「gather」があるので覚えておいてください。
「初めてのタスク」です。orderの中で、create_taskでコルーチンのthinking_orderを渡してタスクを受け取ります。自分にawait taskをして、menuをもつコルーチンを受け取る流れになっています。
では、実行してみましょう。処理は3秒で終わっています。先ほどのように、awaitの間にprintを入れてみましょう。コルーチンがいつのタイミングで実行されているのかを確認したいと思います。
menu = await task、という真ん中の下にprintを入れていて、この下も、awaitした後にループで、awaitした後にループで、となっています。どうでしょう。最初のタスク。ここですね。ここが実行された時点で、登録されているすべてのタスクが実行されていることがわかるかと思います。
awaitされた時に中断、再開するのは、すでにイベントループに登録済みのタスクであることがわかるかと思います。
また、結果をまとめて受け取るgatherというものがあります。これは引数にコルーチン、またはタスクを受け取って、すべてが終わり次第、結果を投入した順で返します。
「tasks」というところに登録してあるコルーチンをまとめて実行して、結果を渡した順で返って来るというものになっています。
考え中のお客さまは、3人とも検索しないと決められないんです。なので検索をしてみましょう。なぜ検索をするかというと、asyncioは外部からの入力が欲しいからです。まずは、httpクライアントのrequestsを利用してみましょう。
「long_think」で、responseで受け取る値に「Google」で「おいしいお蕎麦は?」というのを投げています。これを実行してみましょう。
3秒かかりました。どうなんでしょう。この結果だとそれっぽく動いています。この中には実はブロッキングな処理が紛れ込んでいます。asyncio.runにdebugというものがあるんです。もう一度実行してみましょう。すると、時間のかかっている処理を検出してくれます。
メッセージは省略していますが「requests」を利用しているところで、0.915となっています。long_thinkで、requestsを行っているルーチンに何かあるよということがわかります。
asyncioはイベントループを利用して非同期IOを実現するため、IOの部分のモジュールもasyncioに対応している必要があります。requestsはasyncio対応していないため、イベントループである彼女の手のひらの上に乗っかっていない状態です。そのため、asyncio対応のhttpクライアントで実行を試したいと思います。
ここでは、asyncio対応のクライアントの「httpx」を利用してみましょう。実行してみると、処理の終了までに1秒かからずに完了していることがわかるかと思います。
このように、asyncioを利用するモジュールには、モジュール自体がasyncioに対応している必要があることがわかりました。
さて、「asyncioは怖くない」。コルーチンは関数にasyncを付けるだけで、肝はawaitです。タスク、切り替わる先のコルーチン。イベントループに登録しておくと、よきに計らって切り替えてくれます。イベントループは、登録したタスクをawaitのタイミングで切り替えます。ただし、切り替える先のものなので、asyncio対応している必要があります。
それぞれで必要な機能です。コルーチンにはasync/await。タスクにはcreate_taskで、コルーチンを受け取れます。イベントループに登録をします。そしてgatherです。引数にコルーチンをまとめて指定して、まとめて実行結果を受け取れます。イベントループでは、asyncio.runを紹介しました。get_running_loopやget_event_loopがありますが、余力があればまずはこれを抑えましょう。
ここまでお話ししていた中で、いくつかの武器を手に入れました。まずは並行処理。並行処理は処理を少しずつやります。そして非同期IO。外部からの入力を同期しないという概念でした。そしてこれらを実現しているのがasyncioです。コルーチンのタスクとして、イベントループに登録をして処理を並行に実行します。
タイミングとしてキーになるのがawaitです。これは外部からの入力、非同期IO、例ではhttp検索のタイミングをタスクとイベントループによって切り替えます。ちょっとasyncioはわがままで、イベントループで管理する外部と連携している部分は、asyncioに対応している必要があることがわかりました。
ここまでがasyncioです。お待たせしましたDjangoです。やっとここまできました。本日のお品書きはこんな感じです。
タイトルはこんな感じです。Django Async Viewの使い所。ASGI対応とasync view、asyncioのよきところを享受できるようになります。IOの発生する処理で、効果を発揮します。
例えばAPIのリクエストやIoTデバイスとのMQTT。Djangoでそれらを利用したい場合になります。
先ほどのお蕎麦の注文例ですね。この中で、「asyncio.run()はしないの?」と思うかもしれませんが、実は知らず知らずのうちにしています。
ASGI Djangoはどうやって実行しますか? UvicornやHypercornなどのASGI製のWebサーバーを利用しているかと思います。ASGIでPythonのWebサーバーとPythonのWebフレームなどをつなぐのに、ASGIサーバー側でイベントループの実行しています。
非同期な処理を行いたい場合には、ASGIアプリケーションとして実行することがあります。ただ、非同期な処理の中にも、同期の処理、asyncio対応していないものがあるかもしれません。
その場合、同期処理にはDjangoが用意している同期処理を非同期処理にする「sync_to_async」を利用しましょう。
sync_to_asyncは、同期関数を受け取って、それをラップして非同期関数を返します。直接、ラッパーまたはデコレータとして利用できます。
asyncio対応していない同期処理をイベントループに登録できます。Django側は、sync_to_asyncを用意していますが、これはDjangoに限った話ではありません。ほかにも「concurrent.futures」という、threadingとmultiprocessingを容易に使える標準ライブラリがあるので、これらを利用してイベントループに登録できます。
ThreadPoolExecutorとtProcessPoolExecutorを利用して、同期処理をブロッキングすることなくイベントループを利用できるようになります。非同期にはできますが、実行にはコストがかかり、速度に影響がある場合もあります。
ASGI対応とasync viewです。これによって、Djangoでもasyncioと非同期IO/並行処理の恩恵にあずかることができるようになりました。また、同期処理についても、concurrent.futuresを使ったラップが可能です。ただ、まだ残っている同期処理があるんです。最後の砦のDjangoのORMが残っています。
Node.jsの作者であるRyan Dahl氏は「データベースにクエリを実行している間、ソフトウェアは何をしているのですか?」と尋ねました。もちろん答えは何もありませんでした。データベースが応答するのを待っていました。
Djangoを利用する上で、大きな魅力の1つであるORMはasyncio対応していません。非同期IOの利点を外部の入出力として説明してきました。主に今回の話の中では、ネットワークでした。
もちろんデータベースへのアクセスも有効です。そこができると全体が速くなります。今触っているアプリケーションを思い出してください。Webでデータベースが非同期になったら、早くなることが想像ができるはずです。
DjangoコアデベロッパーのAndrew Godwinさんは、去年の「PyConline AU 2020」で 「Taking Django's ORM Async」というお話をしていました。
その中で、「APIのデザインが重要だ。ただasync DBAPIというPEPはまだない、課題はある」とお話をしていました。その話の中で、安全な非同期プログラミング、すべてが非同期でエラーを起こせない世界を実現する。非同期を可能な限り最高なものにするとおっしゃっていたのが印象的でした。
具体的なバージョンについてです。Djangoのフォーラムの中での最近のやりとりです。具体的なバージョンは、もう少しかかりそうです。「my initial goal was 4.0, but...」というふうに続きます。
asyncioが多少怖くなくなったと思っていただけるとうれしいです。Djangoのasync view、同期と非同期を併せて、IOバウンドな処理を中心に利用してください。Async ORM、楽しみですね。Pythonでは、ほかにもasyncio対応のORMやアダプターがすでにあります。そちらと併せて利用するのも良いかもしれません。
また、DjangoはOSSです。リポジトリをのぞいてみるのもいいかもしれません。以上です。ご清聴ありがとうございました。
2024.12.20
日本の約10倍がん患者が殺到し、病院はキャパオーバー ジャパンハートが描く医療の未来と、カンボジアに新病院を作る理由
2024.12.19
12万通りの「資格の組み合わせ」の中で厳選された60の項目 532の資格を持つ林雄次氏の新刊『資格のかけ算』の見所
2024.12.16
32歳で成績最下位から1年でトップ営業になれた理由 売るテクニックよりも大事な「あり方」
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
PR | 2024.12.20
モンスター化したExcelが、ある日突然崩壊 昭和のガス工事会社を生まれ変わらせた、起死回生のノーコード活用術
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2024.12.18
「社長以外みんな儲かる給与設計」にした理由 経営者たちが語る、優秀な人材集め・会社を発展させるためのヒント
2024.12.17
面接で「後輩を指導できなさそう」と思われる人の伝え方 歳を重ねるほど重視される経験の「ノウハウ化」
2024.12.13
ファシリテーターは「しゃべらないほうがいい」理由 入山章栄氏が語る、心理的安全性の高い場を作るポイント
2024.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
Climbers Startup JAPAN EXPO 2024 - 秋 -
2024.11.20 - 2024.11.21
『主体的なキャリア形成』を考える~資格のかけ算について〜
2024.12.07 - 2024.12.07
Startup CTO of the year 2024
2024.11.19 - 2024.11.19
社員の力を引き出す経営戦略〜ひとり一人が自ら成長する組織づくり〜
2024.11.20 - 2024.11.20
「確率思考」で未来を見通す 事業を成功に導く意思決定 ~エビデンス・ベースド・マーケティング思考の調査分析で事業に有効な予測手法とは~
2024.11.05 - 2024.11.05