Heim > Artikel > Backend-Entwicklung > Warum benötigt PHP asynchrone Programmierung? Detaillierte Erklärung der asynchronen PHP-Programmierung (mit Beispielen)
Mein Wissen über PHP asynchron ist immer noch ziemlich verwirrend. Ich schreibe diesen Artikel, um das Problem zu klären.
Traditionelles PHP-FPM Wenn ein Prozess eine Anforderung ausführt, wie viele Prozesse müssen generiert werden, um die gleiche Parallelität zu erreichen? Was noch schlimmer ist, ist, dass jede Anfrage neu kompiliert und ausgeführt werden muss, was dazu führt, dass die Parallelität nie zunimmt. Daher die Entstehung von Swoole und WorkerMan sind zwei beliebte Resident-Memory-Frameworks in China [1]. Die Prinzipien dieser beiden Frameworks bestehen darin, Ereignisschleifen zu verwenden, um das Programm im Speicher zu halten, auf externe Anforderungen zu warten und eine hohe Parallelität zu erreichen.
Warum Asynchronität benötigt wird
Schauen wir uns zunächst ein Beispiel an
Erstellen Sie eine neue Datei slowServer.php im Arbeitsverzeichnis
<?php sleep(5); // 5秒后才能返回请求 echo 'done';
Starten Sie den Dienst
$ php -S localhost:8081 slowServer.php
Öffnen Sie ein anderes Terminal und installieren Sie Abhängigkeiten
$ pecl install event # 安装 event 扩展 $ composer require workerman/workerman $ composer require react/http-client:^0.5.9
Erstellen Sie eine neue Datei worker.php
require_once __DIR__ . '/vendor/autoload.php'; 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();
Öffnen Sie den Server
php worker.php start
Öffnen zwei Registerkarten im Browser. Beide öffnen die URL http://localhost:8082. Zu diesem Zeitpunkt können Sie sehen, dass das Terminal „1“ ausgibt und nach einer Weile erneut „1“ ausgibt. Der Grund dafür ist, dass der 8081-Server bei der Verarbeitung der ersten Anfrage blockiert ist Wenn die Anfrage abgeschlossen ist, beginnt die Verarbeitung der zweiten Anfrage. Das heißt, wie viele Prozesse müssen nacheinander ausgeführt werden, um die gleiche Anzahl von Parallelitäten zu erreichen, genau wie bei PHP-FPM. Ändern Sie nun den Code
$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(); };
Öffnen Sie nun den Dienst und initiieren Sie eine Anfrage im Browser. Es wird festgestellt, dass die zweite „1“ unmittelbar nach der Anfrage ausgegeben wird, die erste Anfrage jedoch noch nicht beendet ist. Dies zeigt an, dass der Prozess nicht mehr blockiert ist und das Ausmaß der Parallelität von der CPU und dem Speicher abhängt, nicht von der Anzahl der Prozesse.
Warum Asynchronität erforderlich ist
Aus dem obigen Beispiel geht hervor, dass das Reactphp-Framework die HTTP-Anfrage asynchron macht und die CPU-Funktion nicht blockiert um die nächste Anfrage zu bearbeiten. Das heißt, die CPU-Schleife wartet auf die Rückkehr von 8081 und verwandelt sich in eine Epoll-Warteschleife.
Die Bedeutung von asynchron besteht darin, die CPU von IO-Wartezeiten zu befreien und andere Rechenaufgaben erledigen zu können. Wenn Sie wissen möchten, wie Sie das Framework verwenden, um eine asynchrone Implementierung zu erreichen, lesen Sie einfach hier. WorkerMan kann in Kombination mit ReactPHP oder seiner eigenen AsyncTcpConnection bereits die asynchronen Anforderungen vieler IO-Anfragen erfüllen. Lassen Sie uns weiter diskutieren, wie diese Frameworks asynchron sind.
Welche Orte sollten asynchron gemacht werden?
Durch das obige Beispiel wissen wir bereits, dass die CPU nicht einmal für die Ausführung benötigt wird, sondern beim Warten auf io das io Der Prozess sollte asynchron erfolgen.
Ereignisschleife implementieren
Das obige Beispiel verwendet Reactphp, um die HTTP-Anfrage in eine asynchrone umzuwandeln. Tatsächlich ist das WorkerMan-Framework selbst auch asynchron So funktioniert WorkerMan: Aktivieren Sie die Funktion onMessage, um Anfragen asynchron anzunehmen. Erstellen Sie zunächst die folgende Datei „react.php“
<?php $context = stream_context_create(); $socket = stream_socket_server('tcp://0.0.0.0:8081', $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, 'react', $socket); // 注册一个事件,检测 fd 有没有写入内容 $event->add(); $eventBase->loop(); // 开始循环
Ausführung starten
$ php react.php
In einem anderen Terminal ausführen
telnet 127.0.0.1 8081
Sie sehen die erste Terminalausgabe „1“.
Ich habe vor „PHP verwendet Epoll“ einen Artikel geschrieben, der die Grundlage dieses Artikels bildet. In diesem Artikel wird der Ereignisrückruf durch Timing implementiert, d. h.
$event->add($seconds);
Hier wird der Ereignisrückruf durch Erkennen, ob fd geschrieben wurde, implementiert. Wenn Inhalte in fd geschrieben werden, wird die Funktion „react“ aufgerufen und die CPU wird verwendet. Wenn der Prozess zu diesem Zeitpunkt eine weitere asynchrone Anfrage ausführt, z. B. das Anfordern einer Webseite mithilfe des Reactphp-Frameworks, gibt das Programm die CPU frei. Wenn zu diesem Zeitpunkt eine weitere Anfrage eingeht, kann es zurückrufen, um eine weitere „Reaktions“-Funktion auszuführen . Dies erhöht den Grad der Parallelität.
Coroutine
Generator
Dies ist die offizielle PHP-Dokumentation für den Generator http://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"; }Der Generator speichert den Status jedes Mal, wenn das Programm ausgeführt wird, um nachzugeben, und gibt dann $i zurück. Ob die Ausführung der Schleife in gen_one_to_ three fortgesetzt werden soll, hängt vom Hauptprogramm ab Ob weiter aufgerufen werden soll
Was ist eine Coroutine?
Eine andere Möglichkeit, das obige Programm zu schreiben, ist<?php $i = 1; function gen_one_to_three() { global $i; if ($i<=3){ return $i++; } } while ($value = gen_one_to_three()) { echo "$value\n"; }Es ist ersichtlich, dass es sich um eine Coroutine handelt Eine Art Funktion ist so gekapselt, dass sie zu einer Funktion wird, die unterbrochen werden kann und sich eher wie ein Unterprozess oder Unterthread als wie eine Funktion verhält. Auf die spezifische Schreibmethode von Coroutine wird hier nicht näher eingegangen, da die Schreibmethode von Coroutine sehr kompliziert ist und möglicherweise eine weitere Kapselungsschicht erfordert, um einfach zu verwenden zu sein.
Coroutine und asynchron
Da die Coroutine unterbrochen werden kann, solange die Ereignisschleife initiiert wird, nachdem das Programm die Anforderung initiiert hat, kehrt sie mit Ertrag zurück und Anschließend führt das Programm weiterhin den Hauptteil aus. Warten Sie im Programmteil, bis das Ereignis zurückkehrt, lösen Sie die Funktion aus und führen Sie Generatot::next() oder Generator::send() aus, um mit der Ausführung des Coroutine-Teils fortzufahren. Nach der Kapselung ist es so, als gäbe es keine asynchrone Rückruffunktion, die einer synchronen Funktion sehr ähnlich ist. Es gibt jetzt zwei Frameworks, ampphp und swoole, die Coroutinen kapseln. Wenn Sie interessiert sind, können Sie mehr darüber erfahren.Das obige ist der detaillierte Inhalt vonWarum benötigt PHP asynchrone Programmierung? Detaillierte Erklärung der asynchronen PHP-Programmierung (mit Beispielen). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!