0%

Als de server nieuws heeft

Het scorebord dat pollt

Op een quizavond gebeurt het nieuws op de server: de scorebijhouder stuurt een nieuwe score, en de server weet het als eerste. Het programma van een kijker kan dat nieuws niet horen, want bij hoort een client alleen iets van een server als hij erom vraagt. Het eenvoudigste plan is dus om op een timer te vragen. Elke twee seconden stuurt de kijker een verzoek om de huidige scores, en de server antwoordt met wat hij heeft. Dit heet polling: steeds opnieuw opvragen.

Zo ziet dat eruit voor de eerste tien seconden, met één scorewijziging op 7,5 seconden:

seconds   0     2     4     6     8     10
          |     |     |     |     |     |
polls     P     P     P     P     P     P
                                 ^
                                 score changes at 7.5

De kijker pollt op 0, 2, 4, 6, 8 en 10 seconden. De polls van 0 tot en met 6 seconden vinden niets nieuws. De score verandert op 7,5 seconden, en niemand hoort ervan totdat de poll op 8 seconden ernaar vraagt. Die wijziging bereikt de kijker een halve seconde te laat, en de poll op 10 seconden is weer leeg.

Simuleer één uur

Eén wijziging, een halve seconde te laat, zegt weinig. Om te zien wat polling over een hele avond kost, simuleert het programma hieronder een uur met twaalf scorewijzigingen op vaste tijden, gegeven in seconden sinds de start. Het heeft geen netwerk en geen klok nodig: elke tijd is een getal in een , dus het drukt elke keer dat je het draait hetzelfde resultaat af. Draai het:

Try it

Het drukt polls per hour: 1800, polls that carried news: 12, polls with no news: 1788 en average lateness: 1.0 seconds af.

Regel voor regel: een uur heeft 3600 seconden, dus een poll elke interval seconden maakt 3600 // interval polls. De berekent hoe lang elke wijziging wacht. Polls gebeuren op hele veelvouden van het interval, dus change % interval is hoe ver na de laatste poll de wijziging gebeurde, en interval min dat is hoe lang ze op de volgende wacht. De eerste wijziging, op 120,25 seconden, valt 0,25 seconden na de poll op 120, dus ze wacht 1,75 seconden op de poll op 122. De buitenste % interval dekt een wijziging die precies op een poll valt: die wacht 0 seconden, geen heel interval. total_wait telt elke wachttijd op, en de laatste regel deelt door het aantal wijzigingen.

Elke wijziging valt tussen een ander paar polls, dus twaalf polls brengen nieuws, één wijziging per poll, en elke andere poll komt leeg terug.

Waarom ongeveer de helft van het interval

Een scorewijziging kan op elk moment tussen twee polls gebeuren. Vlak na een poll wacht ze bijna een heel interval; vlak ervoor bijna niets. Gemiddeld over veel wijzigingen komt de wachttijd op ongeveer de helft van het interval: ongeveer één seconde bij een interval van 2 seconden.

De twaalf tijden in de lijst zijn zo gekozen dat de simulatie dat gemiddelde precies laat zien. Ze komen in paren die op dezelfde afstand aan weerszijden van een veelvoud van tien seconden liggen, bijvoorbeeld 120,25 en 419,75, dus bij een interval van 2 of van 10 seconden tellen de twee wachttijden van een paar op tot precies één interval. Echte scorewijzigingen komen pas over een lange avond in de buurt van het gemiddelde.

De simulatie laat ook iets weg. Elk verzoek en elk antwoord heeft tijd nodig om het netwerk over te steken, zoals je in Networks with Python schatte, en die vertraging komt bovenop het wachten op de volgende poll. Een echte kijker is dus iets later dan deze getallen zeggen, nooit eerder.

Hetzelfde programma met een poll elke 10 seconden. Wat drukt het af?

score_changes = [120.25, 419.75, 661.5, 908.5, 1332.75, 1707.25, 2043.5, 2296.5, 2614.25, 2955.75, 3220.75, 3489.25]
interval = 10
polls_per_hour = 3600 // interval
total_wait = 0
for change in score_changes:
    wait = (interval - change % interval) % interval
    total_wait += wait
print(f"polls per hour: {polls_per_hour}")
print(f"polls that carried news: {len(score_changes)}")
print(f"polls with no news: {polls_per_hour - len(score_changes)}")
print(f"average lateness: {total_wait / len(score_changes)} seconds")

Elke poll kost een verzoek

Een poll is niet iets kleins. Elke poll is een compleet HTTP-verzoek en een compleet antwoord op een , met een verzoekregel, headers, een statusregel en nog meer headers, precies de bytes die je in HTTP with Python met de hand schreef. Zelfs de goedkoopste lege poll, een conditioneel verzoek dat wordt beantwoord met een 304 zonder body zoals in het hoofdstuk over caching van die cursus, stuurt nog steeds een hele verzoekkop en ontvangt een hele antwoordkop. Hier is er een, ingekort tot de regels die hier tellen:

GET /scores HTTP/1.1
Host: quiz.test
If-None-Match: "v12"

HTTP/1.1 304 Not Modified
ETag: "v12"

De server beantwoordt bijna elk van die verzoeken met “niets nieuws”. Met een interval van 2 seconden stuurt één kijker 1.800 verzoeken per uur om over twaalf wijzigingen te horen. Duizend kijkers sturen er 1,8 miljoen.

Je halveert het pollinginterval van 2 seconden naar 1 seconde. Wat gebeurt er?

Waarom brengen bijna alle polls geen nieuws?

Polling dwingt je te kiezen tussen vertraging en verspilde verzoeken, en geen enkel interval geeft je zowel een snel scorebord als een rustige server. De volgende les bekijkt twee manieren waarop HTTP zelf is opgerekt om het beter te doen.