(jp) =
そこで、PHP 8.1 でのファイバーの使用について詳細なブログ記事を書くつもりでした。 それらを一から説明するために、基本的な例から始めようとしていました。 アイデアは、非同期 HTTP リクエストを送信し、ファイバーを使用してそれらを並行して処理することでした。
しかし、それらをいじってみると、RFC が冗談ではなく、「Fiber API はアプリケーションレベルのコードで直接使用されることは想定されていません。Fiber は、基本的な低レベルのフロー制御 API を提供して、より高いアプリケーションコードで使用されるレベルの抽象化」.

したがって、この道をたどって物事を複雑にしすぎるのではなく、概念的にファイバーとは何か、アプリケーション コードでファイバーがほとんど使用されない理由、そして結局のところ非同期 PHP をどのように利用できるかについて説明します。
最初に、少し理論を説明します。
3 つの HTTP リクエストを送信し、それらを組み合わせた結果を処理するとします。 これを行う同期方法は、最初のものを送信し、応答を待ってから、2番目のものを送信して待機することです。
このようなプログラムの流れを、できるだけわかりやすい図で表してみましょう。 このチャートは上から下に読む必要があり、下に行くほど時間は進みます。 各色は 1 つの HTTP 要求を表します。 各リクエストの色付きの部分は実際に実行されている PHP コードを表し、サーバーの CPU が作業を行っている場所を表し、透明なブロックは待機時間を表します。リクエストはネットワーク経由で送信する必要があり、他のサーバーはそれを処理して送り返す必要があります。 . 応答が届いて初めて、作業を再開できます。

これは同期実行フローです: 送信、待機、処理、繰り返し。
並列処理の世界ではリクエストを送りますが、 しないでください 待つ。 次に、次のリクエストを送信し、続いて別のリクエストを送信します。 それだけ それから すべてのリクエストを待ちますか。 そして、待っている間、リクエストの 1 つがすでに終了しているかどうかを定期的に確認します。 その場合は、すぐに処理できます。

待機時間をより最適に使用しているため、このようなアプローチによって実行時間がどのように短縮されるかがわかります。
ファイバーは PHP 8.1 の新しいメカニズムで、これらの並列実行パスをより効率的に管理できるようにします。 ジェネレーターと yield、しかし、ファイバーはこのユースケース用に特別に設計されているため、大幅に改善されています。
リクエストごとに 1 つのファイバーを作成し、リクエストの送信後にファイバーを一時停止します。 3 つのファイバーをすべて作成したら、それらをループして、1 つずつ再開します。 そうすることで、ファイバーはリクエストがすでに終了しているかどうかを確認し、そうでない場合は再び一時停止し、そうでない場合は応答を処理して最終的に終了します。
ご覧のとおり、ファイバーは、プログラムの分離された部分の実行フローを開始、一時停止、および再開するためのメカニズムです。 ファイバーは「グリーン スレッド」とも呼ばれます。つまり、同じプロセスに実際に存在するスレッドです。 これらのスレッドはオペレーティング システムではなく、ランタイム (この場合は PHP ランタイム) によって管理されます。 それらは、コスト効率の高い管理方法です。 いくつか 並列プログラミングの形式。
ただし、真に非同期のものは何も追加しないことに注意してください。すべてのファイバーは同じ PHP プロセスに存在し、一度に実行できるのは 1 つだけです。 それらをループして待機中にチェックするのはメインプロセスであり、そのループは「イベントループ」と呼ばれることがよくあります。
並列処理の難しい部分は、ファイバーやジェネレーターをループする方法や、使用したいメカニズムではありません。 操作を開始し、それを外部サービスに引き渡し、必要なときにのみ結果を非ブロックの方法で確認できるようにすることです。
前の例では、リクエストを送信して後で必要なときにそのレスポンスを確認できると想定していましたが、実際には思ったほど簡単ではありません。
その通りです。I/O を処理する PHP の関数のほとんどには、このノンブロッキング機能が組み込まれていません。 実際、これを行う関数はほんの一握りしかなく、それらを使用するのは非常に面倒です。
次のように、ノンブロッキングに設定できるソケットの例があります。
[$read, $write] = stream_socket_pair(
STREAM_PF_UNIX,
STREAM_SOCK_STREAM,
STREAM_IPPROTO_IP
);
stream_set_blocking($read, false);
stream_set_blocking($write, false);
使用することで stream_socket_pair()、双方向通信に使用できる 2 つのソケットが作成されます。 ご覧のとおり、次を使用してノンブロッキングに設定できます stream_set_blocking().
この例を実装して、3 つのリクエストを送信するとします。 そのためにソケットを使用することもできますが、その上に HTTP プロトコルを自分で実装する必要があります。 ファイバーとソケットを使用して HTTP GET リクエストを送信する方法を示すために、Reddit で小さな概念実証を共有したユーザーである nox7 がまさにそれを行いました。 アプリケーション コードでこれを行うことに本当に関心がありますか?
少なくとも私にとっては、答えは「いいえ」です。 これはまさにRFCが警告したことです。 私はそれについて怒っていません。 代わりに、既存の非同期フレームワークの 1 つ、Amp または ReactPHP を使用することをお勧めします。
たとえば、ReactPHP を使用すると、次のように記述できます。
$loop = React\EventLoop\Factory::create();
$browser = new Clue\React\Buzz\Browser($loop);
$promises = [
$browser->get('https://example.com/1'),
$browser->get('https://example.com/2'),
$browser->get('https://example.com/3'),
];
$responses = Block\awaitAll($promises, $loop);
これは、ソケット接続を手動で作成するよりも良い例です。 これが RFC の意味です。アプリケーション開発者はファイバーについて心配する必要はありません。これは、Amp や ReactPHP などのフレームワークの実装の詳細です。
しかし、それは私たちに疑問を投げかけます: 私たちがすでに発電機でできることと比較して、繊維の利点は何ですか? RFC では次のように説明されています。
スタックレス ジェネレーターとは異なり、各ファイバーには独自の呼び出しスタックがあり、深くネストされた関数呼び出し内で一時停止できます。 中断ポイントを宣言する (つまり、Fiber::suspend() を呼び出す) 関数は、Generator インスタンスを返す必要がある yield を使用する関数とは異なり、戻り値の型を変更する必要はありません。
ファイバーは、Array_map に提供される関数や Iterator オブジェクトの foreach によって呼び出されるメソッドなど、PHP VM 内から呼び出されるものを含む、任意の関数呼び出しで中断できます。
構文と柔軟性の両方において、ファイバーが大幅に改善されていることは明らかです。 しかし、それらは、たとえば「ゴルーチン」を備えた Go と比較すると、まだ何もありません。
非同期 PHP がフレームワークのオーバーヘッドなしで主流になるにはまだ多くの機能が欠けています。ファイバーは正しい方向への良い一歩ですが、まだそこにはいません。
それで、それがあります。 Amp、ReactPHP、または小規模な非同期 PHP フレームワークのいずれかのメンテナーでない場合、ファイバーについて実際に言うことはあまりありません。 さらに多くのフレームワークやライブラリがそれらを組み込み始めるのでしょうか?
一方、Swoole もあります。これは、実際にいくつかのコア関数をノンブロッキングに変更する PHP 拡張機能です。 Swoole自体は中国のプロジェクトであり、英語に関してはあまり文書化されていないことが多いですが、最近Laravelはそれとの最初のパーティ統合を発表しました. PHP をより非同期のモデルに移行する場合は、おそらくこれがより良い戦略です。必要に応じて、Swoole やその他の拡張機能を Laravel や Symfony などのフレームワークと統合しますか?
未来がもたらすものを見るのは興味深いでしょう!
//platform.twitter.com/widgets.js