Duw op enter om te zoeken

Chat server in 20 minuten (ReactPHP)

04/08/2021
Author Avatar
Mario Aquino
Business Consultant

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.

Basisbegrippen

Gebeurtenislus

“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:

Image 1

 

Programmastappen:

  1. Start van het programma
  2. Registratie van de gebeurtenislus en gebeurtenissen
  3. Uitvoeren van de gedefinieerde Gebeurtenislus
  4. Uitvoeren van de gedefinieerde Gebeurtenislus
  5. Uitvoeren van de gedefinieerde Gebeurtenislus

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:

Image 2

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.

Image 3

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.

Streams

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.

Image 4

In dit voorbeeld zien we 3 belangrijke streams:

  1. ReadableResourceStream – STDIN
    1. In dit voorbeeld is de inputstream datgene wat de gebruiker in de terminal typt (=STDIN)
  2. ThroughStream
    1. Deze stream werkt als de “middleware” tussen de input- en outputstream. We zouden hier nog iets toe kunnen voegen; we kunnen bijvoorbeeld de tekst in hoofdletters zetten, vloekwoorden verwijderen, … Of in een chatcontext, de naam van de zender aan het bericht toevoegen. In dit voorbeeld gaan we de tekst in hoofdletters zetten.
  3. WriteableResourceStream – STDOUT
    1. Dit is de bestemming van ons bericht. In dit voorbeeld staat de output van het resultaat in de terminal (= STDOUT)

 

Image 5

Meer informatie over deze basisbegrippen

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:

 

HubSpot Video

 

Chatserver met gebruik van de ReachtPHP websockets

Websockets

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:

Image 6

  1. Client vraagt om verbinding met de server.
  2. Server accepteert deze aanvraag.
  3. De client opent de verbinding (de verbinding komt tot stand).
  4. SSL-configuratiestappen
  5. De client vraagt de website blog.easi.net aan.
  6. Server levert alles aan voor blog.easi.net.
  7. Client stuurt de server bericht wanneer als is gedownload (vraagt om de verbinding te sluiten).
  8. Server sluit de verbinding.

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:

  1. Bob en Alice ontvangen alleen berichten wanneer ze actief de pagina verversen, of wanneer hun browser een aanvraag naar de server stuurt. (Je zou dit kunnen oplossen met een peiling, maar dit zal ten koste gaan van de bandbreedte).

  2. Performantie/overhead, de 7-staps handshake moet twee keer worden uitgevoerd voor elk bericht dat tussen de gebruikers wordt verzonden. De verbinding mag nooit worden gesloten, omdat we weten dat er nog meer berichten zullen komen.

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:

Image 7

Er zijn in dit codevoorbeeld 3 belangrijke objecten die je moet begrijpen:

  • $server is een basis Websocketserver, die in dit geval geconfigureerd is om te luisteren op poort 6000. Deze socketserver opent verbindingen, maar houdt ze niet bij. Dit is voldoende voor communicatie van het type client - server, maar niet genoeg voor onze chat use case. Wij hebben een verbinding client - server - client
  • $limitingServer is een ge-upgrade versie van de door ons genoemde $server. De tweede parameter wordt gebruikt om te configureren hoeveel verbindingen naar de server zijn toegestaan. (In dit voorbeeld 10.) Een ander belangrijk gegeven is dat de LimitingServer de verbonden knooppunten De LimitingServer kan ons op elk moment vertellen hoeveel open verbindingen er nog over zijn.
  • $client is in dit voorbeeld de verbinding van een gebruiker van onze socketserver.

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:

  • Wanneer een gebruiker verbinding maakt (“connection”): Stuur het bericht “Welkom!” naar de gebruiker.
  • Wanneer een gebruiker een bericht verstuurt (“data”): Verspreid het bericht naar alle open verbindingen en stuur een verzendbevestiging naar de afzender.

Conclusie

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.

Verwijzingen

New call-to-action

Vacatures

Wij zijn voortdurend op zoek naar nieuwe collega's!

Als je onze waarden deelt en op zoek bent naar een uitdagende job in België's Best Workplace, bezoek dan onze website.

Solliciteer nu

Schrijf je in voor onze nieuwsbrief

Follow us

  

Deel dit artikel