ReactPHP is een set pakketten waarmee je asynchrone PHP-code kunt schrijven. Computerbewerkingen zijn meer dan 100x sneller dan I/O-bewerkingen. ReactPHP wint aan performantie door niet eerst te wachten tot I/O-bewerkingen klaar zijn. In plaats daarvan voert het alvast “de volgende code” uit. ReactPHP werkt sneller, omdat het jouw thread op de meest efficiënte manier gebruikt.
Je moet dit niet verwarren met parallel programmeren. Hierbij wordt een nieuwe thread geopend als de originele thread wordt geblokkeerd door een I/O-bewerking. Parallel programmeren brengt problemen zoals isolatie, draadveiligheid en trage contextschakeling met zich mee – en nog veel belangrijker – het maakt het programma onnodig complex.
PHP-webapplicaties werken al parallel, er wordt een nieuwe thread geopend voor elke verbinding. Op deze manier wordt gebruiker A nooit geblokkeerd door bewerkingen van gebruiker B. ReactPHP kan een verbetering op dit model zijn. Er wordt nog steeds een thread gebruikt voor elke gebruiker, maar ReactPHP zal deze thread op maximale capaciteit gebruiken.
In dit artikel gaan we in op enkele basisbegrippen om zo te kunnen begrijpen hoe ReactPHP-pakketten werken. Lees vooral het hele artikel, want in de laatste alinea gaan we een performante websocketserver configureren die ontworpen is om oneindig vaak te worden uitgevoerd.
“In ReactPHP loopt alles asynchroon, behalve jouw code.”
Dit lijkt misschien een rare bewering, omdat er in de vorige alinea werd gesteld dat ReactPHP asynchroon loopt. Laten we eerst eens kijken hoe je dit eenvoudige script moet interpreteren:
Programmastappen:
In stap 2 worden gebeurtenissen geregistreerd (in dit voorbeeld tijdsgebeurtenissen), in de volgende stappen controleren we welke gebeurtenissen geregistreerd zijn en of een daarvan is getriggerd. De stappen die worden vermeld lopen allemaal synchroon, maar dat is nog niet alles. We moeten inzoomen op de “Eventloop” om te kunnen begrijpen waarom ReactPHP asynchroon werkt:
Stappen in de gebeurtenislus:
De lus in de code hierboven zou 100x kunnen worden uitgevoerd, maar zal alleen gegevens afdrukken na 1 en na 2 seconden. Merk op dat de volgorde van de gebeurtenissen er niet toe doet; de gebeurtenis na 1 seconde zal eerst worden uitgevoerd, hoewel de gebeurtenis na 2 seconden het eerst wordt gedefinieerd. Dit is waarom de ReactPHP uitvoer asynchroon loopt.
Dit concept is hetzelfde voor I/O-bewerkingen, het maakt niet uit wanneer de bewerking begint, ReactPHP zal er alleen mee interageren als het iets (gegevens) te bieden heeft.
De controle op overschreden timergebeurtenissen is slechts één stap van de gebeurtenislus, stap twee zou het peilen naar I/O-bewerkingen zijn. Dit is een controle om te kijken of een van de eerder geregistreerde I/O-bewerkingen al resultaten klaar heeft staan. Een I/O-bewerking kan veel inhouden: bewerkingen van een bestand, databasebewerkingen, API-calls, printercommando's, ... En nog veel meer!
Bijna alle I/O-bewerkingen komen op het laagste niveau overeen met streams, een belangrijke bouwsteen in de bibliotheek van ReactPHP.
In dit voorbeeld zien we 3 belangrijke streams:
Met deze twee basisbegrippen heb je een goed begrip van de werking van de ReactPHP-pakketten. Er zijn meer basisbegrippen, mocht je daarin zijn geïnteresseerd bekijk dan zeker mijn conferentievideo:
Om websockets beter te begrijpen, moeten we eerst kijken naar een gewone HTTPS-verbinding. Wanneer een client naar blog.easi.net zou browsen, ziet de HTTPS-handshake er zo uit:
Zoals je wellicht ziet is er sprake van enige overhead voordat de verbinding tot stand is gebracht en gesloten. Deze overhead is niet verkeerd wanneer je een hele website van de server aanvraagt. Een ander belangrijk detail is dat de server geen verbinding met de client tot stand kan brengen. De client moet beginnen met een aanvraag (blog.easi.net).
De meest populaire use case voor websockets is een gebruiker-naar-gebruiker chat. Stel je voor dat twee gebruikers een chatgesprek met elkaar zouden willen voeren (laten we ze Bob en Alice noemen). Ons HTTPS-model levert hierbij twee grote problemen op:
Dit is een use case waarbij websockets van pas komen; een websocket-connectie tussen client en server blijft open. Het verbindingsdiagram blijft hetzelfde, alleen worden stap 7 en 8 niet uitgevoerd. De verbinding blijft open, daarom hoeft de handshake maar twee keer te worden uitgevoerd (één keer voor Bob, één keer voor Alice).
Door websockets te gebruiken winnen we tijd, bandbreedte en het belangrijkste: het verhoogt de gebruikerservaring. Bob zal de berichten van Alice onmiddellijk ontvangen en vice versa.
In ReactPHP kunnen we een dergelijke (websocket-)chat-server in 22 regels code instellen:
Er zijn in dit codevoorbeeld 3 belangrijke objecten die je moet begrijpen:
Afgezien van de drie genoemde objecten is er niets nieuws aan dit script. Er zijn twee gebeurtenissen geregistreerd die bepalen hoe onze server moet reageren:
ReactPHP is een krachtige tool die de performantie en reactiesnelheid van jouw applicatie kan verhogen. ReactPHP kan worden gebruikt als een standalone project, maar de pakketten kunnen ook in jouw bestaande project/framework worden geïnstalleerd, of er aan worden toegevoegd.
Het is raadzaam ReactPHP te gebruiken voor langdurige taken of taken die veel I/O-bewerkingen vergen. Het kan een goed alternatief zijn voor Node.js/Go, vooral als je PHP al kent en de andere technologieën nieuw voor je zijn.