PHPerKaigi 2022 の登壇資料です。 原題は「PHPでEventLoopを書いて非同期処理を完全に理解する」です。
@hanhan1978PHPでEventLoopを書いて非同期処理を完全に理解するPHPerKaigi 2022
View Slide
@hanhan1978同期的なプログラミング言語の目線から非同期処理を理解するPHPerKaigi 2022とりあえず改題
イベントループは関係なかった3すまんな!
@hanhan1978● 富所 亮● 所属株式会社カオナビ Expert● 職業Webアプリケーションエンジニア● ブログhttps://blog.hanhans.net● Yokohama North AMhttps://anchor.fm/yokohama-north-am4
前回までのあらすじ5
2021-11-25 PHP 8.1 ReleaseFibers が導入される6
7「非同期処理が簡単にかけるようになるらしいぜ?」
ここにビジネスチャンスを見出したが....非同期沼に突入分かりにくいプレゼンを行ってしまう8
9https://speakerdeck.com/hanhan1978/php-async-programming
3行でまとめると...- 非同期処理はわからん- Fibers もよくわからん- とりあえず、PHPでPHPを動かせ10すまんかった
11リベンジhttps://speakerdeck.com/hanhan1978/fiber-and-async-request
3行でまとめると...- なぜ Fibers- イベントループってなに?- なぜ 非同期の仕組みがいまさらPHPに入ってくる?12自信作だったが、スライドが荒かった
本日は合体整理版13
● 非同期処理とは● 2つの視点● イベントループ● まとめ目次14
● 非同期処理とは● 2つの視点● イベントループ● まとめ目次15
同期的なプログラミング記述したとおりの順番で動作するようなプログラミングモデル非同期プログラミング独立して発生するイベントに対する処理を記述するための並行プログラミング手法の総称16高野祐輝. (2021). 並行プログラミング入門 5章 (初版.). オライリー・ジャパン.
17PHPアプリケーションは同期的なBlocking IOが基本
18よくある MVC フレームワークのコード例
19プログラムは書いた順番に順次実行されていく
20入出力処理が行われるとプログラムは結果を待つ
21DB検索の処理シーケンスクエリ結果が返ってくるまでメインの処理は待つブロッキングIOと言う
22非同期プログラミングの例
23単純なAPIコールの例
24ここで処理が待たない
25IO処理の結果を待たないメインスレッドは、待ち時間の間に他の処理を行えるノンブロッキングIO という
26実行順序は直感と反する①③②慣れれば分かるんだが ......
入出力処理の結果が、いつ返ってくるか分からないので、結果が戻ってきたときに実行する処理をコールバックで渡しておく27
28非同期プログラミングの利点
29ブロッキング ノンブロッキングCPUを効率的に利用できる
マルチスレッドは難しすぎたが非同期プログラミングであれば、普通の人間でもギリギリ許容できそう。Promise, Async/Await などを使えば、非同期処理も扱いやすくなる。30
世の中の潮流としては、マルチコア-> コアの利用効率を上げることで、プログラムの処理効率を上げるスループットの向上31
● 非同期処理とは● 2つの視点● イベントループ● まとめ目次32
33非同期処理を考える時の二つの視点
341リクエスト複数リクエスト(プロセス)
351リクエスト単位の視点
36
37ToDoリストを検索するバックエンドの処理(例)非同期処理チャンス
38ToDoリストを検索するバックエンドの処理(例)2つめのクエリが1つ目のクエリに依存している無邪気に非同期処理を書くと、整合性のあるデータを取得できない
39コールバックPHPの仮想コード
40コールバックPHPの仮想コード結果出力時には、検索が終わってなければいけない。非同期処理をしてしまうと、データ取得前に結果出力するはめになる
しょうがないので、全部コールバックにする....こうして、コールバック地獄が生まれる41
42コールバックPHPの仮想コード
43コールバックPHPの仮想コード同期的プログラミングのコードのほうがはるかに見通しが良い。処理時間も変わらない......
コールバックは、直列に実行され、データが戻ってくるまで待つことになるので、非同期処理の書き方をしたところで、スループットが上がっていない。※相互に依存した処理の場合、1リクエスト単位で見ると同期でも非同期でも処理時間は変わらない44
Fibers が いわゆる PHP アプリケーション制作において役に立つ機能ではないと言われるのは、この辺。45
とはいえ1リクエスト単位の視点でも非同期処理が役に立ちそうなケースはある46
47例えば、APIリクエストを3回行う必要がある場合かつ、それぞれのAPIコールは相互に依存していない。
48こんな風に処理をまとめられたら、効率的
そういった用途には古来より curl_multi というものがあるFibers は別に使わなくても問題ない...49
しかし、1リクエスト内でIO処理を束ねるようなケースは、そんなに多くない。レアケースに対して、本気に殴りに行くような改善よりも、もっと一般的なケースの改善をするほうが意味がある。50
1リクエスト単位で考えた場合PHPで非同期プログラミングを行うのは、あまり効果的であるとは思えない。51
52余談
JavaScript が基本的にノンブロッキングな処理を行う理由は、ブラウザを操作しているユーザーを待たせないため53
54JSのローディングイメージ例https://webdevtrick.com/lazy-load-images/
ノンブロッキングを徹底することで、ブラウザ利用者の体験に寄与している。このへんはプログラミング言語の根本の思想に関わっている55
56複数リクエストの視点
話を単純化するために、CPUが1コアでウェブサーバーは1リクエストずつ処理していくものとする。57
581リクエストを以下のように図示する※斜線部分は IO 以外の処理を行っているものとする
59通常の PHP ウェブアプリケーションではリクエストはそれぞれ独立に直列で処理される
60いわゆる PHP ウェブサーバーの構成
今までの普通のPHPウェブアプリケーションでは、IO処理がもったいないと感じても、その間の資源を他に割り当てられなかった。61
62いわゆる PHP ウェブサーバーの構成Nginx も PHP-FPM も個別の PHP プロセスの処理状況を細かく監視できない
もし、リクエストをまたいで非同期処理が行えたら、IO待ちの時に、他のリクエストを処理することで効率的にリクエストをさばくことができる。63
64
実際に Fibers が利用が想定されているのは、リクエストをまたいだパターンが多そうリクエストをまたいで、処理の停止・実行を行うためには、全てのリクエスト処理が、PHPのメイン処理から実行されている必要がある65
66私が、PHP が PHP を実行する必要があると言っていたのはコレ。
この世界観については、すでに Swoole 、 ReactPHP、AmPHP などで実現されているこのように、PHPの主処理が停止せずにリクエストを実行していくという処理パターンの文脈でイベントループがでてくる67
68余談
69Mastering Swoole PHP - Bruce DouSwoole 作者による解説著作全体の2/3がPHPの処理モデルやIOモデルの説明となっていて、非同期を取り巻く状況を雑観できる。
● 非同期処理とは● 2つの視点● イベントループ● まとめ目次70
71イベントループとは?
72https://en.wikipedia.org/wiki/Event_loopイベントループの構成要素Wiki から想像して作図
73イベントループの構成要素リクエストはQueueで直列化される (DeMultiplex)
74イベントループの構成要素Event Loop 側はシングルスレッドで処理できる(Reactor Pattern)
75実装はループ処理例えば、配列にイベントが追加されループで逐次処理されるのも、立派なイベントループ
イベントループで実行されるEvent が Fiber オブジェクトになっていればSuspendRestartをメインスレッドから抽象的に扱うことができる。76
77一番のポイント
フレームワークの利用者側の視点で考えると同期的プログラミングを継続できる同期的プログラミングを行ったまま、非同期プログラミングの恩恵を受けることができるそんな非同期フレームワークを、Fibers を使うことで抽象的に理解しやすく作れる78
今後、Promise や Async/Await が PHP にもたらされる可能性はあるが、そもそも同期的であることに意味がある場合が多そうなので、利用シーンは限られそう79
● 非同期処理とは● 2つの視点● イベントループ● まとめ目次80
非同期処理、Fibers、イベントループこの3つのキーワードがセットになって語られることが多いが、その理由はリクエストをまたいだ非同期処理を行うことが、PHPウェブアプリケーションにとって、もっとも現実的で徳が多そうだから81
82Fibers を使った Event Loop の例実装方法はたくさんある
実際は、ev拡張などを利用すれば、現状でもイベントループを簡単に書くことは出来る。Fibers はイベントループから実行された処理スレッドの停止・再開判断を抽象的に便利に行える機能と考えると分かりやすい83
84とはいえ、書いてみないと実感しづらい
85雑なスライドだが実装サンプルを載せたhttps://speakerdeck.com/hanhan1978/fiber-and-async-request
APIリクエストの非同期実行を生PHP -> Generator -> Fiber と抽象化していくサンプルFibers は、抽象化の道具なんだね!が実感できる86
@hanhan1978相談・指摘・その他 下記のTwitterアカウントにどうぞ87