ホームページ  >  記事  >  バックエンド開発  >  なぜ PHP には非同期プログラミングが必要なのでしょうか? PHP非同期プログラミングの詳しい説明(例付き)

なぜ PHP には非同期プログラミングが必要なのでしょうか? PHP非同期プログラミングの詳しい説明(例付き)

不言
不言転載
2019-01-23 10:11:043233ブラウズ

この記事では、なぜ PHP に非同期プログラミングが必要なのかを説明します。 PHP 非同期プログラミングの詳細な説明 (例付き) は、ある種の参考価値があります。必要な友人が参照できるように、お役に立てれば幸いです。

PHP 非同期に関する私の知識はまだ非常に混乱しています。それを整理するためにこの記事を書いています。間違いがあるかもしれません。

従来の php-fpm プロセスがリクエストを実行するとき、同じ同時実行性を達成するにはいくつのプロセスを生成する必要があるか。さらに悪いことに、各リクエストを再コンパイルして実行する必要があるため、同時実行性がまったく増加しないことです。したがって、Swooleと WorkerMan は、中国で人気のある 2 つの常駐メモリ フレームワークです [1]。これら 2 つのフレームワークの原理は、イベント ループを使用してプログラムをメモリ内に保持し、外部リクエストを待機し、高い同時実行性を実現することです。

なぜ非同期が必要なのか

最初に例を見てみましょう

作業ディレクトリに新しいファイルslowServer.phpを作成します

<?php
sleep(5); // 5秒后才能返回请求
echo &#39;done&#39;;

サービスを開始します

$ php -S localhost:8081 slowServer.php

別のターミナルを開いて依存関係をインストールします

$ pecl install event # 安装 event 扩展
$ composer require workerman/workerman
$ composer require react/http-client:^0.5.9

新しいファイルを作成しますworker.php

require_once __DIR__ . &#39;/vendor/autoload.php&#39;;
use Workerman\Worker;
use Workerman\Connection\AsyncTcpConnection;
use Amp\Artax\Response;

$http_worker = new Worker("http://0.0.0.0:8082");

$http_worker->count = 1; // 只开一个进程

$http_worker->onMessage = function($connection, $host) {
    echo 1;
    $data = file_get_contents('http://localhost:8081');
    $connection->send($data);
};

Worker::runAll();

サーバーを開きます

php worker.php start

Openブラウザーに 2 つのタブがあり、両方とも URL http://localhost:8082 を開きます。このとき、ターミナルは「1」を出力し、しばらくすると再び「1」を出力することがわかりますが、これは最初のリクエストを処理するときに 8081 サーバーが 8081 の戻り待ちでブロックされているためです。リクエストが完了すると、2 番目のリクエストの処理が開始されます。つまり、リクエストは 1 つずつ実行されますが、php-fpm と同様に、同じ同時実行数を達成するには、いくつのプロセスを確立する必要があります。コードを変更します

$http_worker->onMessage = function($connection, $host) {
    echo 1;
    $loop    = Worker::getEventLoop();
    $client  = new \React\HttpClient\Client($loop);
    $request = $client->request('GET', 'http://localhost:8081');
    $request->on('error', function(Exception $e) use ($connection) {
        $connection->send($e);
    });
    $request->on('response', function ($response) use ($connection) {
        $response->on('data', function ($data) use ($connection) {
            $connection->send($data);
        });
    });
    $request->end();
};

次にサービスを開いてブラウザでリクエストを開始すると、リクエストの直後に 2 番目の「1」が出力され、最初のリクエストがまだ終了していないことがわかります。これは、プロセスがブロックされなくなり、同時実行の量がプロセスの数ではなく CPU とメモリに依存することを示します。

非同期が必要な理由

上記の例から、reactphp フレームワークが http リクエストを非同期にし、onMessage 関数をノンブロッキングにすることは明らかです。次のリクエストを処理します。つまり、CPU ループは 8081 が返されるのを待ち、epoll 待機に変わります。

非同期の意味は、CPU を IO 待機から解放し、他のコンピューティング タスクを処理できるようにすることです。 フレームワークを使用して非同期を実現する方法を知りたい場合は、ここを参照してください。 WorkerMan は、ReactPHP または独自の AsyncTcpConnection と組み合わせることで、すでに多くの IO リクエストの非同期ニーズを満たすことができます。これらのフレームワークがどのように非同期であるかについて引き続き説明します。

どの場所を非同期にする必要があるか

上記の例を通じて、実行には CPU が不要であることはすでにわかっていますが、io を待機しているときは ioプロセスを非同期にする必要があります。

イベント ループの実装

上記の例では、reactphp を使用して http リクエストを非同期に変換します。実際、WorkerMan フレームワーク自体も非同期です。見てみましょう。 WorkerMan の仕組み onMessage 関数を有効にして、リクエストを非同期に受け入れます。まず、次のファイルを作成しますreact.php

<?php
$context = stream_context_create();
$socket = stream_socket_server(&#39;tcp://0.0.0.0:8081&#39;, $errno, $errmsg, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,$context); // 注册一个 fd(file descriptor)

function react($socket){
    $new_socket = stream_socket_accept($socket, 0, $remote_address);
    echo 1;
}

$eventBase = new EventBase();
$event = new Event($eventBase, $socket, Event::READ | Event::PERSIST, &#39;react&#39;, $socket); // 注册一个事件,检测 fd 有没有写入内容
$event->add();
$eventBase->loop(); // 开始循环

実行開始

$ php react.php

別のターミナルで実行

telnet 127.0.0.1 8081

この時点で、最初のターミナル出力「1」が表示されます。 。

以前に「php は epoll を使用する」という記事を書きましたが、これがこの記事の基礎です。この記事では、#

$event->add($seconds);
というタイミングでイベント コールバックを実装していますが、ここでは fd がコンテンツを書き込んだかどうかを検出することでイベント コールバックを実装しており、このプロセスには CPU の関与は必要ありません。 fd にコンテンツが書き込まれると、関数「react」が呼び出され、CPU が使用され始めます。この時点で、プロセスが別の非同期リクエスト (reactphp フレームワークを使用した Web ページのリクエストなど) を実行すると、プログラムは CPU を解放します。この時点で別のリクエストが受信されると、コールバックして別の「react」関数を実行できます。 。これにより、同時実行の量が増加します。

Coroutine

Generater

これは、ジェネレーター http:// php の PHP 公式ドキュメントです。 net/manual/zh/lang...

<?php
function gen_one_to_three() {
    for ($i = 1; $i <= 3; $i++) {
        //注意变量$i的值在不同的yield之间是保持传递的。
        yield $i;
    }
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}

ジェネレーターはプログラムが実行されるたびに状態を保存してyieldし、$iを返します。gen_one_to_threeのループを実行し続けるかどうかはメインプログラムに依存します呼び出しを継続するかどうか

コルーチンとは

上記のプログラムを記述する別の方法は

<?php
$i = 1;
function gen_one_to_three() {
    global $i;
    if ($i<=3){
        return $i++;
    }
}

while ($value = gen_one_to_three()) {
    echo "$value\n";
}

です。コルーチンは関数をカプセル化すると、関数は割り込み可能な関数になり、関数というよりもサブプロセスまたはサブスレッドのように動作します。コルーチンの具体的な記述方法については、ここでは詳しく説明しません。コルーチンの記述方法は非常に複雑で、使いやすくするには別のカプセル化層が必要になる場合があるためです。

コルーチンと非同期

コルーチンは中断できるため、プログラムがリクエストを開始し、yield で戻った後にイベント ループが開始される限り、その後、プログラムはメインの実行を続けます。プログラム部分で、イベントが返されるのを待って関数をトリガーし、Generatot::next() または Generator::send() を実行してコルーチン部分の実行を続けます。カプセル化後は、非同期コールバック関数がないかのようになり、同期関数とよく似ています。

現在、コルーチンをカプセル化する 2 つのフレームワーク (ampphp と swoole) が存在します。興味がある場合は、それらについて学ぶことができます。

以上がなぜ PHP には非同期プログラミングが必要なのでしょうか? PHP非同期プログラミングの詳しい説明(例付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。