Försök utföra Storm-antimönstret igen
Om en tjänst inte är tillgänglig eller upptagen kan det leda till att klienter försöker ansluta igen för ofta, vilket kan göra att tjänsten får problem med återställningen och kan förvärra problemet. Det är inte heller meningsfullt att försöka igen för alltid, eftersom begäranden vanligtvis bara är giltiga under en definierad tidsperiod.
Problembeskrivning
I molnet får tjänster ibland problem och blir otillgängliga för klienter, eller måste begränsa eller betygsätta sina klienter. Det är en bra idé för klienter att försöka ansluta till tjänster igen, men det är viktigt att de inte försöker för ofta eller för länge. Det är osannolikt att återförsök inom en kort tidsperiod kommer att lyckas eftersom tjänsterna sannolikt inte kommer att ha återställts. Dessutom kan tjänster belastas ännu mer när många anslutningsförsök görs medan de försöker återställa, och upprepade anslutningsförsök kan till och med överbelasta tjänsten och förvärra det underliggande problemet.
I följande exempel visas ett scenario där en klient ansluter till ett serverbaserat API. Om begäran inte lyckas försöker klienten igen omedelbart och fortsätter att försöka igen för alltid. Den här typen av beteende är ofta mer subtilt än i det här exemplet, men samma princip gäller.
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.
}
Så åtgärdar du problemet
Klientprogram bör följa vissa metodtips för att undvika att orsaka en ny försöksstorm.
- Begränsa antalet återförsök och fortsätt inte att försöka igen under en längre tid. Även om det kan verka lätt att skriva en
while(true)
loop vill du nästan säkert inte försöka igen under en längre tid, eftersom situationen som ledde till att begäran initierades förmodligen har ändrats. I de flesta program räcker det att försöka igen i några sekunder eller minuter. - Pausa mellan återförsök. Om en tjänst inte är tillgänglig kommer det sannolikt inte att lyckas att försöka igen omedelbart. Öka gradvis den tid du väntar mellan försöken, till exempel genom att använda en exponentiell backoff-strategi.
- Hantera fel på ett smidigt sätt. Om tjänsten inte svarar bör du överväga om det är lämpligt att avbryta försöket och returnera ett fel tillbaka till användaren eller anroparen av komponenten. Tänk på dessa felscenarier när du utformar ditt program.
- Överväg att använda kretsbrytarmönstret, som är särskilt utformat för att undvika nya försök med stormar.
- Om servern innehåller ett
retry-after
svarshuvud kontrollerar du att du inte försöker igen förrän den angivna tidsperioden har förflutit. - Använd officiella SDK:er när du kommunicerar med Azure-tjänster. Dessa SDK:er har vanligtvis inbyggda återförsöksprinciper och skydd mot att orsaka eller bidra till återförsök av stormar. Om du kommunicerar med en tjänst som inte har något SDK eller där SDK:n inte hanterar logiken för omprövning korrekt kan du överväga att använda ett bibliotek som Polly (för .NET) eller försöka igen (för JavaScript) för att hantera logiken för återförsök på rätt sätt och undvika att skriva koden själv.
- Om du kör i en miljö som stöder det använder du ett tjänstnät (eller ett annat abstraktionslager) för att skicka utgående anrop. Dessa verktyg, till exempel Dapr, stöder vanligtvis återförsöksprinciper och följer automatiskt bästa praxis, till exempel säkerhetskopiering efter upprepade försök. Den här metoden innebär att du inte behöver skriva kod för återförsök själv.
- Överväg att batcha begäranden och använda poolning av begäranden där det är tillgängligt. Många SDK:er hanterar batchbearbetning av förfrågningar och anslutningspooler åt dig, vilket minskar det totala antalet utgående anslutningsförsök som programmet gör, även om du fortfarande måste vara noga med att inte försöka anslutningarna för ofta.
Tjänsterna bör också skydda sig mot återförsöksstormar.
- Lägg till ett gatewaylager så att du kan stänga av anslutningar under en incident. Det här är ett exempel på mönstret Skott. Azure tillhandahåller många olika gatewaytjänster för olika typer av lösningar, inklusive Front Door, Application Gateway och API Management.
- Begränsa begäranden på din gateway, vilket säkerställer att du inte accepterar så många begäranden att serverdelskomponenterna inte kan fortsätta att fungera.
- Om du begränsar skickar du tillbaka ett
retry-after
huvud som hjälper klienterna att förstå när de ska försöka ansluta igen.
Överväganden
- Klienter bör överväga vilken typ av fel som returneras. Vissa feltyper indikerar inte ett fel i tjänsten, utan anger i stället att klienten skickade en ogiltig begäran. Om ett klientprogram till exempel får ett
400 Bad Request
felsvar kommer återförsök av samma begäran förmodligen inte att hjälpa eftersom servern säger att din begäran inte är giltig. - Klienter bör ta hänsyn till hur lång tid det tar att försöka ansluta igen. Hur lång tid du bör försöka igen beror på dina affärskrav och om du rimligen kan sprida ett fel tillbaka till en användare eller uppringare. I de flesta program räcker det att försöka igen i några sekunder eller minuter.
Så identifierar du problemet
Från en klients perspektiv kan symptomen på det här problemet omfatta mycket långa svars- eller bearbetningstider, tillsammans med telemetri som indikerar upprepade försök att försöka ansluta igen.
Ur en tjänsts perspektiv kan symptomen på det här problemet omfatta ett stort antal begäranden från en klient inom en kort tidsperiod, eller ett stort antal begäranden från en enda klient vid återställning efter avbrott. Symtomen kan också vara problem vid återställning av tjänsten eller pågående sammanhängande fel i tjänsten direkt efter att ett fel har reparerats.
Exempeldiagnos
Följande avsnitt illustrerar en metod för att identifiera en potentiell storm för återförsök, både på klientsidan och på tjänstsidan.
Identifiera från klienttelemetri
Azure Application Insights registrerar telemetri från program och gör data tillgängliga för frågor och visualisering. Utgående anslutningar spåras som beroenden och information om dem kan nås och kartläggas för att identifiera när en klient gör ett stort antal utgående begäranden till samma tjänst.
Följande diagram hämtades från fliken Mått i Application Insights-portalen och visar måttet Beroendefel uppdelat efter Namn på fjärrberoende. Detta illustrerar ett scenario där det fanns ett stort antal (över 21 000) misslyckade anslutningsförsök till ett beroende inom en kort tid.
Identifiera från servertelemetri
Serverprogram kan identifiera ett stort antal anslutningar från en enda klient. I följande exempel fungerar Azure Front Door som en gateway för ett program och har konfigurerats för att logga alla begäranden till en Log Analytics-arbetsyta.
Följande Kusto-fråga kan köras mot Log Analytics. Den identifierar klient-IP-adresser som har skickat ett stort antal begäranden till programmet under den senaste dagen.
AzureDiagnostics
| where ResourceType == "FRONTDOORS" and Category == "FrontdoorAccessLog"
| where TimeGenerated > ago(1d)
| summarize count() by bin(TimeGenerated, 1h), clientIp_s
| order by count_ desc
Om du kör den här frågan under en omförsöksstorm visas ett stort antal anslutningsförsök från en enda IP-adress.