同期型PHP開発者が非同期PHPについて学んだこと

長年Laravelを使ってきました。使っていたのは同期型(sync)のPHPです。リクエストが届き、プロセスが実行され、レスポンスが返される。非同期(async)の必要性は一度も感じませんでした。

しかし、新しいPHP 8.6のPolling APIについて知りました。それが、PHPのタスク処理に対する私の見方を変えました。

非同期がどのように機能するのか、その仕組みを解説します。

ブロッキングI/Oの問題点

APIを呼び出すと、コードは待機状態になります。 例:$response = Http::get('https://api.example.com');

もしそのAPIに300msかかるとしたら、PHPプロセスはその300msの間、何も行いません。スリープ状態で留まり、メモリを保持し、ワーカーのスロットを占有し続けます。もしすべてのワーカーがスリープ状態になれば、サーバーは新しいリクエストを受け付けられなくなります。

非同期による解決策

非同期を使えば、その300msの間に他の作業を行うことができます。待機する代わりに、他のタスクを実行するのです。

しかし、データがいつ届いたのかをどうやって知るのでしょうか?ここでカーネルの出番となります。

ポーリングの進化

  1. select() PHP 4からstream_select()が存在します。これはカーネルに対して「これらのソケットのいずれかでデータが準備できていますか?」と問いかけます。 問題は「再スキャンのコスト(rescan tax)」です。10,000件の接続がある場合、毎回リスト全体をカーネルに送らなければなりません。これは低速であり、限界に突き当たります。

  2. epoll / kqueue これは言語の機能ではなく、カーネルの機能です。Linuxはepollを使用し、macOSはkqueueを使用します。 リスト全体をスキャンする代わりに、カーネルは「準備完了リスト(ready-list)」を保持します。データがある特定のソケットだけを教えてくれるのです。これにより、追加コストなしで数千もの接続へとスケールさせることができます。

  3. Fibers (PHP 8.1) Fibersを使うと、コールスタック内のどこでも関数の実行を一時停止できます。 Fiberは自分自身で目覚めることはありません。それは一時停止したYouTube動画のようなものです。再び再生するには、誰かが $fiber->resume() を呼び出す必要があります。

失われていたミッシングリンク:PHP 8.6

非同期I/Oには3つの要素が必要です: • 一時停止(Pause):Fibers(現在はPHPコアに実装済み) • 決定(Decide):イベントループ(純粋なPHPコード) • 検知(Know):カーネルポーリング(ここが欠けていた部分)

これまでPHPには、古いツールやC拡張を使わずに、どのソケットが準備できているかを「検知」するネイティブな方法がありませんでした。

PHP 8.6はこのギャップを埋めます。コアにネイティブなPolling APIをもたらします。これにより、Linuxでは自動的にepollを、Macではkqueueを使用するようになります。

全体像

非同期は魔法ではありません。イベントループとは、Fiberに対していつresume()を呼び出すかを決定する、単なるPHPコードに過ぎません。

Fibersは一時停止する能力を提供します。 epollはいつ再開すべきかを知るためのインテリジェンスを提供します。

もしあなたが同期型PHPのみを使用しているなら、今すぐLaravelアプリを変更する必要はありません。しかし、このモデルを理解しておくことで、ReactPHPやAmpのような非同期ライブラリをより簡単にマスターできるようになります。

単に消費するだけでなく、構築しましょう。実際にコードを動かして、どのように機能するかを確認してみてください。

出典: https://dev.to/alamriku/sync-php-developer-hisebe-async-php-bujhte-giye-yaa-shikhlaam-fibers-epoll-aar-php-86-462j