Antipatroon Storm opnieuw proberen

Wanneer een service niet beschikbaar is of bezet is, kunnen clients hun verbindingen te vaak opnieuw proberen, ertoe leiden dat de service moeite heeft om te herstellen en kan het probleem nog erger worden. Het is ook niet zinvol om het voor altijd opnieuw te proberen, omdat aanvragen doorgaans alleen geldig zijn voor een gedefinieerde periode.

Beschrijving van het probleem

In de cloud ervaren services soms problemen en zijn ze niet meer beschikbaar voor clients of moeten ze hun klanten beperken of de snelheid beperken. Hoewel het een goede gewoonte is dat clients mislukte verbindingen met services opnieuw proberen, is het belangrijk dat ze niet te vaak of te lang opnieuw proberen. Nieuwe proberen binnen een korte periode zullen waarschijnlijk niet slagen, omdat de services waarschijnlijk niet zijn hersteld. Bovendien kunnen services onder nog meer stress worden gezet wanneer er veel verbindingspogingen worden gedaan terwijl ze proberen te herstellen. Herhaalde verbindingspogingen kunnen zelfs de service overbelasten en het onderliggende probleem erger maken.

In het volgende voorbeeld ziet u een scenario waarin een client verbinding maakt met een servergebaseerde API. Als de aanvraag niet lukt, wordt de client onmiddellijk opnieuw proberen en blijft deze proberen. Dit soort gedrag is vaak subtieler dan in dit voorbeeld, maar hetzelfde principe is van toepassing.

public async Task<string> GetDataFromServer()
{
    while(true)
    {
        var result = await httpClient.GetAsync(string.Format("http://{0}:8080/api/...", hostName));
        if (result.IsSuccessStatusCode) break;
    }

    // ... Process result.
}

Het probleem oplossen

Clienttoepassingen moeten enkele best practices volgen om te voorkomen dat een storm voor opnieuw proberen wordt veroorzaakt.

  • Begrens het aantal nieuwe pogingen en probeer het niet langer opnieuw. Hoewel het misschien eenvoudig lijkt om een lus te schrijven, wilt u vrijwel zeker niet echt opnieuw proberen voor een lange periode, omdat de situatie die heeft geleid tot het starten van de aanvraag waarschijnlijk is while(true) gewijzigd. In de meeste toepassingen is opnieuw proberen voor een paar seconden of minuten voldoende.
  • Onderbreken tussen nieuwe pogingen. Als een service niet beschikbaar is, is het onwaarschijnlijk dat het onmiddellijk opnieuw proberen lukt. Verhoog geleidelijk de hoeveelheid tijd die u tussen pogingen wacht, bijvoorbeeld door gebruik te maken van een exponentiële back-offstrategie.
  • Fouten op een goede manier afhandelen. Als de service niet reageert, moet u overwegen of het zinvol is om de poging af te breken en een fout te retourneren aan de gebruiker of aanroeper van uw onderdeel. Houd rekening met deze foutscenario's bij het ontwerpen van uw toepassing.
  • Overweeg het gebruik van het circuit breaker-patroon,dat speciaal is ontworpen om stormen opnieuw te voorkomen.
  • Als de server een antwoordheader levert, moet u niet opnieuw proberen totdat de opgegeven periode retry-after is verstreken.
  • Gebruik officiële SDK's bij het communiceren met Azure-services. Deze SDK's hebben over het algemeen ingebouwd beleid voor opnieuw proberen en beveiliging tegen het veroorzaken of bijdragen aan stormen voor opnieuw proberen. Als u communiceert met een service die geen SDK heeft, of als de SDK logica voor opnieuw proberen niet correct verwerkt, kunt u overwegen een bibliotheek zoals Polly (voor .NET) of opnieuw te proberen (voor JavaScript) om uw logica voor opnieuw proberen correct te verwerken en te voorkomen dat u de code zelf schrijft.
  • Als u wordt uitgevoerd in een omgeving die dit ondersteunt, gebruikt u een service-mesh (of een andere abstractielaag) om uitgaande aanroepen te verzenden. Deze hulpprogramma's, zoals Dapr,ondersteunen doorgaans beleid voor opnieuw proberen en volgen automatisch best practices, zoals het uitschakelen na herhaalde pogingen. Deze aanpak betekent dat u zelf geen code voor opnieuw proberen hoeft te schrijven.
  • Overweeg batching van aanvragen en gebruik van aanvraaggroepering, indien beschikbaar. Veel SDK's verwerken het batchen van aanvragen en het groeperen van verbindingen namens u, waardoor het totale aantal uitgaande verbindingspogingen dat uw toepassing doet, wordt beperkt, hoewel u er nog steeds op moet letten dat u deze verbindingen niet te vaak opnieuw probeert.

Services moeten zich ook beschermen tegen stormen voor nieuwe pogingen.

  • Voeg een gatewaylaag toe, zodat u verbindingen tijdens een incident kunt afsluiten. Dit is een voorbeeld van het bulkhead-patroon. Azure biedt veel verschillende gatewayservices voor verschillende typen oplossingen, Front Door, Application Gatewayen API Management.
  • Vertragingsaanvragen op uw gateway, wat ervoor zorgt dat u niet zo veel aanvragen accepteert dat uw back-endonderdelen niet kunnen blijven werken.
  • Als u een beperking aan het gebruiken bent, stuurt u een header terug om clients te helpen begrijpen wanneer ze hun retry-after verbindingen opnieuw moeten proberen.

Overwegingen

  • Clients moeten rekening houden met het type fout dat wordt geretourneerd. Sommige fouttypen wijzen niet op een fout van de service, maar geven in plaats daarvan aan dat de client een ongeldige aanvraag heeft verzonden. Als een clienttoepassing bijvoorbeeld een foutbericht ontvangt, helpt het waarschijnlijk niet om dezelfde aanvraag opnieuw uit te proberen, omdat de server u vertelt dat uw aanvraag 400 Bad Request ongeldig is.
  • Clients moeten rekening houden met de tijdsduur die zinvol is om verbindingen opnieuw te proberen. De tijdsduur die u opnieuw moet proberen, wordt bepaald door uw bedrijfsvereisten en of u redelijk een fout kunt doorgeven aan een gebruiker of aanroeper. In de meeste toepassingen is opnieuw proberen voor een paar seconden of minuten voldoende.

Het probleem vaststellen

Vanuit het perspectief van een client kunnen de symptomen van dit probleem zeer lange reactie- of verwerkingstijden omvatten, samen met telemetrie die wijst op herhaalde pogingen om de verbinding opnieuw te proberen.

Vanuit het perspectief van een service kunnen de symptomen van dit probleem een groot aantal aanvragen van één client binnen een korte periode omvatten, of een groot aantal aanvragen van één client tijdens het herstellen van uitval. Symptomen kunnen ook problemen zijn bij het herstellen van de service of lopende oplopende fouten van de service direct nadat een fout is hersteld.

Voorbeeld van diagnose

In de volgende secties ziet u één benadering voor het detecteren van een potentiële storm voor opnieuw proberen, zowel aan de clientzijde als aan de servicezijde.

Identificeren op client-telemetrie

Azure-toepassing Insights registreert telemetrie van toepassingen en maakt de gegevens beschikbaar voor het uitvoeren van query's en visualisaties. Uitgaande verbindingen worden bijgespoord als afhankelijkheden en informatie over deze verbindingen kan worden gebruikt en in een grafiek worden weergegeven om te bepalen wanneer een client een groot aantal uitgaande aanvragen naar dezelfde service maakt.

De volgende grafiek is afkomstig van het tabblad Metrische gegevens in de Application Insights-portal en geeft de metrische gegevens voor afhankelijkheidsfouten weer, gesplitst op naam van externe afhankelijkheid. Dit illustreert een scenario waarin er binnen korte tijd een groot aantal (meer dan 21.000) mislukte verbindingspogingen naar een afhankelijkheid was.

Schermopname van Application Insights met 21.000 afhankelijkheidsfouten in één afhankelijkheid binnen een periode van 30 minuten

Identificatie van server-telemetrie

Servertoepassingen kunnen mogelijk grote aantallen verbindingen van één client detecteren. In het volgende voorbeeld fungeert Azure Front Door als een gateway voor een toepassing en is geconfigureerd om alle aanvragen naar een Log Analytics-werkruimte te loggen.

De volgende Kusto-query kan worden uitgevoerd op basis van Log Analytics. Het identificeert IP-adressen van klanten die de afgelopen dag een groot aantal aanvragen naar de toepassing hebben verzonden.

AzureDiagnostics
| where ResourceType == "FRONTDOORS" and Category == "FrontdoorAccessLog"
| where TimeGenerated > ago(1d)
| summarize count() by bin(TimeGenerated, 1h), clientIp_s
| order by count_ desc

Als u deze query uitvoert tijdens een storm voor opnieuw proberen, ziet u een groot aantal verbindingspogingen vanaf één IP-adres.

Schermopname van Log Analytics met 81.608 binnenkomende verbindingen met Front Door vanaf één IP-adres binnen een periode van één uur