PHP Academy · Oppitunti

Tapahtumasilmukat ReactPHP:llä

Suorittakaa ei-blokkaavaa I/O:ta ReactPHP:n tapahtumasilmukalla.

Oppitunti 3/413 vaihetta

Tapahtumasilmukat ReactPHP:llä on ilmainen PHP Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu PHP Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. PHP Academy-kurssilla on yhteensä 4 oppituntia.

ReactPHP yhdellä lauseella

ReactPHP on puhdas-PHP:hen perustuva, riippuvuuksista riippumaton työkalupakki ei-blokkaavaan I/O:hon. Se rakentuu yhden tapahtumasilmukan ympärille. Laajennusta ei tarvita — se toimii perus-PHP:llä käyttäen stream_select-toimintoa tai sen ollessa saatavilla ext-event/ext-ev-laajennusta. Sen avulla voitte kirjoittaa pitkäkestoisia palvelimia, samanaikaisia HTTP-asiakkaita ja suoratoistoputkia tavallisella PHP:llä.

Globaali silmukka

ReactPHP:n nykyaikaiset versiot (v1.2+) tarjoavat oletussilmukan staattisen React\EventLoop\Loop-julkisivun kautta — silmukkaa ei tarvitse välittää kaikkialle erikseen. Ajastakaa takaisinkutsu ajastimella; silmukka suoritetaan, kunnes sillä ei ole enää tehtävää.

<?php
require 'vendor/autoload.php';
use React\EventLoop\Loop;

Loop::addTimer(1.0, function () {
    echo "fired after 1 second\n";
});
echo "scheduled\n";
// Loop::run() is auto-invoked at script shutdown in v1.2+

Ajastimet ja toistuva työ

Ei-blokkaava ajoitus korvaa sleep()-kutsun. addTimer suoritetaan kerran; addPeriodicTimer toistetaan. Kumman tahansa voi peruuttaa pysäyttääkseen sen. Koska nämä ohjautuvat silmukasta, muut tehtävät jatkavat suoritustaan ajastimen odottaessa.

<?php
use React\EventLoop\Loop;

$count = 0;
$timer = Loop::addPeriodicTimer(0.5, function () use (&$count, &$timer) {
    echo 'tick ' . (++$count) . "\n";
    if ($count === 4) {
        Loop::cancelTimer($timer); // stop after 4 ticks
    }
});

Promiset asynkronisille tuloksille

ReactPHP palauttaa promisen jokaisesta myöhemmin valmistuvasta toiminnosta. Liittäkää kohtaan then() onnistumisen käsittelevä takaisinkutsu ja toinen takaisinkutsu epäonnistumisen käsittelyä varten. Promiset voidaan yhdistää, joten voitte muuntaa ja ketjuttaa tuloksia ilman syvälle sisäkkäisiä takaisinkutsuja.

<?php
use React\Promise\Promise;

function asyncDouble(int $n): Promise {
    return new Promise(function ($resolve) use ($n) {
        \React\EventLoop\Loop::addTimer(0.1, fn() => $resolve($n * 2));
    });
}

asyncDouble(21)
    ->then(fn($r) => printf("result: %d\n", $r))   // 42
    ->catch(fn(\Throwable $e) => printf("err: %s\n", $e->getMessage()));

Samanaikaiset HTTP-pyynnöt asynkronisella asiakkaalla

Suurin hyöty on se, että voitte lähettää monia HTTP-pyyntöjä samanaikaisesti yhdestä säikeestä. React\Http\Browser palauttaa promiseja; React\Promise\all() odottaa koko erän valmistumista. Kymmenen yhden sekunnin pyyntöä valmistuu noin sekunnissa, ei kymmenessä.

<?php
use React\Http\Browser;
use function React\Promise\all;

$browser = new Browser();
$urls = [
    'https://httpbin.org/delay/1',
    'https://httpbin.org/delay/1',
    'https://httpbin.org/delay/1',
];
$requests = array_map(fn($u) => $browser->get($u), $urls);

all($requests)->then(function (array $responses) {
    echo 'all done: ' . count($responses) . " responses\n";
});

Fibers tekevät koodista synkronisen näköistä

Takaisinkutsuketjuista tulee hankalia. ReactPHP integroituu react/async-pakettiin, joka käyttää PHP 8.1:n Fibers-ominaisuutta, joten voitte odottaa promisea kohdassa await() ja kirjoittaa suoraviivaista koodia. coroutine()/async()-kääre suorittaa funktion fiberin sisällä.

<?php
use function React\Async\async;
use function React\Async\await;
use React\Http\Browser;

$main = async(function () {
    $browser = new Browser();
    $a = await($browser->get('https://httpbin.org/uuid'));
    $b = await($browser->get('https://httpbin.org/uuid'));
    echo (string) $a->getBody();
    echo (string) $b->getBody();
});
$main();

Ei-blokkaava TCP-palvelin

ReactPHP on parhaimmillaan palvelimissa. SocketServer hyväksyy yhteyksiä ja tuottaa tapahtumia; jokainen yhteys on itsessään ei-blokkaava stream. Yksi prosessi käsittelee tuhansia samanaikaisia asiakkaita.

<?php
use React\Socket\SocketServer;
use React\Socket\ConnectionInterface;

$socket = new SocketServer('127.0.0.1:8080');
$socket->on('connection', function (ConnectionInterface $conn) {
    $conn->write("Welcome\n");
    $conn->on('data', function ($data) use ($conn) {
        $conn->write('echo: ' . $data); // streamed back, non-blocking
    });
});
echo "listening on 127.0.0.1:8080\n";

HTTP-palvelin muutamalla rivillä

react/http-palvelin käsittelee pyynnöt takaisinkutsulla, joka palauttaa Response-olion tai sellaisen palauttavan promisen. Koska kyseessä on pitkäkestoinen prosessi, riippuvuudet alustetaan kerran — mutta vastaatte myös muistin hallinnasta pyyntöjen välillä.

<?php
use React\Http\HttpServer;
use React\Http\Message\Response;
use Psr\Http\Message\ServerRequestInterface;
use React\Socket\SocketServer;

$http = new HttpServer(function (ServerRequestInterface $request) {
    return Response::json(['path' => $request->getUri()->getPath()]);
});
$http->listen(new SocketServer('0.0.0.0:8000'));
echo "http://0.0.0.0:8000\n";

Suoratoisto ilman puskurointia

ReactPHP:n streamit toteuttavat vastapaineen hallinnan: voitte yhdistää luettavan streamin kirjoitettavaan kohdassa pipe(), ja ajonaikainen ympäristö rajoittaa nopeutta automaattisesti. Näin gigatavuja voidaan siirtää käyttäen jatkuvasti pientä ja vakiokokoista muistimäärää sen sijaan, että tiedostot ladattaisiin RAM-muistiin.

<?php
use React\Stream\ReadableResourceStream;
use React\Stream\WritableResourceStream;

$source = new ReadableResourceStream(fopen('big.log', 'r'));
$dest   = new WritableResourceStream(fopen('php://stdout', 'w'));
$source->pipe($dest); // back-pressure aware, never buffers whole file

Kuolemansynti: silmukan blokkaaminen

Kaikki pysähtyy heti, kun kutsutte käsittelijässä blokkaavaa funktiota — esimerkiksi sleep()-kutsua, blokkaavaa PDO-kutsua, curl_exec-kutsua tai URL-osoitteen käsittelevää file_get_contents-kutsua. Ne jäädyttävät yhden säikeen ja pysäyttävät kaikki yhteydet. Käyttäkää ReactPHP:n vastineita: viiveisiin Loop::addTimer-kutsua, kyselyihin clue/reactphp-*-pakettia tai asynkronisia tietokanta-ajureita ja HTTP-pyyntöihin Browser-asiakasta.

<?php
use React\EventLoop\Loop;
use function React\Async\await;
use React\Promise\Deferred;

// BAD:  sleep(2);  // freezes the entire loop
// GOOD: async delay that yields control
function delay(float $s): \React\Promise\PromiseInterface {
    $d = new Deferred();
    Loop::addTimer($s, fn() => $d->resolve(null));
    return $d->promise();
}
// await(delay(2.0)); // inside a fiber/coroutine

Samanaikaisuuden rajoittaminen

Rajoittamaton fan-out voi kuluttaa tiedostokuvaajat loppuun tai kuormittaa liikaa taustalla olevaa API:a. ReactPHP:n clue/mq-react jonottaa työt ja rajoittaa samanaikaisuuden käsittelemällä N:ää promisea kerrallaan muiden odottaessa. Tämä rajoittaa oman ulospäin suuntautuvan kuormituksen määrää.

<?php
use Clue\React\Mq\Queue;
use React\Http\Browser;

$browser = new Browser();
// At most 10 in-flight requests across 1000 URLs
$q = new Queue(10, null, fn($url) => $browser->get($url));

$promises = array_map(fn($u) => $q($u), $thousandUrls ?? []);
\React\Promise\all($promises)->then(function ($responses) {
    echo 'completed ' . count($responses) . " with cap of 10\n";
});

Pikatarkistus

Miten ReactPHP-käsittelijässä odotetaan 2 sekuntia heikentämättä samanaikaisuutta?

Kertaus

ReactPHP tuo ei-blokkaavan I/O:n tavalliseen PHP:hen:

  • Yksi tapahtumasilmukka (Loop-fasadi) ohjaa ajastimia, socket-yhteyksiä ja streameja — laajennusta ei tarvita.
  • Operaatiot palauttavat promiseja; all() suorittaa pyynnöt rinnakkain, ja react/async sekä Fibers mahdollistavat await()-kutsun synkroniselta näyttävässä koodissa.
  • Rakentakaa pitkäikäisiä TCP/HTTP-palvelimia ja backpressurea käyttäviä streameja, jotka käsittelevät tuhansia yhteyksiä prosessia kohden.
  • Silmukkaa ei saa koskaan blokata — sleep/blokkaava I/O on korvattava Reactin ajastimilla ja async-ajureilla.
Aloita maksutta

Opi PHP tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
49
Oppitunnit
195

Usein kysytyt kysymykset

Onko oppitunti ”Tapahtumasilmukat ReactPHP:llä” ilmainen?

Kyllä – oppitunnin ”Tapahtumasilmukat ReactPHP:llä” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko PHP Academy-kurssin, päivitä CoddyKit PROhon. PHP Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Tapahtumasilmukat ReactPHP:llä”?

Suorittakaa ei-blokkaavaa I/O:ta ReactPHP:n tapahtumasilmukalla. Harjoittelet PHP Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni PHP Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin PHP Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”Tapahtumasilmukat ReactPHP:llä”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä PHP Academy-oppitunnilla?

Kyllä. Jokainen PHP Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. PHP:n samanaikaisuusmalli
  2. PHP 8.1 Fibers
  3. Tapahtumasilmukat ReactPHP:llä
  4. Tehokkaat palvelimet Swoolella
← Takaisin: PHP Academy