Redigera

Dela via


Modernt webbappmönster för .NET

Azure App Service
Azure Front Door
Azure Cache for Redis
.NET

Den här artikeln visar hur du implementerar det moderna webbappsmönstret. Mönstret Modern webbapp definierar hur du ska modernisera webbappar i molnet och introducera en tjänstorienterad arkitektur. Det moderna webbappsmönstret ger förebyggande arkitektur, kod och konfigurationsvägledning som överensstämmer med principerna i Azure Well-Architected Framework och bygger på reliable web app-mönstret.

Varför ska du använda det moderna webbappsmönstret?

Det moderna webbappsmönstret hjälper till att optimera områden med hög efterfrågan i en webbapp. Det ger detaljerad vägledning för att frikoppla dessa områden, vilket möjliggör oberoende skalning för kostnadsoptimering. Med den här metoden kan du allokera dedikerade resurser till kritiska komponenter, vilket förbättrar den övergripande prestandan. Avkoppling av separerbara tjänster kan förbättra tillförlitligheten genom att förhindra att avmattning i en del av appen påverkar andra. Avkoppling möjliggör också versionshantering av enskilda appkomponenter oberoende av varandra.

Så här implementerar du det moderna webbappsmönstret

Den här artikeln innehåller vägledning för arkitektur, kod och konfiguration för att implementera det moderna webbappsmönstret. Använd följande länkar för att navigera till den vägledning du behöver:

  • Arkitekturvägledning: Lär dig hur du modulariserar webbappkomponenter och väljer lämpliga PaaS-lösningar (Plattform som en tjänst).
  • Kodvägledning: Implementera fyra designmönster för att optimera de frikopplade komponenterna: Strangler Fig, köbaserad belastningsutjämning, konkurrerande konsumenter och hälsoslutpunktsövervakningsmönster.
  • Konfigurationsvägledning: Konfigurera autentisering, auktorisering, automatisk skalning och containerisering för de frikopplade komponenterna.

Dricks

GitHub-logotyp Det finns en referensimplementering (exempelapp) för mönstret Modern webbapp. Den representerar sluttillståndet för implementeringen av den moderna webbappen. Det är en webbapp i produktionsklass som innehåller alla kod-, arkitektur- och konfigurationsuppdateringar som beskrivs i den här artikeln. Distribuera och använd referensimplementeringen för att vägleda implementeringen av det moderna webbappsmönstret.

Vägledning för arkitektur

Det moderna webbappsmönstret bygger på mönstret Reliable Web App. Det krävs några extra arkitektoniska komponenter för att implementera. Du behöver en meddelandekö, containerplattform, frikopplat tjänstdatalager och ett containerregister (se bild 1).

Diagram som visar baslinjearkitekturen för det moderna webbappsmönstret.Bild 1. Viktiga arkitektoniska element i det moderna webbappsmönstret.

För ett högre servicenivåmål (SLO) kan du lägga till en andra region i webbappens arkitektur. En andra region kräver att du konfigurerar lastbalanseraren för att dirigera trafik till den andra regionen för att stödja antingen en aktiv-aktiv eller aktiv-passiv konfiguration. Använd en nätverkstopologi för nav och eker för att centralisera och dela resurser, till exempel en nätverksbrandvägg. Få åtkomst till containerlagringsplatsen via det virtuella hubbnätverket. Om du har virtuella datorer lägger du till en skyddsvärd i det virtuella hubbnätverket för att hantera dem på ett säkert sätt (se bild 2).

Diagram som visar modern webbappsmönsterarkitektur med den andra regionen och nätverkstopologin hub-and-spoke.Bild 2. Modern webbapps mönsterarkitektur med den andra region- och nav-och-eker-nätverkstopologin.

Frikoppla arkitektur

För att implementera det moderna webbappsmönstret måste du frikoppla den befintliga webbapparkitekturen. Att frikoppla arkitekturen innebär att dela upp ett monolitiskt program i mindre, oberoende tjänster, som var och en ansvarar för en specifik funktion eller funktion. Den här processen innebär att utvärdera den aktuella webbappen, ändra arkitekturen och slutligen extrahera webbappkoden till en containerplattform. Målet är att systematiskt identifiera och extrahera programtjänster som drar störst nytta av att vara frikopplade. Följ dessa rekommendationer för att frikoppla arkitekturen:

  • Identifiera tjänstgränser. Använd domändrivna designprinciper för att identifiera avgränsade kontexter i ditt monolitiska program. Varje begränsad kontext representerar en logisk gräns och kan vara en kandidat för en separat tjänst. Tjänster som representerar distinkta affärsfunktioner och har färre beroenden är bra kandidater för avkoppling.

  • Utvärdera tjänstfördelar. Fokusera på tjänster som drar mest nytta av oberoende skalning. Frikoppling av dessa tjänster och konvertering av bearbetningsuppgifter från synkrona till asynkrona åtgärder möjliggör effektivare resurshantering, stöder oberoende distributioner och minskar risken för att påverka andra delar av programmet under uppdateringar eller ändringar. Du kan till exempel separera orderutcheckningen från orderbearbetning.

  • Utvärdera teknisk genomförbarhet. Granska den aktuella arkitekturen för att identifiera tekniska begränsningar och beroenden som kan påverka avkopplingsprocessen. Planera hur data hanteras och delas mellan tjänster. Frikopplade tjänster bör hantera sina egna data och minimera direkt databasåtkomst över tjänstgränser.

  • Distribuera Azure-tjänster. Välj och distribuera de Azure-tjänster som du behöver för att stödja den webbapptjänst som du avsåg att extrahera. Använd följande avsnitt Välj rätt Azure-tjänster för vägledning.

  • Frikoppla webbapptjänster. Definiera tydliga gränssnitt och API:er för de nyligen extraherade webbapptjänsterna för att interagera med andra delar av systemet. Utforma en datahanteringsstrategi som gör att varje tjänst kan hantera sina egna data samtidigt som konsekvens och integritet säkerställs. Specifika implementeringsstrategier och designmönster som ska användas under den här extraheringsprocessen finns i avsnittet Kodvägledning .

  • Använd oberoende lagring för frikopplade tjänster. Varje frikopplad tjänst bör ha ett eget isolerat datalager för att underlätta oberoende versionshantering, distribution, skalbarhet och upprätthålla dataintegritet. Referensimplementeringen separerar till exempel biljettrenderingstjänsten från webb-API:et och eliminerar behovet av att tjänsten får åtkomst till API:ets databas. I stället kommunicerar tjänsten URL:en där biljettbilder genererades tillbaka till webb-API:et via ett Azure Service Bus-meddelande, och API:et bevarar sökvägen till databasen.

  • Implementera separata distributionspipelines för varje frikopplad tjänst. Med separata distributionspipelines kan varje tjänst uppdateras i sin egen takt. Om olika team eller organisationer inom ditt företag äger olika tjänster ger separata distributionspipelines varje team kontroll över sina egna distributioner. Använd verktyg för kontinuerlig integrering och kontinuerlig leverans (CI/CD) som Jenkins, GitHub Actions eller Azure Pipelines för att konfigurera dessa pipelines.

  • Ändra säkerhetskontroller. Se till att dina säkerhetskontroller uppdateras för att ta hänsyn till den nya arkitekturen, inklusive brandväggsregler och åtkomstkontroller.

Välj rätt Azure-tjänster

För varje Azure-tjänst i din arkitektur läser du relevant Azure-tjänstguide i Well-Architected Framework. För det moderna webbappsmönstret behöver du ett meddelandesystem som stöder asynkrona meddelanden, en programplattform som stöder containerisering och en lagringsplats för containeravbildningar.

  • Välj en meddelandekö. En meddelandekö är en viktig del av tjänstorienterade arkitekturer. Den frikopplar meddelandeavsändare och mottagare för att aktivera asynkrona meddelanden. Använd vägledningen för att välja en Azure-meddelandetjänst för att välja ett Azure-meddelandesystem som stöder dina designbehov. Azure har tre meddelandetjänster: Azure Event Grid, Azure Event Hubs och Service Bus. Börja med Service Bus som standardval och använd de andra två alternativen om Service Bus inte uppfyller dina behov.

    Tjänst Användningsfall
    Service Bus Välj Service Bus för tillförlitlig, ordnad och möjligen transaktionell leverans av värdefulla meddelanden i företagsprogram.
    Event Grid Välj Event Grid när du behöver hantera ett stort antal diskreta händelser effektivt. Event Grid är skalbart för händelsedrivna program där många små, oberoende händelser (till exempel resurstillståndsändringar) måste dirigeras till prenumeranter i en modell med låg svarstid och publiceringsprenumerering.
    Event Hubs Välj Event Hubs för massiv datainmatning med högt dataflöde, till exempel telemetri, loggar eller realtidsanalys. Event Hubs är optimerat för strömningsscenarier där massdata måste matas in och bearbetas kontinuerligt.
  • Implementera en containertjänst. För de delar av programmet som du vill behålla behöver du en programplattform som stöder containrar. Använd vägledningen välj en Azure-containertjänst för att fatta ditt beslut. Azure har tre huvudsakliga containertjänster: Azure Container Apps, Azure Kubernetes Service (AKS) och Azure App Service. Börja med Container Apps som standardval och använd de andra två alternativen om Container Apps inte uppfyller dina behov.

    Tjänst Användningsfall
    Container Apps Välj Container Apps om du behöver en serverlös plattform som automatiskt skalar och hanterar containrar i händelsedrivna program.
    AKS Välj AKS om du behöver detaljerad kontroll över Kubernetes-konfigurationer och avancerade funktioner för skalning, nätverk och säkerhet.
    Web Apps för container Välj Webbapp för containrar i App Service för den enklaste PaaS-upplevelsen.
  • Implementera en containerlagringsplats. När du använder en containerbaserad beräkningstjänst måste du ha en lagringsplats för att lagra containeravbildningarna. Du kan använda ett offentligt containerregister som Docker Hub eller ett hanterat register som Azure Container Registry. Använd vägledningen Introduktion till containerregister i Azure för att fatta ditt beslut.

Kodvägledning

För att kunna frikoppla och extrahera en oberoende tjänst måste du uppdatera webbappens kod med följande designmönster: Strangler Fig-mönstret, mönster för köbaserad belastningsutjämning, konkurrerande konsumentmönster, hälsoslutpunktsövervakningsmönster och återförsöksmönster.

Diagram som visar designmönstrens roll i modern webbapps mönsterarkitektur.Bild 3. Designmönstrens roll.

  1. Strangler Fig-mönster: Strangler Fig-mönstret migrerar inkrementellt funktioner från ett monolitiskt program till den frikopplade tjänsten. Implementera det här mönstret i huvudwebbappen för att gradvis migrera funktioner till oberoende tjänster genom att dirigera trafik baserat på slutpunkter.

  2. Mönster för köbaserad belastningsutjämning: Mönstret Köbaserad belastningsutjämning hanterar flödet av meddelanden mellan producenten och konsumenten med hjälp av en kö som buffert. Implementera det här mönstret på producentdelen av den frikopplade tjänsten för att hantera meddelandeflödet asynkront med hjälp av en kö.

  3. Mönster för konkurrerande konsumenter: Med mönstret Konkurrerande konsumenter kan flera instanser av den frikopplade tjänsten läsas oberoende av samma meddelandekö och konkurrera om att bearbeta meddelanden. Implementera det här mönstret i den frikopplade tjänsten för att distribuera uppgifter över flera instanser.

  4. Mönster för hälsoslutpunktsövervakning: Mönstret Hälsoslutpunktsövervakning visar slutpunkter för övervakning av status och hälsa för olika delar av webbappen. (4a) Implementera det här mönstret i huvudwebbappen. (4b) Implementera den också i den frikopplade tjänsten för att spåra slutpunkternas hälsotillstånd.

  5. Återförsöksmönster: Återförsöksmönstret hanterar tillfälliga fel genom att försöka utföra åtgärder som kan misslyckas tillfälligt. (5a) Implementera det här mönstret för alla utgående anrop till andra Azure-tjänster i huvudwebbappen, till exempel samtal till meddelandekö och privata slutpunkter. (5b) Implementera också detta mönster i den frikopplade tjänsten för att hantera tillfälliga fel i anrop till de privata slutpunkterna.

Varje designmönster ger fördelar som överensstämmer med en eller flera pelare i det väldefinierade ramverket (se följande tabell).

Designmönster Implementeringsplats Tillförlitlighet (RE) Säkerhet (SE) Kostnadsoptimering (CO) Operational Excellence (OE) Prestandaeffektivitet (PE) Stöd för väldefinierade ramverksprinciper
Strangler Fig-mönster Huvudwebbapp RE:08
CO:07
CO:08
OE:06
OE:11
Mönster för köbaserad belastningsutjämning Producent av frikopplad tjänst RE:06
RE:07
CO:12
PE:05
Mönster för konkurrerande konsumenter Frikopplad tjänst RE:05
RE:07
CO:05
CO:07
PE:05
PE:07
Hälsoslutpunktsövervakningsmönster Huvudwebbapp & frikopplad tjänst RE:07
RE:10
OE:07
PE:05
Mönster för återförsök Huvudwebbapp & frikopplad tjänst RE:07

Implementera Strangler Fig-mönstret

Använd Strangler Fig-mönstret för att gradvis migrera funktioner från den monolitiska kodbasen till nya oberoende tjänster. Extrahera nya tjänster från den befintliga monolitiska kodbasen och modernisera långsamt kritiska delar av webbappen. Följ dessa rekommendationer för att implementera Strangler Fig-mönstret:

  • Konfigurera ett routningslager. I den monolitiska webbappens kodbas implementerar du ett routningslager som dirigerar trafik baserat på slutpunkter. Använd anpassad routningslogik efter behov för att hantera specifika affärsregler för att dirigera trafik. Om du till exempel har en /users slutpunkt i din monolitiska app och du har flyttat den funktionen till den frikopplade tjänsten dirigerar routningsskiktet alla begäranden till /users den nya tjänsten.

  • Hantera funktionsdistribution. Använd .NET-funktionshanteringsbibliotek för att implementera funktionsflaggor och stegvis distribution för att gradvis distribuera de frikopplade tjänsterna. Den befintliga monolitiska appdirigeringen bör styra hur många begäranden de frikopplade tjänsterna tar emot. Börja med en liten procentandel begäranden och öka användningen över tid när du får förtroende för dess stabilitet och prestanda. Referensimplementeringen extraherar till exempel funktionen för biljettrendering till en fristående tjänst, som gradvis kan introduceras för att hantera en större del av begäranden om biljettåtergivning. När den nya tjänsten visar sin tillförlitlighet och prestanda kan den så småningom ta över hela biljettrenderingsfunktionen från monoliten och slutföra övergången.

  • Använd en fasadtjänst (om det behövs). En fasadtjänst är användbar när en enskild begäran behöver interagera med flera tjänster eller när du vill dölja komplexiteten i det underliggande systemet från klienten. Men om den frikopplade tjänsten inte har några offentliga API:er kanske en fasadtjänst inte är nödvändig. I den monolitiska webbappens kodbas implementerar du en fasadtjänst för att dirigera begäranden till rätt serverdel (monolit eller mikrotjänst). I den nya frikopplade tjänsten ser du till att den nya tjänsten kan hantera begäranden oberoende av varandra när den nås via fasaden.

Implementera mönstret för köbaserad belastningsutjämning

Implementera mönstret Köbaserad belastningsutjämning på producentdelen av den frikopplade tjänsten för att asynkront hantera uppgifter som inte behöver omedelbara svar. Det här mönstret förbättrar systemets övergripande svarstider och skalbarhet med hjälp av en kö för att hantera arbetsbelastningsdistribution. Det gör att den frikopplade tjänsten kan bearbeta begäranden med en konsekvent hastighet. Följ dessa rekommendationer för att effektivt implementera det här mönstret:

  • Använd icke-blockerande meddelandeköer. Se till att processen som skickar meddelanden till kön inte blockerar andra processer i väntan på att den frikopplade tjänsten ska hantera meddelanden i kön. Om processen kräver resultatet av den frikopplade tjänståtgärden kan du använda ett annat sätt att hantera situationen i väntan på att den köade åtgärden ska slutföras. Referensimplementeringen använder till exempel Service Bus och nyckelordet await med messageSender.PublishAsync() för att asynkront publicera meddelanden i kön utan att blockera tråden som kör den här koden:

    // Asynchronously publish a message without blocking the calling thread
    await messageSender.PublishAsync(new TicketRenderRequestMessage(Guid.NewGuid(), ticket, null, DateTime.Now), CancellationToken.None);
    

    Den här metoden säkerställer att huvudprogrammet förblir responsivt och kan hantera andra uppgifter samtidigt, medan den frikopplade tjänsten bearbetar de köade begärandena med en hanterbar hastighet.

  • Implementera återförsök och borttagning av meddelanden. Implementera en mekanism för att försöka bearbeta köade meddelanden igen som inte kan bearbetas korrekt. Om felen kvarstår bör dessa meddelanden tas bort från kön. Service Bus har till exempel inbyggda köfunktioner för återförsök och obeställbara meddelanden.

  • Konfigurera idempotent meddelandebearbetning. Logiken som bearbetar meddelanden från kön måste vara idempotent för att hantera fall där ett meddelande kan bearbetas mer än en gång. Referensimplementeringen använder ServiceBusClient.CreateProcessor till exempel med AutoCompleteMessages = true och ReceiveMode = ServiceBusReceiveMode.PeekLock för att säkerställa att meddelanden endast bearbetas en gång och kan bearbetas på nytt vid fel (se följande kod).

    // Create a processor for idempotent message processing
    var processor = serviceBusClient.CreateProcessor(path, new ServiceBusProcessorOptions
    {
        // Allow the messages to be auto-completed
        // if processing finishes without failure.
        AutoCompleteMessages = true,
    
        // PeekLock mode provides reliability in that unsettled messages
        // will be redelivered on failure.
        ReceiveMode = ServiceBusReceiveMode.PeekLock,
    
        // Containerized processors can scale at the container level
        // and need not scale via the processor options.
        MaxConcurrentCalls = 1,
        PrefetchCount = 0
    });
    
  • Hantera ändringar i upplevelsen. Asynkron bearbetning kan leda till att aktiviteter inte slutförs omedelbart. Användarna bör informeras när deras uppgift fortfarande bearbetas för att ställa in korrekta förväntningar och undvika förvirring. Använd visuella tips eller meddelanden för att indikera att en uppgift pågår. Ge användarna möjlighet att ta emot meddelanden när deras uppgift är klar, till exempel ett e-postmeddelande eller ett push-meddelande.

Implementera mönstret Konkurrerande konsumenter

Implementera mönstret Konkurrerande konsumenter i de frikopplade tjänsterna för att hantera inkommande uppgifter från meddelandekön. Det här mönstret omfattar distribution av uppgifter över flera instanser av frikopplade tjänster. Dessa tjänster bearbetar meddelanden från kön, förbättrar belastningsutjämningen och ökar systemets kapacitet för att hantera samtidiga begäranden. Mönstret Konkurrerande konsumenter är effektivt när:

  • Sekvensen för meddelandebearbetning är inte avgörande.
  • Kön påverkas inte av felaktiga meddelanden.
  • Bearbetningsåtgärden är idempotent, vilket innebär att den kan tillämpas flera gånger utan att ändra resultatet utöver det ursprungliga programmet.

Följ dessa rekommendationer för att implementera mönstret Konkurrerande konsumenter:

  • Hantera samtidiga meddelanden. När du tar emot meddelanden från en kö kontrollerar du att systemet är utformat för att hantera flera meddelanden samtidigt. Ange maximalt antal samtidiga anrop till 1 så att en separat konsument hanterar varje meddelande.

  • Inaktivera prefetching. Inaktivera förinläsning av meddelanden så att användarna bara hämtar meddelanden när de är redo.

  • Använd tillförlitliga lägen för meddelandebearbetning. Använd ett tillförlitligt bearbetningsläge, till exempel PeekLock (eller motsvarande), som automatiskt försöker skicka meddelanden som inte bearbetas igen. Det här läget förbättrar tillförlitligheten jämfört med metoder som tas bort först. Om en arbetare inte kan hantera ett meddelande måste en annan kunna bearbeta det utan fel, även om meddelandet bearbetas flera gånger.

  • Implementera felhantering. Dirigera felaktiga eller ej bearbetade meddelanden till en separat kö med obeställbara meddelanden. Den här designen förhindrar repetitiv bearbetning. Du kan till exempel fånga undantag under meddelandebearbetningen och flytta det problematiska meddelandet till den separata kön.

  • Hantera meddelanden som inte är i ordning. Utforma konsumenter för att bearbeta meddelanden som kommer ur sekvens. Flera parallella konsumenter innebär att de kan bearbeta meddelanden ur ordning.

  • Skala baserat på kölängd. Tjänster som använder meddelanden från en kö ska skalas automatiskt baserat på kölängd. Skalbaserad autoskalning möjliggör effektiv bearbetning av toppar av inkommande meddelanden.

  • Använd en meddelandesvarskö. Om systemet kräver meddelanden för bearbetning efter meddelandet konfigurerar du en dedikerad svars- eller svarskö. Den här konfigurationen delar upp operativa meddelanden från meddelandeprocesser.

  • Använd tillståndslösa tjänster. Överväg att använda tillståndslösa tjänster för att bearbeta begäranden från en kö. Det möjliggör enkel skalning och effektiv resursanvändning.

  • Konfigurera loggning. Integrera loggning och specifik undantagshantering i arbetsflödet för meddelandebearbetning. Fokusera på att samla in serialiseringsfel och dirigera dessa problematiska meddelanden till en mekanism med obeställbara bokstäver. Dessa loggar ger värdefulla insikter för felsökning.

Referensimplementeringen använder till exempel mönstret Konkurrerande konsumenter på en tillståndslös tjänst som körs i Container Apps för att bearbeta begäranden om biljettåtergivning från en Service Bus-kö. Den konfigurerar en köprocessor med:

  • AutoCompleteMessages: Slutför automatiskt meddelanden om de bearbetas utan fel.
  • ReceiveMode: Använder PeekLock-läge och redelivers-meddelanden om de inte är avgjorda.
  • MaxConcurrentCalls: Ange till 1 för att hantera ett meddelande i taget.
  • PrefetchCount: Ange till 0 för att undvika förinställda meddelanden.

Processorn loggar information om meddelandebearbetning, vilket underlättar felsökning och övervakning. Den samlar in deserialiseringsfel och dirigerar ogiltiga meddelanden till en kö med obeställbara meddelanden, vilket förhindrar upprepad bearbetning av felaktiga meddelanden. Tjänsten skalas på containernivå, vilket möjliggör effektiv hantering av meddelandetoppar baserat på kölängd.

// Create a processor for the given queue that will process
// incoming messages.
var processor = serviceBusClient.CreateProcessor(path, new ServiceBusProcessorOptions
{
    // Allow the messages to be auto-completed
    // if processing finishes without failure.
    AutoCompleteMessages = true,
    // PeekLock mode provides reliability in that unsettled messages
    // are redelivered on failure.
    ReceiveMode = ServiceBusReceiveMode.PeekLock,
    // Containerized processors can scale at the container level
    // and need not scale via the processor options.
    MaxConcurrentCalls = 1,
    PrefetchCount = 0
});

// Called for each message received by the processor.
processor.ProcessMessageAsync += async args =>
{
    logger.LogInformation("Processing message {MessageId} from {ServiceBusNamespace}/{Path}", args.Message.MessageId, args.FullyQualifiedNamespace, args.EntityPath);
    // Unhandled exceptions in the handler will be caught by
    // the processor and result in abandoning and dead-lettering the message.
    try
    {
        var message = args.Message.Body.ToObjectFromJson<T>();
        await messageHandler(message, args.CancellationToken);
        logger.LogInformation("Successfully processed message {MessageId} from {ServiceBusNamespace}/{Path}",args.Message.MessageId, args.FullyQualifiedNamespace, args.EntityPath);
    }
    catch (JsonException)
    {
        logger.LogError("Invalid message body; could not be deserialized to {Type}", typeof(T));
        await args.DeadLetterMessageAsync(args.Message, $"Invalid message body; could not be deserialized to {typeof(T)}",cancellationToken: args.CancellationToken);
    }
};

Implementera hälsoslutpunktsövervakningsmönstret

Implementera hälsoslutpunktsövervakningsmönstret i huvudappkoden och den frikopplade tjänstkoden för att spåra hälsotillståndet för programslutpunkter. Orkestrerare som AKS eller Container Apps kan avsöka dessa slutpunkter för att verifiera tjänstens hälsa och starta om instanser med feltillstånd. ASP.NET Core-appar kan lägga till dedikerade hälsokontrollmellanprogram för att effektivt hantera slutpunktshälsodata och nyckelberoenden. Följ dessa rekommendationer för att implementera hälsoslutpunktsövervakningsmönstret:

  • Implementera hälsokontroller. Använd ASP.NET Core-hälsokontroller mellanprogram för att tillhandahålla slutpunkter för hälsokontroll.

  • Verifiera beroenden. Kontrollera att hälsokontrollerna verifierar tillgängligheten för viktiga beroenden, till exempel databasen, lagringen och meddelandesystemet. Icke-Microsoft-paketet AspNetCore.Diagnostics.HealthChecks kan implementera beroendekontroller för hälsokontroller för många vanliga appberoenden.

    Referensimplementeringen använder till exempel ASP.NET Kärnhälsokontroll mellanprogram för att exponera slutpunkter för hälsokontroll med hjälp av AddHealthChecks() -metoden för builder.Services objektet. Koden verifierar tillgängligheten för nyckelberoenden, Azure Blob Storage och Service Bus-kön med AddAzureBlobStorage() metoderna och AddAzureServiceBusQueue() som ingår i AspNetCore.Diagnostics.HealthChecks paketet. Container Apps tillåter konfiguration av hälsoavsökningar som övervakas för att mäta om appar är felfria eller i behov av återvinning.

    // Add health checks, including health checks for Azure services
    // that are used by this service.
    // The Blob Storage and Service Bus health checks are provided by
    // AspNetCore.Diagnostics.HealthChecks
    // (a popular open source project) rather than by Microsoft. 
    // https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks
    builder.Services.AddHealthChecks()
    .AddAzureBlobStorage(options =>
    {
        // AddAzureBlobStorage will use the BlobServiceClient registered in DI
        // We just need to specify the container name
        options.ContainerName = builder.Configuration.GetRequiredConfigurationValue("App:StorageAccount:Container");
    })
    .AddAzureServiceBusQueue(
        builder.Configuration.GetRequiredConfigurationValue("App:ServiceBus:Host"),
        builder.Configuration.GetRequiredConfigurationValue("App:ServiceBus:RenderRequestQueueName"),
        azureCredentials);
    
    // Further app configuration omitted for brevity
    app.MapHealthChecks("/health");
    
  • Konfigurera Azure-resurser. Konfigurera Azure-resurserna så att de använder appens url:er för hälsokontroll för att bekräfta liveness och beredskap. Referensimplementeringen använder till exempel Bicep för att konfigurera url:erna för hälsokontroll för att bekräfta Azure-resursens livskraft och beredskap. En liveness-avsökning som når /health slutpunkten var 10:e sekund efter en inledande fördröjning på 2 sekunder.

    probes: [
      {
        type: 'liveness'
        httpGet: {
          path: '/health'
          port: 8080
        }
        initialDelaySeconds: 2
        periodSeconds: 10
      }
    ]
    

Implementera återförsöksmönstret

Med återförsöksmönstret kan program återställas från tillfälliga fel. Återförsöksmönstret är centralt för mönstret Reliable Web App, så webbappen bör redan använda mönstret Försök igen. Använd återförsöksmönstret för begäranden till meddelandesystemen och begäranden som utfärdats av de frikopplade tjänster som du extraherar från webbappen. Följ dessa rekommendationer för att implementera återförsöksmönstret:

  • Konfigurera återförsöksalternativ. När du integrerar med en meddelandekö måste du konfigurera klienten som ansvarar för interaktioner med kön med lämpliga återförsöksinställningar. Ange parametrar som det maximala antalet återförsök, fördröjning mellan återförsök och maximal fördröjning.

  • Använd exponentiell backoff. Implementera en exponentiell backoff-strategi för återförsök. Det innebär att öka tiden mellan varje nytt försök exponentiellt, vilket minskar belastningen på systemet under perioder med höga felfrekvenser.

  • Använd SDK:s återförsöksfunktioner. För tjänster med specialiserade SDK:er, till exempel Service Bus eller Blob Storage, använder du de inbyggda mekanismerna för återförsök. De inbyggda återförsöksmekanismerna är optimerade för tjänstens vanliga användningsfall och kan hantera återförsök mer effektivt med mindre konfiguration som krävs från din sida. Referensimplementeringen använder till exempel de inbyggda återförsöksfunktionerna i Service Bus SDK (ServiceBusClient och ServiceBusRetryOptions). Objektet ServiceBusRetryOptions hämtar inställningar från MessageBusOptions för att konfigurera omförsöksinställningar som MaxRetries, Delay, MaxDelay och TryTimeout.

    // ServiceBusClient is thread-safe and can be reused for the lifetime
    // of the application.
    services.AddSingleton(sp =>
    {
        var options = sp.GetRequiredService<IOptions<MessageBusOptions>>().Value;
        var clientOptions = new ServiceBusClientOptions
        {
            RetryOptions = new ServiceBusRetryOptions
            {
                Mode = ServiceBusRetryMode.Exponential,
                MaxRetries = options.MaxRetries,
                Delay = TimeSpan.FromSeconds(options.BaseDelaySecondsBetweenRetries),
                MaxDelay = TimeSpan.FromSeconds(options.MaxDelaySeconds),
                TryTimeout = TimeSpan.FromSeconds(options.TryTimeoutSeconds)
            }
        };
        return new ServiceBusClient(options.Host, azureCredential ?? new DefaultAzureCredential(), clientOptions);
    });
    
  • Implementera standardbibliotek för motståndskraft för HTTP-klienter. För HTTP-kommunikation integrerar du ett standardbibliotek för motståndskraft, till exempel Polly eller Microsoft.Extensions.Http.Resilience. Dessa bibliotek erbjuder omfattande mekanismer för återförsök som är avgörande för att hantera kommunikation med externa webbtjänster.

  • Hantera meddelandelåsning. För meddelandebaserade system implementerar du strategier för meddelandehantering som stöder återförsök utan dataförlust, till exempel att använda "peek-lock"-lägen där det är tillgängligt. Kontrollera att misslyckade meddelanden görs om effektivt och flyttas till en kö med obeställbara meddelanden efter upprepade fel.

Implementera distribuerad spårning

I takt med att programmen blir mer tjänstorienterade och deras komponenter frikopplas är det viktigt att övervaka körningsflödet mellan tjänster. Det moderna webbappsmönstret använder Application Insights och Azure Monitor för att få insyn i programmets hälsa och prestanda via OpenTelemetry-API:er, som stöder distribuerad spårning.

Distribuerad spårning spårar en användarbegäran när den passerar flera tjänster. När en begäran tas emot taggas den med en spårningsidentifierare som skickas till andra komponenter via HTTP-huvuden och Service Bus-egenskaper under beroenden. Spårningar och loggar innehåller sedan både spårningsidentifieraren och en aktivitetsidentifierare (eller span-ID), som motsvarar den specifika komponenten och dess överordnade aktivitet. Övervakningsverktyg som Application Insights använder det för att visa ett träd med aktiviteter och loggar över olika tjänster, avgörande för övervakning av distribuerade program.

  • Installera OpenTelemetry-bibliotek. Använd instrumentationsbibliotek för att aktivera spårning och mått från vanliga komponenter. Lägg till anpassad instrumentation med System.Diagnostics.ActivitySource och System.Diagnostics.Activity om det behövs. Använd exportbibliotek för att lyssna efter OpenTelemetry-diagnostik och registrera dem i beständiga arkiv. Använd befintliga exportörer eller skapa egna med System.Diagnostics.ActivityListener.

  • Konfigurera OpenTelemetry. Använd Azure Monitor-distributionen av OpenTelemetry (Azure.Monitor.OpenTelemetry.AspNetCore). Se till att den exporterar diagnostik till Application Insights och innehåller inbyggda instrumentation för vanliga mått, spårningar, loggar och undantag från .NET-körningen och ASP.NET Core. Inkludera andra OpenTelemetry-instrumentationspaket för SQL-, Redis- och Azure SDK-klienter.

  • Övervaka och analysera. När du har konfigurerat kontrollerar du att loggar, spårningar, mått och undantag samlas in och skickas till Application Insights. Kontrollera att spårnings-, aktivitets- och överordnad aktivitetsidentifierare ingår, så att Application Insights kan ge spårningssynlighet från slutpunkt till slutpunkt över HTTP- och Service Bus-gränser. Använd den här konfigurationen för att övervaka och analysera programmets aktiviteter på ett effektivt sätt mellan tjänster.

Exemplet modern webbapp använder Azure Monitor-distributionen av OpenTelemetry (Azure.Monitor.OpenTelemetry.AspNetCore). Fler instrumentationspaket används för SQL-, Redis- och Azure SDK-klienter. OpenTelemetry konfigureras i den moderna webbappens exempeltjänst för biljettåtergivning så här:

builder.Logging.AddOpenTelemetry(o => 
{ 
    o.IncludeFormattedMessage = true; 
    o.IncludeScopes = true; 
}); 

builder.Services.AddOpenTelemetry() 
    .UseAzureMonitor(o => o.ConnectionString = appInsightsConnectionString) 
    .WithMetrics(metrics => 
    { 
        metrics.AddAspNetCoreInstrumentation() 
                .AddHttpClientInstrumentation() 
                .AddRuntimeInstrumentation(); 
    }) 
    .WithTracing(tracing => 
    { 
        tracing.AddAspNetCoreInstrumentation() 
                .AddHttpClientInstrumentation() 
                .AddSource("Azure.*"); 
    }); 

Metoden builder.Logging.AddOpenTelemetry dirigerar all loggning genom OpenTelemetry, vilket säkerställer konsekvent spårning och loggning i hela programmet. Genom att registrera OpenTelemetry-tjänster med builder.Services.AddOpenTelemetrykonfigureras programmet för att samla in och exportera diagnostik, som sedan skickas till Application Insights via UseAzureMonitor. Dessutom konfigureras klientinstrumentation för komponenter som Service Bus- och HTTP-klienter via WithMetrics och WithTracing, vilket aktiverar automatiska mått och spårningsinsamling utan att kräva ändringar i den befintliga klientanvändningen, endast en uppdatering av konfigurationen.

Konfigurationsvägledning

Följande avsnitt innehåller vägledning om hur du implementerar konfigurationsuppdateringarna. Varje avsnitt överensstämmer med en eller flera pelare i det välarkitekterade ramverket.

Konfiguration Tillförlitlighet (RE) Säkerhet (SE) Kostnadsoptimering (CO) Operational Excellence (OE) Prestandaeffektivitet (PE) Stöd för väldefinierade ramverksprinciper
Konfigurera autentisering och auktorisering SE:05
OE:10
Implementera oberoende autoskalning RE:06
CO:12
PE:05
Distribution av containertjänster CO:13
PE:09
PE:03

Konfigurera autentisering och auktorisering

Följ dessa rekommendationer för att konfigurera autentisering och auktorisering för alla nya Azure-tjänster (arbetsbelastningsidentiteter) som du lägger till i webbappen:

  • Använd hanterade identiteter för varje ny tjänst. Varje oberoende tjänst ska ha en egen identitet och använda hanterade identiteter för tjänst-till-tjänst-autentisering. Hanterade identiteter eliminerar behovet av att hantera autentiseringsuppgifter i koden och minska risken för läckage av autentiseringsuppgifter. De hjälper dig att undvika att placera känslig information som anslutningssträng i koden eller konfigurationsfilerna.

  • Bevilja minst behörighet till varje ny tjänst. Tilldela endast nödvändiga behörigheter till varje ny tjänstidentitet. Om en identitet till exempel bara behöver skickas till ett containerregister ska du inte ge den pull-behörigheter. Granska dessa behörigheter regelbundet och justera efter behov. Använd olika identiteter för olika roller, till exempel distribution och program. Detta begränsar den potentiella skadan om en identitet komprometteras.

  • Anta infrastruktur som kod (IaC). Använd Bicep eller liknande IaC-verktyg för att definiera och hantera dina molnresurser. IaC säkerställer konsekvent tillämpning av säkerhetskonfigurationer i dina distributioner och gör att du kan versionskontroll din infrastrukturkonfiguration.

Följ dessa rekommendationer för att konfigurera autentisering och auktorisering för användare (användaridentiteter):

  • Ge användare minst behörighet. Precis som med tjänster ser du till att användarna endast får de behörigheter de behöver för att utföra sina uppgifter. Granska och justera dessa behörigheter regelbundet.

  • Utför regelbundna säkerhetsgranskningar. Granska och granska säkerhetskonfigurationen regelbundet. Leta efter eventuella felkonfigurationer eller onödiga behörigheter och åtgärda dem omedelbart.

Referensimplementeringen använder IaC för att tilldela hanterade identiteter till tillagda tjänster och specifika roller för varje identitet. Den definierar roller och behörigheter för distribution (containerRegistryPushRoleId), programägare (containerRegistryPushRoleId) och Container Apps-program () (containerRegistryPullRoleIdse följande kod).

roleAssignments: \[
    {
    principalId: deploymentSettings.principalId
    principalType: deploymentSettings.principalType
    roleDefinitionIdOrName: containerRegistryPushRoleId
    }
    {
    principalId: ownerManagedIdentity.outputs.principal_id
    principalType: 'ServicePrincipal'
    roleDefinitionIdOrName: containerRegistryPushRoleId
    }
    {
    principalId: appManagedIdentity.outputs.principal_id
    principalType: 'ServicePrincipal'
    roleDefinitionIdOrName: containerRegistryPullRoleId
    }
\]

Referensimplementeringen tilldelar den hanterade identiteten som den nya Container Apps-identiteten vid distributionen (se följande kod).

module renderingServiceContainerApp 'br/public:avm/res/app/container-app:0.1.0' = {
  name: 'application-rendering-service-container-app'
  scope: resourceGroup()
  params: {
    // Other parameters omitted for brevity
    managedIdentities: {
      userAssignedResourceIds: [
        managedIdentity.id
      ]
    }
  }
}

Konfigurera oberoende autoskalning

Det moderna webbappsmönstret börjar bryta upp den monolitiska arkitekturen och introducerar tjänstkoppling. När du frikopplar en webbappsarkitektur kan du skala de frikopplade tjänsterna oberoende av varandra. Genom att skala Azure-tjänsterna för att stödja en oberoende webbapptjänst, i stället för en hel webbapp, optimeras skalningskostnaderna samtidigt som kraven uppfylls. Följ dessa rekommendationer för automatisk skalning av containrar:

  • Använd tillståndslösa tjänster. Se till att dina tjänster är tillståndslösa. Om ditt .NET-program innehåller sessionstillstånd under processen kan du externalisera det till ett distribuerat cacheminne som Redis eller en databas som SQL Server.

  • Konfigurera regler för automatisk skalning. Använd konfigurationerna för automatisk skalning som ger den mest kostnadseffektiva kontrollen över dina tjänster. För containerbaserade tjänster ger händelsebaserad skalning, till exempel Kubernetes Event-Driven Autoscaler (KEDA), ofta detaljerad kontroll, så att du kan skala baserat på händelsemått. Container Apps och AKS har stöd för KEDA. För tjänster som inte stöder KEDA, till exempel App Service, använder du funktionerna för automatisk skalning som tillhandahålls av själva plattformen. Dessa funktioner omfattar ofta skalning baserat på måttbaserade regler eller HTTP-trafik.

  • Konfigurera minsta repliker. För att förhindra en kallstart konfigurerar du inställningar för automatisk skalning så att minst en replik bibehålls. En kallstart är när du initierar en tjänst från ett stoppat tillstånd, vilket ofta skapar ett fördröjt svar. Om det är en prioritet att minimera kostnaderna och du kan tolerera fördröjningar vid kallstart anger du det minsta antalet repliker till 0 när du konfigurerar automatisk skalning.

  • Konfigurera en nedkylningsperiod. Använd en lämplig nedkylningsperiod för att införa en fördröjning mellan skalningshändelser. Målet är att förhindra överdrivna skalningsaktiviteter som utlöses av tillfälliga belastningstoppar.

  • Konfigurera köbaserad skalning. Om ditt program använder en meddelandekö som Service Bus konfigurerar du inställningarna för automatisk skalning för att skala baserat på köns längd med begärandemeddelanden. Skalningen syftar till att underhålla en replik av tjänsten för varje N-meddelanden i kön (avrundas uppåt).

Referensimplementeringen använder till exempel Service Bus KEDA-skalning för att skala containerappen baserat på köns längd. Skalar service-bus-queue-length-rule tjänsten baserat på längden på en angiven Service Bus-kö. Parametern messageCount är inställd på 10, så skalaren har en tjänstreplik för varje 10 meddelanden i kön. Parametrarna scaleMaxReplicas och scaleMinReplicas anger maximalt och minsta antal repliker för tjänsten. Hemlighetenqueue-connection-string, som innehåller anslutningssträng för Service Bus-kön, hämtas från Azure Key Vault. Den här hemligheten används för att autentisera skalningsappen till Service Bus.

scaleRules: [
  {
    name: 'service-bus-queue-length-rule'
    custom: {
      type: 'azure-servicebus'
      metadata: {
        messageCount: '10'
        namespace: renderRequestServiceBusNamespace
        queueName: renderRequestServiceBusQueueName
      }
      auth: [
        {
          secretRef: 'render-request-queue-connection-string'
          triggerParameter: 'connection'
        }
      ]
    }
  }
]

scaleMaxReplicas: 5
scaleMinReplicas: 0

Distribution av containertjänster

Containerisering innebär att alla beroenden för att appen ska fungera kapslas in i en enkel avbildning som kan distribueras på ett tillförlitligt sätt till en mängd olika värdar. Följ dessa rekommendationer om du vill distribuera containrar:

  • Identifiera domängränser. Börja med att identifiera domängränserna i ditt monolitiska program. Detta hjälper dig att avgöra vilka delar av programmet som du kan extrahera till separata tjänster.

  • Skapa Docker-avbildningar. När du skapar Docker-avbildningar för dina .NET-tjänster använder du mejslade basavbildningar. Dessa avbildningar innehåller bara den minimala uppsättning paket som behövs för att .NET ska kunna köras, vilket minimerar både paketstorleken och attackytan.

  • Använd Dockerfiles i flera steg. Implementera Dockerfiles i flera steg för att separera byggtidstillgångar från containeravbildningen runtime. Det hjälper till att hålla dina produktionsbilder små och säkra.

  • Kör som en icke-root-användare. Kör dina .NET-containrar som en icke-root-användare (via användarnamn eller UID, $APP_UID) för att anpassa till principen om minsta behörighet. Det begränsar de potentiella effekterna av en komprometterad container.

  • Lyssna på port 8080. När du kör som en icke-root-användare konfigurerar du programmet så att det lyssnar på port 8080. Det är en vanlig konvention för icke-root-användare.

  • Kapsla in beroenden. Se till att alla beroenden för att appen ska fungera är inkapslade i Docker-containeravbildningen. Inkapsling gör att appen kan distribueras på ett tillförlitligt sätt till ett brett utbud av värdar.

  • Välj rätt basbilder. Vilken basavbildning du väljer beror på distributionsmiljön. Om du till exempel distribuerar till Container Apps måste du använda Linux Docker-avbildningar.

Referensimplementeringen använder till exempel en byggprocess i flera steg . De inledande stegen kompilerar och skapar programmet med en fullständig SDK-avbildning (mcr.microsoft.com/dotnet/sdk:8.0-jammy). Den slutliga körningsavbildningen skapas från basavbildningen chiseled , vilket exkluderar SDK och byggartefakter. Tjänsten körs som en icke-root-användare (USER $APP_UID) och exponerar port 8080. De beroenden som krävs för att programmet ska fungera ingår i Docker-avbildningen, vilket framgår av kommandona för att kopiera projektfiler och återställningspaket. Användningen av Linux-baserade avbildningar (mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled) säkerställer kompatibilitet med Container Apps, vilket kräver Linux-containrar för distribution.

# Build in a separate stage to avoid copying the SDK into the final image
FROM mcr.microsoft.com/dotnet/sdk:8.0-jammy AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src

# Restore packages
COPY ["Relecloud.TicketRenderer/Relecloud.TicketRenderer.csproj", "Relecloud.TicketRenderer/"]
COPY ["Relecloud.Messaging/Relecloud.Messaging.csproj", "Relecloud.Messaging/"]
COPY ["Relecloud.Models/Relecloud.Models.csproj", "Relecloud.Models/"]
RUN dotnet restore "./Relecloud.TicketRenderer/Relecloud.TicketRenderer.csproj"

# Build and publish
COPY . .
WORKDIR "/src/Relecloud.TicketRenderer"
RUN dotnet publish "./Relecloud.TicketRenderer.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

# Chiseled images contain only the minimal set of packages needed for .NET 8.0
FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled AS final
WORKDIR /app
EXPOSE 8080

# Copy the published app from the build stage
COPY --from=build /app/publish .

# Run as non-root user
USER $APP_UID
ENTRYPOINT ["dotnet", "./Relecloud.TicketRenderer.dll"]

Distribuera referensimplementeringen

Distribuera referensimplementeringen av modernt webbappmönster för .NET. Det finns instruktioner för både utvecklings- och produktionsdistribution på lagringsplatsen. När du har distribuerat kan du simulera och observera designmönster.

Diagram som visar arkitekturen för referensimplementeringen.Bild 3. Arkitektur för referensimplementeringen. Ladda ned en Visio-fil med den här arkitekturen.