Frequentiebeperkingspatroon

Bepaal de snelheid waarmee uw toepassing aanvragen naar een service verzendt, zodat u binnen de beperkingslimieten en de totale capaciteit van de service blijft. Met deze aanpak kunt u fouten door snelheidsbeperking vermijden of beperken en de doorvoer nauwkeuriger voorspellen.

Snelheidsbeperking is geschikt in veel scenario's, maar het is vooral handig voor grootschalige, terugkerende geautomatiseerde taken, zoals batchverwerking.

Context en probleem

Het uitvoeren van grote aantallen bewerkingen op basis van een vertraagde service kan leiden tot meer verkeer en verminderde doorvoer, omdat u geweigerde aanvragen moet bijhouden en de bewerkingen vervolgens opnieuw moet uitvoeren. Naarmate het aantal bewerkingen toeneemt, kan een beperkingslimiet meerdere doorvoeringen van het opnieuw verzenden van gegevens vereisen, wat resulteert in een grotere impact op de prestaties.

Neem bijvoorbeeld het volgende problematische retry-on-error-proces voor het invoeren van data in Azure Cosmos DB:

  1. Uw toepassing moet 10.000 records opnemen in Azure Cosmos DB. Elke record kost 10 aanvraageenheden (RU's) om op te nemen, dus een totaal van 100.000 RU's is vereist om de taak te voltooien.

  2. Uw Azure Cosmos DB-instantie heeft 20.000 RU's aan ingerichte capaciteit.

  3. U verzendt alle 10.000 records naar Azure Cosmos DB. 2000 records worden geschreven en 8.000 records worden geweigerd.

  4. U verzendt de resterende 8000 records naar Azure Cosmos DB. 2000 records worden geschreven en 6.000 records worden geweigerd.

  5. U verzendt de resterende 6000 records naar Azure Cosmos DB. 2000 records worden met succes geschreven en 4.000 records worden geweigerd.

  6. U verzendt de resterende 4000 records naar Azure Cosmos DB. 2000 records worden met succes geschreven en 2000 records worden geweigerd.

  7. U verzendt de resterende 2000 records naar Azure Cosmos DB. Ze zijn allemaal succesvol geschreven.

De opnametaak is voltooid, maar pas na het verzenden van 30.000 records naar Azure Cosmos DB. De volledige gegevensset bestaat uit slechts 10.000 records.

In dit voorbeeld zijn er andere factoren die u moet overwegen:

  • Grote aantallen fouten kunnen ook leiden tot extra werk om deze fouten te registreren en de resulterende logboekgegevens te verwerken. De voorgaande benadering verwerkt 20.000 fouten en het vastleggen van deze fouten kan een verwerkings-, geheugen- of opslagresourcekosten met zich meebrengt.

  • Omdat u de beperkingslimieten van de opnameservice niet kent, hebt u geen manier om verwachtingen in te stellen voor de duur van gegevensverwerking. Met snelheidsbeperking kunt u de benodigde tijd voor opname berekenen.

Oplossing

Snelheidsbeperking kan het verkeer verminderen en de doorvoer mogelijk verbeteren door het aantal records dat gedurende een bepaalde periode naar een service wordt verzonden, te verminderen.

Een service kan aanvragen beperken op basis van verschillende metrische gegevens in de loop van de tijd, zoals:

  • Het aantal bewerkingen (bijvoorbeeld 20 aanvragen per seconde).
  • De hoeveelheid gegevens (bijvoorbeeld 2 GiB per minuut).
  • De relatieve kosten van bewerkingen (bijvoorbeeld 20.000 RU's per seconde).

Ongeacht welke maatstaf u voor throttling gebruikt, uw rate limiting-implementatie omvat het beheersen van het aantal en/of de omvang van bewerkingen die binnen een bepaalde periode naar de service worden verzonden. Frequentiebeperking helpt u de dienst optimaal te gebruiken zonder de throttlinglimiet te overschrijden.

In scenario's waarin uw API's aanvragen sneller kunnen verwerken dan de beperkte opnameservices toestaan, moet u beheren hoe snel u de service gebruikt. Het behandelen van throttling alleen als een mismatch van gegevenssnelheid en het bufferen van opnameverzoeken totdat de service herstelt, creëert risico's. Als uw toepassing niet meer reageert in dit scenario, gaan eventuele gebufferde gegevens verloren.

Om dit risico te vermijden, kunt u overwegen uw records naar een duurzaam berichtensysteem te sturen dat kan omgaan met uw volledige innamesnelheid. (Services zoals Azure Event Hubs kunnen miljoenen bewerkingen per seconde verwerken.) Vervolgens kunt u een of meer taakprocessors gebruiken om de records van het berichtensysteem te lezen met een gecontroleerde snelheid die binnen de limieten van de beperkte service valt. Als u records naar het berichtensysteem verzendt, kunt u intern geheugen besparen door alleen de records uit de wachtrij te verwijderen die gedurende een bepaald tijdsinterval kunnen worden verwerkt.

Azure biedt verschillende duurzame berichtenservices die u met dit patroon kunt gebruiken, waaronder:

Diagram dat een duurzame berichtenstroom toont. Drie taakverwerkers roepen een service met snelheidsbeperking aan.

Wanneer u records verzendt, is de periode die u gebruikt voor het vrijgeven van records mogelijk gedetailleerder dan de periode waarop de service wordt beperkt. Systemen stellen vaak limieten in op basis van tijdsperioden die u gemakkelijk kunt begrijpen en waarmee u kunt werken. Voor de computer waarop een service wordt uitgevoerd, kunnen deze tijdsbestekken echter erg lang zijn in vergelijking met hoe snel informatie kan worden verwerkt. Een systeem kan bijvoorbeeld per seconde of per minuut worden afgeremd, maar doorgaans wordt de code verwerkt in de orde van grootte van nanoseconden of milliseconden.

Hoewel dit niet vereist is, wordt het vaak aanbevolen om kleinere aantallen records vaker te verzenden om de doorvoer te verbeteren. Dus in plaats van records voor vrijgave één keer per seconde of één keer per minuut te bundelen, kunt u nog fijnmaziger te werk gaan om het verbruik van resources (geheugen, CPU en netwerk) gelijkmatiger te laten verlopen. Deze aanpak voorkomt potentiële knelpunten die worden veroorzaakt door plotselinge bursts van aanvragen. Als een service bijvoorbeeld 100 bewerkingen per seconde toestaat, kan de implementatie van een frequentielimieter aanvragen gelijkmatig uitzetten door elke 200 milliseconden 20 bewerkingen uit te brengen, zoals wordt weergegeven in de volgende grafiek.

Grafiek die een snelheidsbeperkte stroom in de tijd weergeeft.

Daarnaast is het soms noodzakelijk dat meerdere niet op elkaar afgestemde processen een in snelheid begrensde service delen. Als u snelheidsbeperking in dit scenario wilt implementeren, kunt u de capaciteit van de service logisch partitioneren en vervolgens een gedistribueerd wederzijds uitsluitingssysteem gebruiken om exclusieve vergrendelingen op deze partities te beheren. De niet-gecoördineerde processen kunnen vervolgens concurreren voor vergrendelingen op deze partities wanneer ze capaciteit nodig hebben. Voor elke partitie waarvoor een proces een vergrendeling bevat, krijgt het een bepaalde hoeveelheid capaciteit.

Als het vertraagde systeem bijvoorbeeld 500 aanvragen per seconde toestaat, kunt u 20 partities maken die elk 25 aanvragen per seconde waard zijn. Als een proces nodig is om 100 aanvragen uit te geven, kan het het gedistribueerde wederzijdse uitsluitingssysteem voor vier partities vragen. Het systeem kan twee partities gedurende 10 seconden verlenen. Het proces zou vervolgens een frequentielimiet hebben van 50 aanvragen per seconde, de taak in twee seconden voltooien en vervolgens de vergrendeling vrijgeven.

Een manier om dit patroon te implementeren, is door Azure Storage te gebruiken. In dit scenario maakt u één 0-byte-blob per logische partitie in een container. Uw toepassingen kunnen dan gedurende korte tijd exclusieve leases verkrijgen op basis van deze blobs (bijvoorbeeld 15 seconden). Voor elke lease waarvoor een aanvraag wordt goedgekeurd, kan de capaciteit van die partitie worden gebruikt. De toepassing moet vervolgens de leasetijd bijhouden, zodat, wanneer de tijd verloopt, de toepassing kan stoppen met het gebruik van de capaciteit die is verleend. Wanneer u dit patroon implementeert, wilt u vaak dat elk proces probeert een willekeurige partitie te leasen wanneer deze capaciteit nodig heeft.

Als u de latentie verder wilt verminderen, kunt u voor elk proces een kleine hoeveelheid exclusieve capaciteit toewijzen. Een proces zou dan alleen een lease voor gedeelde capaciteit aanvragen als het zijn gereserveerde capaciteit moest overschrijden.

Diagram met meerdere processen die concurreren voor exclusieve leases op blobpartities in Azure Blob Storage.

Als alternatief voor Azure Storage kunt u dit soort leasebeheersysteem ook implementeren met behulp van technologieën zoals ZooKeeper, enzovoort en Redis/Redsync.

Problemen en overwegingen

Houd rekening met de volgende punten wanneer u besluit hoe u dit patroon implementeert:

  • Hoewel het frequentiebeperkingspatroon het aantal beperkingsfouten kan verminderen, moet uw toepassing nog steeds eventuele beperkingsfouten die zich kunnen voordoen, correct verwerken.

  • Zorg ervoor dat herhalingspogingen zijn afgestemd op ratelimiting. Blinde of al te agressieve nieuwe verzoekpogingen kunnen de belasting verhogen en een stortvloed aan nieuwe verzoekpogingen veroorzaken, dus geef back-pressure-signalen (bijvoorbeeld HTTP 429 met Retry-After) door en gebruik een beperkt aantal nieuwe verzoekpogingen met kleine willekeurige vertragingen tussen de pogingen.

  • Als uw toepassing meerdere werkstromen heeft die toegang hebben tot dezelfde beperkte service, moet u ze allemaal integreren in uw strategie voor snelheidsbeperking. U kunt bijvoorbeeld het in bulk laden van records in een database ondersteunen, maar ook het opvragen van records in diezelfde database. U kunt de capaciteit beheren door ervoor te zorgen dat alle werkstromen worden beperkt door hetzelfde mechanisme voor snelheidsbeperking. U kunt ook afzonderlijke pools met capaciteit reserveren voor elke workstream.

  • De beperkte service kan in meerdere toepassingen worden gebruikt. In sommige gevallen is het mogelijk om dat gebruik te coördineren (zoals eerder in dit artikel wordt weergegeven). Als u meer throttlingfouten ziet dan verwacht, kan dat erop wijzen dat er concurrentie is tussen toepassingen die toegang proberen te krijgen tot een service. In dit geval moet u overwegen om de doorvoer die wordt opgelegd door uw snelheidsbeperkingsmechanisme tijdelijk te verminderen totdat het gebruik van andere toepassingen afneemt.

Wanneer gebruikt u dit patroon?

Gebruik dit patroon wanneer:

  • U moet het aantal throttlingfouten verminderen dat wordt veroorzaakt door een service waarvoor een limiet op het aantal aanvragen geldt.

  • U wilt het verkeer minimaliseren vergeleken met naïeve benaderingen waarbij bij fouten opnieuw wordt geprobeerd.

  • U moet het geheugenverbruik verminderen door records alleen uit de wachtrij te verwijderen wanneer er voldoende capaciteit is om ze te verwerken.

Dit patroon is mogelijk niet geschikt wanneer:

  • Voor de bewerking is onmiddellijke, synchrone voltooiing met zeer lage latentie vereist en kan wachtrij- of uitgestelde verwerking niet worden getolereerd.

  • Het belangrijkste knelpunt is niet de snelheid van aanvragen, maar eerder gelijktijdige verwerking of strijd om resources (bijvoorbeeld CPU-verzadiging of langlopende actieve taken). In deze gevallen zijn instellingen voor schaalbaarheid of concurrentie geschikter.

Ontwerp van werkbelasting

Beoordeel hoe u het patroon voor snelheidsbeperking gebruikt in het ontwerp van een workload om de doelstellingen en principes te ondersteunen die worden behandeld in de pijlers van het Azure Well-Architected Framework. De volgende tabel bevat richtlijnen over hoe dit patroon de doelstellingen van elke pijler ondersteunt.

Pijler Hoe dit patroon ondersteuning biedt voor pijlerdoelen
betrouwbaarheid ontwerpbeslissingen helpen uw workload tolerant te worden defect te raken en ervoor te zorgen dat deze herstelt naar een volledig functionerende status nadat er een storing is opgetreden. Deze tactiek beschermt de klant door de beperkingen en kosten van communicatie met een service te erkennen en te respecteren wanneer de service liever overmatig gebruik vermijdt.

- RE:07 Zelfbehoud

Als dit patroon compromissen binnen een pijler introduceert, moet u deze tegen de doelstellingen van de andere pijlers overwegen.

Example

Met de volgende voorbeeldtoepassing kunnen gebruikers records van verschillende typen verzenden naar een API. Elk recordtype heeft een unieke taakprocessor die de volgende stappen uitvoert:

  1. Validation
  2. Verrijking
  3. Invoeging van de record in de database

Alle onderdelen van de toepassing (API, taakprocessor A en taakprocessor B) zijn afzonderlijke processen die onafhankelijk kunnen worden geschaald. De processen communiceren niet rechtstreeks met elkaar.

Diagram van een stroom met meerdere wachtrijen en processors, waarbij gepartitioneerde leaseopslag naar een afgeknepen database schrijft.

In het diagram ziet u twee gebruikers die records verzenden via een gedeelde API. Elk recordtype wordt naar een aparte wachtrij doorgestuurd, door een aparte jobprocessor verwerkt en met een gecontroleerde snelheid weggeschreven naar een database waarvan de doorvoersnelheid wordt begrensd door blobpartition-leases in Azure Storage. De twee gebruikers verzenden elk een batch berichten naar het API-onderdeel. Eén gebruiker verzendt 10.000 berichten en de andere verzendt 5.000 berichten. Met de API worden de 10.000 berichten gerouteerd naar wachtrij A en de 5000 berichten naar wachtrij B. Wachtrij A maakt verbinding met taakprocessor A en wachtrij B maakt verbinding met taakprocessor B. Taakprocessor A schrijft met een snelheid van 300 records per seconde naar de database. Taakprocessor B schrijft met 500 records per seconde naar de database. Beide taakprocessors maken verbinding met een databaseonderdeel met het label 'beperkt bij 800 records per seconde'. Onder de twee taakprocessors bevat Azure Storage acht blobpartities met het label 0 tot en met 7. Een opmerking geeft aan dat elke partitie 100 records per seconde waard is. Met meerdere pijlen worden de taakprocessors verbonden met de blobpartities. Groene pijlen wijzen van taakprocessor A naar partities 0, 4 en 6, wat aangeeft dat de processor leases op deze drie partities heeft. Deze leases verklaren de snelheid van 300 records per seconde. Groene pijlen wijzen van taakprocessor B naar partities 1, 2, 3, 5 en 7. Deze pijlen geven aan dat deze processor leases voor vijf partities heeft. Deze leases zijn verantwoordelijk voor de 500 records per seconde. Mislukte leasepogingen worden weergegeven als rode pijlen die doorlopen naar partities die al in bezit zijn van de andere processor. Deze pijlen vertegenwoordigen conflictenresolutie. Het diagram laat zien dat de som van alle toegewezen partities over beide processors heen 800 records per seconde bedraagt, wat overeenkomt met de throttlingslimiet van de database.

In dit voorbeeld vertegenwoordigt elke bloblease een vast aandeel van de toegestane databasedoorvoer. Een processor kan alleen items uit de wachtrij halen en schrijven met de gezamenlijke snelheid van de leases waarover deze momenteel beschikt. Naarmate processors in de loop van de tijd leases krijgen of verliezen, worden de toegestane schrijfsnelheid gewijzigd, waardoor het totale databaseverkeer binnen de geconfigureerde limiet blijft, terwijl alle in de wachtrij geplaatste werkvoortgang nog steeds wordt toegestaan.

Dit diagram bevat de volgende werkstroom:

  1. Een gebruiker verzendt 10.000 records van het type A naar de API.
  2. De API plaatst die 10.000 records in wachtrij A.
  3. Een gebruiker verzendt 5000 records van het type B naar de API.
  4. De API plaatst die 5.000 records in wachtrij B.
  5. Taakprocessor A ziet dat wachtrij A records bevat en probeert een exclusieve lease op blob 2 te verkrijgen.
  6. Taakprocessor B ziet dat wachtrij B records bevat en probeert een exclusieve lease op blob 2 te verkrijgen.
  7. Taakverwerker A kan de lease niet verkrijgen.
  8. Taakprocessor B verkrijgt de lease op blob 2 voor 15 seconden. Aanvragen voor de database kunnen nu met een snelheid van 100 per seconde worden beperkt.
  9. Taakprocessor B haalt 100 records uit wachtrij B en schrijft ze.
  10. Er gaat een seconde voorbij.
  11. Jobprocessor A ziet dat wachtrij A meer records heeft en probeert een exclusieve lease op blob 6 te verkrijgen.
  12. Jobprocessor B ziet dat wachtrij B meer records heeft en probeert een exclusieve lease op blob 3 te verkrijgen.
  13. Taakprocessor A krijgt de lease voor blob 6 voor 15 seconden. Aanvragen voor de database kunnen nu met een snelheid van 100 per seconde worden beperkt.
  14. Jobprocessor B verkrijgt de lease op blob 3 gedurende 15 seconden. Aanvragen voor de database kunnen nu worden beperkt met een snelheid van 200 per seconde. (Het heeft ook de lease voor blob 2.)
  15. Taakverwerker A verwijdert 100 records uit wachtrij A en schrijft ze.
  16. Jobprocessor B verwijdert 200 records uit wachtrij B en schrijft ze.
  17. Er gaat een seconde voorbij.
  18. Jobprocessor A ziet dat wachtrij A meer records heeft en probeert een exclusieve lease op blob 0 te verkrijgen.
  19. Jobprocessor B ziet dat wachtrij B meer records heeft en probeert een exclusieve lease op blob 1 te verkrijgen.
  20. Jobprocessor A verkrijgt de lease op blob 0 gedurende 15 seconden. Aanvragen voor de database kunnen nu worden beperkt met een snelheid van 200 per seconde. (Het heeft ook de lease op blob 6.)
  21. Jobprocessor B verkrijgt de lease op blob 1 gedurende 15 seconden. Aanvragen voor de database kunnen nu worden beperkt met een snelheid van 300 per seconde. (Het heeft ook het leasecontract voor blobs 2 en 3.)
  22. Taakverwerker A verwijdert 200 records uit wachtrij A en schrijft ze.
  23. Jobprocessor B verwijdert 300 records uit wachtrij B en schrijft ze.
  24. Enzovoort.

Na 15 seconden worden een of beide taken nog steeds niet voltooid. Naarmate de leases verlopen, moet een processor ook het aantal aanvragen verminderen dat deze in de wachtrij zet en schrijft.

Implementaties van dit patroon zijn beschikbaar in verschillende programmeertalen:

  • Go-implementatie is beschikbaar op GitHub.
  • Java-implementatie is beschikbaar op GitHub.

Volgende stappen 

De volgende richtlijnen zijn mogelijk ook relevant wanneer u dit patroon implementeert:

De volgende patronen en richtlijnen zijn mogelijk ook relevant wanneer u dit patroon implementeert:

  • Throttling. Het frequentiebeperkingspatroon wordt doorgaans geïmplementeerd als reactie op een beperkte service.

  • Retry. Wanneer aanvragen naar een service waarvoor snelheidsbeperking geldt, resulteren in snelheidsbeperkingsfouten, is het over het algemeen raadzaam die aanvragen na een passend tijdsinterval opnieuw te proberen.

  • Queue-Based Load Leveling is vergelijkbaar met het Rate Limiting-patroon, maar verschilt op een aantal belangrijke punten:

    • Snelheidsbeperking hoeft niet noodzakelijkerwijs wachtrijen te gebruiken om de belasting te beheren, maar moet wel gebruikmaken van een duurzame berichtenservice. Een frequentiebeperkingspatroon kan bijvoorbeeld gebruikmaken van services zoals Apache Kafka of Event Hubs.

    • Het snelheidsbeperkingspatroon introduceert het concept van een gedistribueerd systeem voor wederzijdse uitsluiting over partities heen, waarmee u capaciteit kunt beheren voor meerdere ongecoördineerde processen die met dezelfde service met snelheidsbeperking communiceren.

    • Het queue-based load-leveling-patroon is toepasbaar wanneer er een prestatieverschil tussen services is of wanneer u de veerkracht wilt verbeteren. Dat is dus een breder patroon dan rate limiting, dat zich specifieker richt op efficiënte toegang tot een afgeknepen service.