非同期PHPの真実:Fibers、epoll、そしてPHP 8.6

長年Laravelを使ってきました。同期的なPHPを使用してきました。リクエストが届き、プロセスが実行され、レスポンスが返される。非同期コードが必要になったことは一度もありませんでした。

しかし、新しいPHP 8.6のPolling APIについて知りました。それが私の見方すべてを変えました。

非同期が内部でどのように動作するのか、私が学んだことをここにまとめます。

I/Oの問題

APIを呼び出すとき、PHPプロセスは待機します。 例: $response = Http::get('https://api.example.com');

もしその呼び出しに300msかかると、CPUはその300ms間、何も行いません。スリープ状態のままです。これがブロッキングI/Oです。

3つのAPI呼び出しがある場合:

  • API A: 300ms
  • API B: 400ms
  • API C: 200ms

逐次実行の合計:900ms。 非同期の合計:400ms(最も遅い呼び出しの時間)。

非同期にすることで、データの待機中にプロセスが他の作業を行うことができます。

select vs. epoll

非同期を実現するには、どのソケットに準備ができたデータがあるかを知る必要があります。

1. select()

PHPはバージョン4からstream_select()を使用してきました。これは、カーネルにソケットのリストを監視するように依頼することで動作します。 問題点:データが届くたびに、リスト全体を再度スキャンしなければなりません。これは再スキャンによるコスト(rescan tax)となります。また、接続数に約1024個という制限があります。

2. epoll (Linux) / kqueue (macOS)

これらはカーネルの機能です。リストをスキャンする代わりに、カーネルは「準備完了リスト(ready-list)」を保持します。そして、どの特定のソケットが準備できているかだけを教えてくれます。これにより、余計な負荷をかけることなく、数千の接続までスケールさせることができます。

epollはPHPの機能ではありません。Linuxの機能です。Go、Rust、Node.jsもすべてこれを使用しています。

Fibers:一時停止ボタン

PHP 8.1でFibersが導入されました。私はFibersが勝手に動き出すものだと思っていました。しかし、そうではありません。

Fiberは一時停止したビデオのようなものです。誰かが $fiber->resume() を呼び出すまで、停止したままです。

イベントループとは、いつ resume() を呼び出すかを決定する、単なるPHPコードの一片に過ぎません。

非同期I/Oには3つの要素が必要です:

  • 一時停止:Fibers (PHP 8.1 コア)
  • 決定:イベントループ (プレーンなPHPコード)
  • 把握:カーネルポーリング (epoll/kqueue)

PHP 8.6より前、PHPには「一時停止」と「決定」の要素はありましたが、「把握」の部分は古い select() か、低速なC拡張に依存していました。

PHP 8.6はこのギャップを埋めます。ネイティブのPolling APIをコアに取り入れたのです。これにより、PHPは追加の拡張なしで epollkqueue を直接使用できるようになります。

まとめ

もしあなたがPHP-FPMでLaravelを使っているなら、今日すぐに何かを変える必要はありません。

しかし、これだけは理解しておいてください。非同期は魔法ではありません。単に待ち時間を管理するための賢い方法なのです。

ただコードを消費するのをやめましょう。簡単なスクリプトを実行し、壊してみてください。それが真の学び方です。

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