Als de server nieuws heeft
Twee manieren om een push na te bootsen
Elke twee seconden pollen liet het scorebord ongeveer een seconde achterlopen en kostte 1.800 verzoeken per uur per kijker, bijna allemaal beantwoord met “niets nieuws”. Beide kosten hebben dezelfde oorzaak: de server heeft het nieuws, maar hij mag alleen antwoorden als er een verzoek wacht. Er zijn twee bekende manieren om
Houd het verzoek open
De eerste truc is long polling. De kijker stuurt zoals eerder een verzoek, maar de server antwoordt niet meteen. Hij houdt het verzoek open totdat een score verandert, of totdat zijn eigen wachttijd verloopt, bijvoorbeeld na 25 seconden. Dan antwoordt hij, en de kijker stuurt meteen het volgende verzoek.
client server
GET /scores ---->
holds the request
a score changes
<---- 200 OK, the new scores
GET /scores ---->
holds the request
25 seconds pass, no change
<---- 200 OK, nothing new
GET /scores ---->
holds the request
De kijker vraagt, en de server houdt het verzoek vast. Als een score verandert, antwoordt de server meteen met de nieuwe scores, en de kijker vraagt opnieuw. Als er 25 rustige seconden voorbijgaan, antwoordt de server dat er niets veranderd is, en de kijker vraagt opnieuw.
De vertraging verdwijnt bijna. Een wijziging wacht niet meer op de volgende poll, omdat er bijna altijd al een verzoek bij de server wacht; meestal wacht ze alleen tot het antwoord het netwerk is overgestoken. Ze wacht iets langer als ze binnenkomt terwijl het vorige antwoord nog onderweg is en het volgende verzoek de server nog niet heeft bereikt. De lege antwoorden worden ook minder: één per 25 rustige seconden in plaats van één elke 2 seconden.
Er is één nieuwe regel voor de client. In HTTP with Python gaf je elk verzoek een time-out, en leerde je dat een time-out betekent dat je client besluit niet langer te wachten, niet dat de server faalt. Een long poll is een verzoek dat juist hoort te wachten, dus de time-out van de client moet langer zijn dan de tijd dat de server het vasthoudt. Anders geeft de client het bij elke rustige periode op, net voordat het eerlijke “niets nieuws” van de server aankomt.
De server houdt een long poll maximaal 25 seconden vast. Welke time-out voor de client is zinnig?
Eén antwoord dat nooit eindigt
De tweede truc gaat verder: één verzoek, en een antwoord dat nooit afloopt. Dit is Server-Sent Events, vaak afgekort tot SSE. De kijker stuurt één GET, en de server antwoordt met een kop waarvan de Content-Type text/event-stream is:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Transfer-Encoding: chunked
De kop heeft geen Content-Length, omdat de server niet weet hoe lang de body wordt. Hij stuurt de body in chunks, een van de drie manieren waarop een body kan eindigen die je in HTTP with Python tegenkwam, en hij houdt de verbinding open. Telkens als een score verandert, schrijft de server nog een event in de body. Als je de chunkgroottes eruit haalt, ziet de body die de kijker leest er zo uit:
data: Owls 12
data: Herons 9
Elk event is een data:-regel gevolgd door een lege regel. De lege regel is wat een event beëindigt; een event mag er meerdere data:-regels voor hebben. De data is hier gewone tekst, maar het kan alles zijn wat op een regel past, ook
Events uit die tekst lezen doe je met gereedschap dat je al hebt. Dit blok werkt op dezelfde tekst, geschreven als één stringliteral, dus er wordt nergens iets vandaan ontvangen. Draai het:
Het drukt Owls 12 en daarna Herons 9 af. Splitsen bij "\n\n", de lege regel tussen events, geeft één stuk per event, plus een lege data: begint, en daar hoort die lege string bij, en de event[6:] haalt de zes tekens van data: van de voorkant weg.
Een browser heeft dit lezen ingebouwd, en opent ook een stream die wegvalt opnieuw. Aan de serverkant is een eventstream een gewoon HTTP-antwoord, dus heeft hij niets nodig wat HTTP nog niet heeft.
Wat beëindigt in een eventstream één event?
Wat elk ervan houdt
Elke truc haalt één kostenpost van polling weg en houdt één beperking.
| Techniek | Wat het weghaalt | Wat het houdt |
|---|---|---|
| Long polling | het wachten op de volgende poll, en de meeste lege antwoorden | één verzoek voor elk stukje nieuws, en de client begint nog steeds elke uitwisseling |
| Server-Sent Events | het verzoek voor elk stukje nieuws: één antwoord draagt ze allemaal | maar één richting, van server naar client |
Bij long polling kan de server alleen een verzoek beantwoorden, dus elke scorewijziging beëindigt nog steeds een verzoek en vraagt de kijker om een nieuw te sturen. Bij SSE schrijft de server wanneer hij wil, maar alleen de server schrijft. De kijker kan niets terugsturen op de eventstream.
Dat doet ertoe voor het scorebord, want de scorebijhouder heeft ook iets te zeggen. Bij SSE ontvangt een kijker scores op de stream, maar de nieuwe score van de scorebijhouder moet een andere weg nemen.
Hoe stuurt de scorebijhouder bij Server-Sent Events een nieuwe score naar de server?
De onderdelen bestaan dus: een verzoek dat de server kan vasthouden, en een antwoord dat nieuws blijft brengen. Wat ontbreekt, is één verbinding waarop beide kanten mogen spreken wanneer ze willen. De volgende les somt precies op wat zo’n verbinding moet bieden.