Scenario voor het afstemmen van prestaties: Gedistribueerde bedrijfstransacties

In dit artikel wordt beschreven hoe een ontwikkelingsteam metrische gegevens gebruikt om knelpunten te vinden en de prestaties van een gedistribueerd systeem te verbeteren. Het artikel is gebaseerd op de werkelijke belastingstests die we hebben uitgevoerd voor een voorbeeldtoepassing. De toepassing is van de basislijn Azure Kubernetes Service (AKS) voor microservices.

Dit artikel maakt deel uit van een reeks. Lees het eerste deel hier.

Scenario: een clienttoepassing initieert een bedrijfstransactie die meerdere stappen omvat.

Dit scenario omvat een drone delivery-toepassing die wordt uitgevoerd op AKS. Klanten gebruiken een web-app om leveringen per drone te plannen. Voor elke transactie zijn meerdere stappen vereist die worden uitgevoerd door afzonderlijke microservices op de back-end:

  • De Leveringsservice beheert leveringen.
  • De Drone Scheduler-service plant drones om op te halen.
  • De pakketservice beheert pakketten.

Er zijn twee andere services: een opnameservice die clientaanvragen accepteert en deze in een wachtrij plaatst voor verwerking, en een Workflow-service die de stappen in de werkstroom coördineert.

Diagram met de gedistribueerde werkstroom

Zie Een microservicearchitectuurontwerpen voor meer informatie over dit scenario.

Test 1: Basislijn

Voor de eerste belastingstest heeft het team een AKS-cluster met zes knooppunt gemaakt en drie replica's van elke microservice geïmplementeerd. De belastingstest was een stapbelastingstest, beginnend bij twee gesimuleerde gebruikers en het opstarten van maximaal 40 gesimuleerde gebruikers.

Instelling Waarde
Clusterknooppunten 6
Peulen 3 per service

In het volgende diagram ziet u de resultaten van de belastingstest, zoals weergegeven in Visual Studio. De paarse lijn geeft de gebruikersbelasting weer en de oranje lijn geeft het totale aantal aanvragen weer.

Graph van Visual Studio belastingstestresultaten

Het eerste wat u over dit scenario moet realiseren, is dat clientaanvragen per seconde geen nuttige prestatiemeter zijn. Dat komt doordat aanvragen asynchroon worden verwerkt door de toepassing, zodat de client meteen een antwoord krijgt. De antwoordcode is altijd HTTP 202 (Geaccepteerd), wat betekent dat de aanvraag is geaccepteerd, maar de verwerking niet is voltooid.

Wat we echt willen weten, is of de back-end de aanvraagsnelheid bij houdt. De Service Bus kan pieken opvangen, maar als de back-end een langdurige belasting niet kan verwerken, zal de verwerking steeds verder achterop komen.

Hier ziet u een meer informatieve grafiek. Het aantal binnenkomende en uitgaande berichten wordt in de Service Bus wachtrij. Binnenkomende berichten worden lichtblauw weergegeven en uitgaande berichten worden donkerblauw weergegeven:

Graph van binnenkomende en uitgaande berichten

Deze grafiek laat zien dat de snelheid van binnenkomende berichten toeneemt, een piek bereikt en vervolgens weer terug daalt naar nul aan het einde van de belastingstest. Maar het aantal uitgaande berichten piekt vroeg in de test en daalt vervolgens. Dit betekent dat de Workflow-service, die de aanvragen verwerkt, niet bijwerkt. Zelfs nadat de belastingstest is beëindigd (ongeveer 9:22 in de grafiek), worden berichten nog steeds verwerkt omdat de Workflow-service de wachtrij blijft leeglaten.

Wat vertraagt de verwerking? Het eerste wat u moet zoeken, zijn fouten of uitzonderingen die kunnen duiden op een systematisch probleem. Het toepassingskaart in Azure Monitor toont de grafiek van aanroepen tussen onderdelen. Dit is een snelle manier om problemen op te lossen en vervolgens door te klikken voor meer informatie.

Het toepassingskaart laat zien dat de Workflow-service fouten krijgt van de Leveringsservice:

Schermopname van toepassingskaart

Voor meer informatie kunt u een knooppunt in de grafiek selecteren en in een end-to-end transactieweergave klikken. In dit geval ziet u dat de Leveringsservice HTTP 500-fouten retournt. De foutberichten geven aan dat er een uitzondering wordt veroorzaakt door geheugenlimieten in Azure Cache voor Redis.

Schermopname van de end-to-end-transactieweergave

Mogelijk ziet u dat deze aanroepen naar Redis niet worden weergegeven in het toepassingskaart. Dat komt doordat de .NET-bibliotheek voor Application Insights geen ingebouwde ondersteuning voor het bijhouden van Redis als afhankelijkheid heeft. (Zie Automatische verzameling van afhankelijkheden voor een lijst met wat buiten het vak wordt ondersteund.) Als terugval kunt u de TrackDependency-API gebruiken om elke afhankelijkheid bij te houden. Belastingstests tonen vaak dit soort hiaten in de telemetrie, die kunnen worden verteerd.

Test 2: Grotere cachegrootte

Voor de tweede belastingstest heeft het ontwikkelteam de cachegrootte in Azure Cache voor Redis. (Zie How to Scale Azure Cache voor Redis.) Door deze wijziging zijn de out-of-memory-uitzonderingen opgelost en nu bevat het toepassingskaart nul fouten:

Schermopname van toepassingskaart die laat zien dat het vergroten van de cachegrootte de uitzonderingen over geheugenverlies heeft opgelost.

Er is echter nog steeds een enorme vertraging bij het verwerken van berichten. Op het piekpunt van de belastingstest is de snelheid van het binnenkomende bericht meer dan 5 × de uitgaande snelheid:

Graph aantal binnenkomende en uitgaande berichten met de snelheid van het binnenkomende bericht is meer dan 5 keer de uitgaande snelheid.

In de volgende grafiek wordt de doorvoer in termen van voltooiing van berichten met andere woorden, de snelheid waarmee de Werkstroomservice de Service Bus — als voltooid markeert. Elk punt in de grafiek vertegenwoordigt 5 seconden aan gegevens, met een maximale doorvoer van ~16 per seconde.

Graph doorvoer van berichten

Deze grafiek is gegenereerd door een query uit te voeren in de Log Analytics-werkruimte met behulp van de Kusto-querytaal:

let start=datetime("2020-07-31T22:30:00.000Z");
let end=datetime("2020-07-31T22:45:00.000Z");
dependencies
| where cloud_RoleName == 'fabrikam-workflow' 
| where timestamp > start and timestamp < end
| where type == 'Azure Service Bus' 
| where target has 'https://dev-i-iuosnlbwkzkau.servicebus.windows.net'
| where client_Type == "PC"
| where name == "Complete" 
| summarize succeeded=sumif(itemCount, success == true), failed=sumif(itemCount, success == false) by bin(timestamp, 5s)
| render timechart

Test 3: De back-endservices uitschalen

Het lijkt erop dat de back-end het knelpunt is. Een eenvoudige volgende stap is het uitschalen van de bedrijfsservices (Pakket, Levering en Drone Scheduler) en te kijken of de doorvoer verbetert. Voor de volgende belastingstest schaalde het team deze services uit van drie replica's naar zes replica's.

Instelling Waarde
Clusterknooppunten 6
Opnameservice 3 replica's
Werkstroomservice 3 replica's
Pakket, levering, Drone Scheduler-services 6 replica's elk

Helaas toont deze belastingstest slechts een lichte verbetering. Uitgaande berichten blijven nog steeds niet up-up met binnenkomende berichten:

Graph van binnenkomende en uitgaande berichten die laten zien dat uitgaande berichten nog steeds niet worden bij te houden met binnenkomende berichten.

De doorvoer is consistenter, maar het maximum dat wordt bereikt, is ongeveer hetzelfde als de vorige test:

Graph van de berichtdoorvoer, wat laat zien dat het maximum dat is bereikt ongeveer gelijk is aan de vorige test.

Als u bovendien naar Azure Monitor voor containerskijkt, lijkt het erop dat het probleem niet wordt veroorzaakt door resource-uitputting in het cluster. Ten eerste laten de metrische gegevens op knooppuntniveau zien dat het CPU-gebruik onder de 40% blijft, zelfs in het 95e percentiel, en dat het geheugengebruik ongeveer 20% is.

Graph van het gebruik van AKS-knooppunt

In een Kubernetes-omgeving is het mogelijk dat afzonderlijke pods resourcebeperkt zijn, zelfs wanneer dat niet het aantal knooppunten is. In de weergave op podniveau ziet u echter dat alle pods in orde zijn.

Graph van het gebruik van AKS-pods

Uit deze test blijkt dat alleen het toevoegen van meer pods aan de back-end niet helpt. De volgende stap is om de Workflow-service beter te bekijken om te begrijpen wat er gebeurt wanneer berichten worden verwerkt. Application Insights laat zien dat de gemiddelde duur van de bewerking van de Process Werkstroomservice 246 ms is.

Schermopname van Application Insights

We kunnen ook een query uitvoeren om metrische gegevens op te halen over de afzonderlijke bewerkingen binnen elke transactie:

Doel percentile_duration_50 percentile_duration_95
https://dev-i-iuosnlbwkzkau.servicebus.windows.net/ | dev-i-iuosnlbwkzkau 86.66950203 283.4255578
levering 37 57
pakket 12 17
dronescheduler 21 41

De eerste rij in deze tabel vertegenwoordigt de Service Bus wachtrij. De andere rijen zijn de aanroepen naar de back-endservices. Ter referentie is dit de Log Analytics-query voor deze tabel:

let start=datetime("2020-07-31T22:30:00.000Z");
let end=datetime("2020-07-31T22:45:00.000Z");
let dataset=dependencies
| where timestamp > start and timestamp < end
| where (cloud_RoleName == 'fabrikam-workflow')
| where name == 'Complete' or target in ('package', 'delivery', 'dronescheduler');
dataset
| summarize percentiles(duration, 50, 95) by target

Schermopname van het resultaat van de Log Analytics-query

Deze latentie lijkt redelijk. Maar dit is het belangrijkste inzicht: Als de totale bewerkingstijd ~250 ms is, wordt er een strikte bovengrens gegeven aan hoe snel berichten serieel kunnen worden verwerkt. De sleutel tot het verbeteren van de doorvoer is daarom groter parallellisme.

Dat moet mogelijk zijn in dit scenario, om twee redenen:

  • Dit zijn netwerkoproepen, dus de meeste tijd wordt besteed aan het wachten op voltooiing van I/O
  • De berichten zijn onafhankelijk en hoeven niet op volgorde te worden verwerkt.

Test 4: Parallellelisme verhogen

Voor deze test heeft het team zich gericht op het vergroten van de parallellisme. Hiervoor hebben ze twee instellingen aangepast op de Service Bus client die wordt gebruikt door de Workflow-service:

Instelling Beschrijving Standaard Nieuwe waarde
MaxConcurrentCalls Het maximum aantal berichten dat gelijktijdig moet worden verwerkt. 1 20
PrefetchCount Hoeveel berichten de client van tevoren zal ophalen in de lokale cache. 0 3000

Zie Best Practices for performance improvements using Service Bus Messaging (Best Practices voor prestatieverbeteringen met behulp van Service Bus Messaging) voor meer informatie over deze instellingen. Het uitvoeren van de test met deze instellingen heeft de volgende grafiek geproduceerd:

Graph van binnenkomende en uitgaande berichten met het aantal uitgaande berichten dat het totale aantal binnenkomende berichten overschrijdt.

Zoals u weet, worden binnenkomende berichten lichtblauw weergegeven en worden uitgaande berichten donkerblauw weergegeven.

Op het eerste gezicht is dit een zeer grafische grafiek. De snelheid van het uitgaande bericht houdt de inkomende snelheid een tijdje exact bij. Maar om ongeveer 2:03 uur neemt de snelheid van binnenkomende berichten af, terwijl het aantal uitgaande berichten blijft toenemen, wat het totale aantal binnenkomende berichten overschrijdt. Dat lijkt onmogelijk.

De aanwijzing hiervoor is te vinden in de weergave Afhankelijkheden in Application Insights. Deze grafiek bevat een overzicht van alle aanroepen die de Workflow-service heeft gedaan Service Bus:

Graph afhankelijkheidsoproepen

U ziet dat de vermelding voor DeadLetter . Die aanroepen geven aan dat berichten in de wachtrij Service Bus in de wachtrij staan.

Als u wilt weten wat er gebeurt, moet u inzicht hebben in de semantiek van Peek-Lock in Service Bus. Wanneer een client Peek-Lock gebruikt, Service Bus een bericht atomisch opgehaald en vergrendeld. Tijdens het vergrendelen wordt het bericht gegarandeerd niet bezorgd bij andere ontvangers. Als de vergrendeling is verlopen, wordt het bericht beschikbaar voor andere ontvangers. Na een maximum aantal bezorgingspogingen (dat configureerbaar is), Service Bus de berichten in een wachtrij voor in de wachtrij geplaatst, waar ze later kunnen worden onderzocht.

Houd er wel voor dat de Workflow-service vooraf grote batches met berichten — van 3000 berichten tegelijk opeet). Dit betekent dat de totale tijd voor het verwerken van elk bericht langer is, wat resulteert in time-outs van berichten, teruggaan naar de wachtrij en uiteindelijk naar de wachtrij voor in de wachtrij voor in wachtrij te gaan.

U kunt dit gedrag ook zien in de uitzonderingen, waarbij talloze MessageLostLockException uitzonderingen worden vastgelegd:

Schermopname van application Insights uitzonderingen met talloze MessageLostLockException-uitzonderingen.

Test 5: Duur vergrendeling verhogen

Voor deze belastingstest is de duur van de berichtvergrendeling ingesteld op 5 minuten om time-outs van vergrendelingen te voorkomen. In de grafiek van binnenkomende en uitgaande berichten ziet u nu dat het systeem de snelheid van binnenkomende berichten bij houdt:

Graph van binnenkomende en uitgaande berichten die laten zien dat het systeem de snelheid van binnenkomende berichten bij houdt.

Gedurende de totale duur van de belastingstest van 8 minuten heeft de toepassing 25.000 bewerkingen voltooid, met een piekdoorvoer van 72 bewerkingen per seconde, wat een toename van de maximale doorvoer van 400% vertegenwoordigt.

Graph doorvoer van berichten met een toename van 400% van de maximale doorvoer.

Het uitvoeren van dezelfde test met een langere duur heeft echter aangetoond dat de toepassing deze snelheid niet kan handhaven:

Graph van binnenkomende en uitgaande berichten die laten zien dat de toepassing deze snelheid niet kan handhaven.

De metrische gegevens van de container laten zien dat het maximale CPU-gebruik dicht bij 100% ligt. Op dit moment lijkt de toepassing CPU-gebonden te zijn. Het schalen van het cluster kan de prestaties nu verbeteren, in tegenstelling tot de vorige poging om uit te schalen.

Graph gebruik van AKS-knooppunt laat zien dat het maximale CPU-gebruik dicht bij 100% ligt.

Test 6: De back-endservices uitschalen (opnieuw)

Voor de laatste belastingstest in de reeks heeft het team het Kubernetes-cluster en de pods als volgt uitschaald:

Instelling Waarde
Clusterknooppunten 12
Opnameservice 3 replica's
Werkstroomservice 6 replica's
Pakket, levering, Drone Scheduler-services 9 replica's per

Deze test heeft geleid tot een hogere doorvoer, zonder significante vertragingen bij het verwerken van berichten. Bovendien blijft het CPU-gebruik van knooppunt onder de 80%.

Graph doorvoer van berichten met een hogere doorvoer, zonder significante vertragingen bij het verwerken van berichten.

Samenvatting

Voor dit scenario zijn de volgende knelpunten geïdentificeerd:

  • Out-of-memory uitzonderingen in Azure Cache voor Redis.
  • Gebrek aan parallelle verwerking van berichten.
  • Onvoldoende berichtvergrendelingsduur, wat leidt tot time-outs van vergrendelingen en berichten die in de wachtrij voor niet-vergrendelde berichten worden geplaatst.
  • CPU-uitputting.

Om deze problemen te diagnosticeren, vertrouwde het ontwikkelteam op de volgende metrische gegevens:

  • De snelheid van binnenkomende en uitgaande Service Bus berichten.
  • Toepassingskaart in Application Insights.
  • Fouten en uitzonderingen.
  • Aangepaste Log Analytics-query's.
  • CPU- en geheugengebruik in Azure Monitor voor containers.

Volgende stappen

Zie Een microservicearchitectuurontwerpen voor meer informatie over het ontwerp van dit scenario.