Tillfällig felhantering
Alla program som kommunicerar med fjärrtjänster och -resurser måste vara känsliga för tillfälliga fel. Detta gäller särskilt för program som körs i molnet, där typen av miljö och anslutning via internet innebär att dessa typer av fel troligen förekommer oftare. Tillfälliga fel omfattar tillfällig avbruten nätverksanslutning till komponenter och tjänster, att en tjänst är tillfälligt otillgänglig eller att tidsgränser uppnås när en tjänst är upptagen. Dessa fel är ofta själv korrigerande, och om åtgärden upprepas efter en lämplig fördröjning är det troligt att den lyckas.
Det här dokumentet beskriver allmänna riktlinjer för hantering av tillfälliga fel. Information om hur du hanterar tillfälliga fel när du använder Microsoft Azure-tjänster finns i Azure service-specific retry guidelines (Azures tjänstspecifika vägledning för återförsök).
Varför uppstår tillfälliga fel i molnet?
Tillfälliga fel kan uppstå i alla miljöer, på alla plattformar eller operativsystem, samt i alla typer av program. I lösningar som körs på lokal infrastruktur underhålls prestanda och tillgänglighet för programmet och dess komponenter vanligtvis via dyr och ofta underanvänd maskinvaruredundans, och komponenter och resurser finns nära varandra. Även om den här metoden gör ett fel mindre troligt kan det fortfarande resultera i tillfälliga fel – och till och med ett avbrott genom oförutsedda händelser som externa strömförsörjnings- eller nätverksproblem eller andra katastrofscenarier.
Molnvärdtjänster, inklusive privata molnsystem, kan erbjuda högre övergripande tillgänglighet genom att använda delade resurser, redundans, automatisk redundans och dynamisk resursallokering över många beräkningsnoder. Dessa miljöers utformning kan dock innebära att risken för att tillfälliga fel uppstår kan öka. Det finns flera orsaker till detta:
Flera resurser i en molnmiljö delas och åtkomsten till dessa resurser kan begränsas för att skydda resursen. Vissa tjänster nekar anslutningar när belastningen ökar till en viss nivå eller det maximala dataflödet nås, för att tillåta bearbetning av befintliga begäranden och se till att prestanda för tjänsten levereras till alla användare. Begränsning hjälper dig upprätthålla tjänstekvaliteten för grannar och andra klienter som använder den delade resursen.
Molnmiljöer skapas med stora mängder vanliga maskinvaruenheter. De ger prestanda genom att belastningen distribueras dynamiskt över flera beräkningsenheter och infrastrukturkomponenter och ger tillförlitlighet genom att felaktiga enheter automatiskt återvinns eller ersätts. Den här dynamiska utformningen innebär att tillfälliga fel och tillfälliga anslutningsfel ibland kan uppstå.
Det finns ofta fler maskinvarukomponenter för t.ex. nätverksinfrastruktur såsom routrar och lastbalanserare, mellan programmet och resurserna och tjänsterna som det använder. Den här ytterligare infrastrukturen kan ibland medföra längre svarstid för anslutningar och tillfälliga anslutningsfel.
Nätverksförhållanden mellan klienten och servern kan vara en variabel, särskilt när kommunikation sker över internet. Även på lokala platser kan tung trafikbelastning sakta ner kommunikationen och orsaka tillfälliga anslutningsfel.
Utmaningar
Tillfälliga fel kan ha en enorm inverkan på den uppfattade tillgängligheten för ett program, även om det har testats noggrant under alla förutsebara omständigheter. För att säkerställa att molnbaserade program fungerar på ett tillförlitligt sätt måste de kunna klara följande utmaningar:
Programmet måste kunna identifiera fel när de inträffar och avgöra om dessa fel kan antas vara tillfälliga, mer långvariga eller om de är permanenta fel. Olika resurser returnerar troligen olika svar när ett fel inträffar, och svaren kan även variera beroende på åtgärdens kontext. Svaret för ett fel vid läsning från lagring kan till exempel vara annorlunda jämfört med ett svar för fel vid skrivning till lagring. Många resurser och tjänster har kontrakt med väl dokumenterade tillfälliga fel. Men om någon sådan information inte är tillgänglig kan det vara svårt att identifiera felet och avgöra om det troligen är tillfälligt.
Programmet måste kunna återförsöka åtgärden igen om det anser att felet nog är tillfälligt och hålla reda på hur många gånger som åtgärden återförsöktes.
Programmet måste använda en lämplig strategi för återförsöken. Den här strategin anger antalet gånger som det ska återförsöka, fördröjningen mellan varje försök, samt åtgärderna som ska utföras efter ett misslyckat försök. Det lämpliga antalet försök och fördröjningen mellan vart och ett av dem är ofta svåra att avgöra, och varierar beroende på typen av resurs och aktuella driftsförhållanden för resursen och programmet.
Allmänna riktlinjer
Följande riktlinjer hjälper dig att utforma en lämplig mekanism för hantering av tillfälliga fel för dina program:
Avgör om det finns en inbyggd mekanism för återförsök:
Många tjänster tillhandahåller ett SDK eller klientbibliotek som innehåller en mekanism för hantering av tillfälliga fel. Återförsöksprincipen anpassas vanligtvis utifrån måltjänstens typ och krav. REST-gränssnitt för tjänster kan även returnera information som är användbar för att fastställa om ett återförsök är lämpligt, och hur lång tid som ska gå innan nästa försök.
Använd den inbyggda återförsöksmekanismen där det är tillgängligt, såvida du inte har specifika och välförenade krav som gör ett annat återförsöksbeteende mer lämpligt.
Avgör om åtgärden är lämplig för återförsök:
Du bör endast utföra återförsök för åtgärder där felen är tillfälliga (visas vanligtvis genom typen av fel), och om det åtminstone finns någon sannolikhet för att åtgärden ska lyckas när den återförsöks. Det finns ingen anledning att försöka igen som indikerar en ogiltig åtgärd, till exempel en databasuppdatering av ett objekt som inte finns, eller begäranden till en tjänst eller resurs som har drabbats av ett allvarligt fel.
I allmänhet bör du endast implementera återförsök när du kan fastställa den fullständiga effekten av det, villkoren är väl förstådda och kan valideras. Om inte så låter du den anropande koden implementera återförsök. Kom ihåg att fel som returnerats från resurser och tjänster utanför din kontroll kan utvecklas över tid, och att du kan behöva kontrollera din identifieringslogik för tillfälliga fel igen.
När du skapar tjänster eller komponenter, kan du överväga att implementera felkoder och meddelanden som hjälper klienter avgöra om de bör göra återförsök för felaktiga åtgärder. Meddela särskilt om klienten ska göra ett återförsök för åtgärden (kanske genom att returnera ett värde av typen isTransient) och föreslå en lämplig fördröjning innan nästa återförsök. Överväg att returnera anpassade fel som definierats i dina servicekontrakt om du skapar en webbtjänst. Även om allmänna klienter kanske inte kan läsa dem kan de vara användbara när du skapar anpassade klienter.
Fastställ lämpligt värde och intervall för återförsök:
Det är viktigt att optimera antalet återförsök och intervallet efter typen av användningsfall. Om du inte återförsöker tillräckligt många gånger kan programmet inte slutföra åtgärden och ett fel uppstår sannolikt. Om du gör för många återförsök eller använder ett för kort intervall mellan försök kan programmet potentiellt hålla resurser, till exempel trådar, anslutningar och minne under långa perioder, något som påverkar programmets hälsotillstånd.
Lämpliga värden för tidsintervall och antal nya återförsök beror på vilken typ av åtgärd som ska utföras. Om åtgärden till exempel utgör en del av en användarinteraktion, ska intervallet vara kort och endast några återförsök ska göras för att undvika att användare väntar på ett svar (som skapar öppna anslutningar och kan minska tillgängligheten för andra användare). Om åtgärden är en del av ett långvarigt eller kritiskt arbetsflöde, där det är dyrt eller tidskrävande att avbryta och starta om processen, är det lämpligt att vänta längre mellan försök och försöka igen flera gånger.
Att bestämma lämpliga intervall mellan försök tillhör den svåraste delen för att utveckla en lyckad strategi. Vanliga strategier använder följande typer av intervall för återförsök:
Exponentiell back-off. Programmet väntar en kort tid före det första återförsöket och ökar sedan exponentiellt tiden mellan varje efterföljande återförsök. Det kan t.ex. göra ett återförsök efter 3 sekunder, 12 sekunder, 30 sekunder och så vidare.
Inkrementella intervall. Programmet väntar en kort tid före det första återförsöket och ökar sedan stegvis tiden mellan varje efterföljande återförsök. Det kan t.ex. göra ett återförsök efter 3 sekunder, 7 sekunder, 13 sekunder och så vidare.
Regelbundna intervall. Programmet väntar samma tid mellan varje återförsök. Det kan till exempel göra ett återförsök för åtgärden var tredje sekund.
Omedelbart återförsök. Ibland är ett tillfälligt fel kort, kanske på grund av en händelse, till exempel en kollision mellan nätverkspaket eller en topp i en maskinvarukomponent. I det här fallet är det lämpligt att omedelbart göra ett återförsök eftersom det kan lyckas om felet har åtgärdats under tiden det tar för programmet att sätta samman och skicka nästa begäran. Det bör dock aldrig finnas fler än ett omedelbart återförsök och du bör växla till alternativa strategier, till exempel exponentiella back off- eller återställningsåtgärder, om det omedelbara återförsöket misslyckas.
Randomisering. Alla strategier för återförsök som listas ovan kan innehålla slumpmässighet för att förhindra att flera instanser av klienten skickar efterföljande återförsök samtidigt. En instans kan till exempel försöka igen efter 3 sekunder, 11 sekunder, 28 sekunder och så vidare, medan en annan instans kan försöka igen efter 4 sekunder, 12 sekunder, 26 sekunder och så vidare. Slumpmässighet är en användbar teknik som kan kombineras med andra strategier.
Som en allmän riktlinje kan du använda en strategi för exponentiell backoff för bakgrundsåtgärder, och strategier för omedelbara återförsök eller återförsök med regelbundna intervall för interaktiva åtgärder. I båda fallen bör du välja fördröjning och antal försök så att den maximala svarstiden för alla antal återförsök ligger inom det obligatoriska svarstidskravet för slutpunkt till slutpunkt.
Väg in kombinationen av alla faktorer som bidrar till den sammanlagda maximala tidsgränsen för en åtgärd som återförsöks. Dessa faktorer omfattar tiden det tar för en felaktig anslutning att skapa ett svar (oftast anges den av ett timeoutvärde i klienten) samt fördröjningen mellan återförsök och det maximala antalet återförsök. Summan av alla dessa tider kan resultera i långa övergripande åtgärdstider, särskilt när du använder en strategi för exponentiell fördröjning där intervallet mellan återförsök växer snabbt efter varje fel. Om en process måste uppfylla ett visst serviceavtal (SLA) måste den totala drifttiden, inklusive alla tidsgränser och fördröjningar, vara inom de gränser som anges i serviceavtalet.
Alltför aggressiv återförsöksstrategier, som har intervall som är för korta eller återförsök som är för frekventa, kan ha en negativ inverkan på målresursen eller måltjänsten. Det kan hindra resursen eller tjänsten från återställning från sitt överbelastade tillstånd, och den fortsätter blockera eller neka begäranden. Detta resulterar i en ond cirkel där fler och fler begäranden skickas till resursen eller tjänsten, och därför ytterligare minskar möjligheten för återställning.
Väg in tidsgränser för åtgärderna när du väljer intervall för återförsök för att undvika att starta ett efterföljande försök omedelbart (om tidsgränsen t.ex. liknar intervallet för återförsök). Överväg även om du behöver se till att den totala perioden (tidsgränsen plus intervallen för återförsök) hamnar under en viss totaltid. Åtgärder som har ovanligt korta eller mycket långa tidsgränser kan påverka hur lång tid man ska vänta och hur ofta återförsök för åtgärden ska göras.
Använd typen av undantag och alla data som den innehåller, eller felkoder och meddelanden som returneras från tjänsten, för att optimera intervall och antalet återförsök. Vissa undantag eller felkoder (t.ex. HTTP-koden 503 Tjänsten är inte tillgänglig med en rubrik för återförsök i svaret) kan ange hur länge felet kan vara, eller att tjänsten har misslyckats och inte svarar på något efterföljande försök.
Undvik antimönster:
I de allra flesta fall bör du undvika implementeringar som innehåller duplicerade lager av kod för återförsök. Undvik utformningar som omfattar sammanhängande metoder för återförsök, eller som implementerar återförsök i varje skede av en åtgärd som inbegriper en hierarki av begäranden, såvida du inte har särskilda krav som kräver detta. I dessa undantagsfall använder du principer som förhindrar överdrivna antal återförsök och fördröjningstider, och ser till att du förstår konsekvenserna. Om en komponent till exempel gör en begäran till en annan som sedan kommer åt måltjänsten, och du implementerar tre återförsök för båda anropen, så finns totalt nio återförsök mot tjänsten. För flera tjänster och resurser implementeras en inbyggd mekanism för återförsök och du bör undersöka hur du kan inaktivera eller modifiera den om du behöver implementera återförsök på en högre nivå.
Implementera aldrig en oändlig mekanism för återförsök. Den förhindrar troligen att en resurs eller tjänst återhämtar sig från situationer med överbelastning, samt orsaka att begränsningar och nekade anslutningar fortsätter under en längre period. Använd ett begränsat antal återförsök eller implementera ett mönster såsom Kretsbrytare så att tjänsten kan återställas.
Utför aldrig ett omedelbart återförsök mer än en gång.
Undvik att använda ett vanligt intervall för återförsök, särskilt när du har ett stort antal återförsök, när du kommer åt tjänster och resurser i Azure. Den bästa metoden i det här scenariot är en exponentiell backoff-strategi med en kretsbrytarfunktion.
Förhindra att flera instanser av samma klient, eller flera instanser av olika klienter, skickar återförsök vid samma tidpunkt. Om det sannolikt inträffar använder du slumpmässighet i intervallen för återförsök.
Testa din strategi och implementering för återförsök:
Se till att du testar hela din strategiimplementering för återförsök under en så bred uppsättning omständigheter som möjligt, särskilt när både programmet och målresurserna eller -tjänsterna som det använder är under extrem belastning. Om du vill kontrollera beteende under testningen kan du:
Mata in tillfälliga och icke-övergående fel i tjänsten. Skicka till exempel ogiltiga begäranden eller lägg till kod som identifierar testbegäranden och svarar med olika typer av fel. Ett exempel som använder TestApi finns i Fault Injection Testing with TestApi (Felinjektionstestning med TestApi) och Introduction to TestApi – Part 5: Managed Code Fault Injection APIs (Introduktion till TestApi – del 5: hanterade API:er för felinjektion i kod).
Skapa ett utkast av resursen eller tjänsten som returnerar ett antal fel som den verkliga tjänsten kan returnera. Se till att du täcker alla typer av fel som din strategi för återförsök har utformats för att identifiera.
Tvinga tillfälliga fel att inträffa genom att tillfälligt inaktivera eller överbelasta tjänsten om det är en anpassad tjänst som du har skapat och distribuerat (naturligtvis bör du inte försöka överbelasta delade resurser eller delade tjänster i Azure).
Överväg att använda FiddlerCore-biblioteket i dina automatiserade tester för HTTP-baserade API:er om du vill ändra resultatet för HTTP-begäranden. Gör detta antingen genom att lägga till extra tur och retur-tider eller genom att ändra svaret (t.ex HTTP-statuskoden, rubriker, brödtext eller andra faktorer). Detta möjliggör deterministisk testning av en delmängd av felförhållandena, oavsett om det rör sig om tillfälliga fel eller om andra typer av fel. Mer information finns i FiddlerCore. Om du vill ha exempel på hur du använder biblioteket, och särskilt klassen HttpMangler, granskar du källkoden för Azure Storage SDK.
Utför test med hög belastningsfaktor och samtidiga tester för att säkerställa att mekanismen och strategin för återförsök fungerar korrekt under dessa förhållanden och inte har en negativ effekt på driften av klienten eller orsakar problem mellan begäranden.
Hantera principkonfigurationer för återförsök:
En princip för återförsök är en kombination av alla element i din strategi för återförsök. Den definierar identifieringsprocessen som avgör om ett fel troligen är tillfälligt, vilken typ av intervall som ska användas (till exempel regelbundet, exponentiell backoff och slumpmässigt), de faktiska intervallvärdena och hur många gånger som återförsök ska göras.
Återförsök måste implementeras på många ställen, även i ett enkelt program, och i alla lager i mer komplexa program. I stället för att hårdkoda elementen för varje princip på flera platser, bör du överväga att använda en central plats för att lagra alla principer. Du kan till exempel lagra värden som intervall och antal återförsök i konfigurationsfiler för programmet, läsa dem vid körning och programmässigt skapa principerna för återförsök. Detta gör det enklare att hantera inställningarna och att ändra och finjustera värdena för att kunna svara på föränderliga krav och scenarier. Utforma dock systemet så att värdena lagras i stället för att läsa en konfigurationsfil varje gång, och se till att lämpliga standardinställningar används om värdena inte kan hämtas från konfigurationen.
I ett Azure Cloud Services-program kan du överväga att lagra värdena som används för att skapa principer för återförsök vid körning i tjänstekonfigurationsfilen så att de kan ändras utan att programmet måste startas om.
Använd inbyggda strategier eller standardstrategier för återförsök som är tillgängliga i de klient-API:er som du använder, men endast om de är lämpliga för ditt scenario. Dessa strategier är vanligtvis för generell användning. I vissa scenarier kan de räcka för kraven som finns, men i andra scenarier kan de inte erbjuder en fullständig uppsättning alternativ som passar dina krav. Du måste förstå hur inställningarna kommer att påverka ditt program genom att fastslå de lämpligaste värdena.
Logga och spåra tillfälliga och icke-övergående fel:
Inkludera undantagshantering och annan instrumentation som loggar när nya försök görs som en del av din strategi för återförsök. Även om tillfälliga tillfälliga fel och återförsök kan förväntas och inte indikerar något problem, är regelbundna och ökande antal återförsök ofta en indikation på ett problem som kan orsaka fel eller som för närvarande försämrar programmets prestanda och tillgänglighet.
Logga tillfälliga fel som varningsposter i stället för felposter så att övervakningssystem inte identifierar dem som programfel som kan utlösa falsklarm.
Överväg att lagra ett värde i dina loggposter som anger om återförsöken orsakades av begränsning i tjänsten, eller av andra typer av fel såsom anslutningsfel, så att du kan skilja dem åt under analys av data. En ökning av antalet begränsningsfel är ofta ett tecken på ett utformningsfel i programmet eller på ett behov av att byta till en premiumtjänst som erbjuder dedikerad maskinvara.
Överväg att mäta och logga den totala tid det tar för åtgärder som omfattar en mekanism för återförsök. Detta är en bra indikator på den övergripande effekten av tillfälliga fel för svarstider för användare, svarstid för processer och effektiviteten för programmets användningsområden. Logga även antalet försök som uppstod för att förstå de faktorer som bidrog till svarstiden.
Överväg att implementera ett system för telemetridata och övervakning som kan skicka aviseringar när antalet fel och felfrekvensen, det genomsnittliga antalet återförsök eller tiden det tar för hela åtgärder att lyckas ökar.
Hantera åtgärder som kontinuerligt misslyckas:
Det finns fall där åtgärden fortfarande misslyckas vid varje försök, och det är viktigt att tänka på hur du hanterar den situationen:
En strategi för återförsök definierar det maximala antalet gånger som en åtgärd ska provas igen, men den förhindrar inte att programmet upprepar åtgärden igen med samma antal återförsök. Om en tjänst för bearbetning av beställningar till exempel misslyckas med ett oåterkalleligt fel som permanent gör tjänsten otillgänglig så kan strategin för återförsök identifiera en tidsgräns för anslutning och bedöma att det rör sig om ett tillfälligt fel. Koden utför återförsök för åtgärden ett angivet antal gånger och ger sedan upp. Men när en annan kund gör en beställning, kommer åtgärden att försöka genomföras igen – trots att den misslyckas varje gång.
För att förhindra kontinuerliga återförsök för åtgärder som kontinuerligt misslyckas bör du överväga att implementera Kretsbrytarmönstret. Om antalet fel inom ett angivet tidsintervall överstiger tröskelvärdet i det här mönstret returneras begäranden omedelbart till anroparen som fel, utan att det görs försök att komma åt resursen eller tjänsten med fel.
Programmet kan regelbundet testa tjänsten, periodvis och med långa intervall mellan begäranden, för att identifiera när den blir tillgänglig. Ett lämpligt intervall beror på scenariot, till exempel åtgärdens allvarlighetsgrad och typen av tjänst. Det kan röra sig om allt mellan några minuter och flera timmar. Vid den punkt där testet lyckas återupptar programmet normal drift och skickar begäranden till den nyligen återställda tjänsten.
Under tiden kan det vara möjligt att växla över till en annan instans av tjänsten (kanske på ett annat datacenter eller i ett annat program), använda en liknande tjänst som erbjuder kompatibla (kanske enklare) funktioner eller utföra andra åtgärder i hopp om att tjänsten snart blir tillgänglig. Det kan t.ex. vara lämpligt att lagra begäranden för tjänsten i en kö eller datalagring och spela upp dem senare. Annars kan du kanske dirigera om användaren till en annan instans av programmet, sänka programmets prestanda men fortfarande tillhandahålla funktioner på en acceptabel nivå, eller bara returnera ett meddelande till användaren som anger att programmet för närvarande inte är tillgängligt.
Ytterligare överväganden
När du bestämmer dig för värdena för antalet återförsök och återförsöksintervallen för en princip bör du överväga om åtgärden på tjänsten eller resursen är en del av en långvarig åtgärd eller åtgärd i flera steg. Det kan vara svårt eller dyrt att kompensera för alla andra åtgärdssteg som har redan har slutförts när ett misslyckas. I det här fallet kan ett mycket långt intervall och ett stort antal återförsök användas så länge som inte andra åtgärder blockeras genom att begränsade resurser hålls eller är låsta.
Överväg om återförsök för samma åtgärd kan orsaka inkonsekvenser i data. Om vissa delar av en process med flera steg upprepas och åtgärderna inte är idempotenta kan det resultera i en inkonsekvens. En åtgärd som till exempel ökar ett värde producerar ett ogiltigt resultat om den upprepas. Om en åtgärd upprepas som skickar ett meddelande till en kö kan det orsaka en inkonsekvens i meddelandekonsumenten om den inte kan hitta dubblettmeddelanden. Se till att du utformar varje steg som en idempotent åtgärd för att undvika detta. Mer information om idempotens finns i Idempotency patterns (Idempotensmönster).
Överväg omfattningen för de åtgärder som det ska utföras återförsök för. Det kan till exempel vara enklare att implementera kod för återförsök på en nivå som omfattar flera åtgärder och sedan göra återförsök för alla om en misslyckas. Detta kan emellertid resultera i idempotensproblem eller onödiga återställningsåtgärder.
Om du väljer ett omgång för återförsök som omfattar flera åtgärder bör du väga in den totala svarstiden för samtliga åtgärder när du fastställer intervallen för återförsök, när du övervakar spenderad tid och innan du skapar varningar för fel.
Överväg hur din strategi för återförsök kan påverka grannar och andra klienter i ett delat program, eller när du använder delade resurser och tjänster. Aggressiva principer för återförsök kan orsaka att ett större antal tillfälliga fel inträffar för dessa andra användare och för program som delar resurserna och tjänsterna. På samma sätt kan programmet påverkas av de principer för återförsök som implementeras av andra användare av resurserna och tjänsterna. För verksamhetskritiska program kan du välja att använda premiumtjänster som inte delas. Det ger dig mycket större kontroll över belastningen och den efterföljande begränsningen av dessa resurser och tjänster, något som kan bidra till att motivera den extra kostnaden.
Mer information
- Azure service-specific retry guidelines (Azures tjänstspecifika vägledning för återförsök)
- Kretsbrytarmönster
- Mönster för kompenserande transaktion
- Idempotensmönster