I den här artikeln beskrivs hur du implementerar det moderna webbappsmönstret. Det moderna webbappsmönstret definierar hur du moderniserar molnwebbappar och introducerar en tjänstorienterad arkitektur. Mönstret innehåller riktlinjer för preskriptiv arkitektur, kod och konfiguration som överensstämmer med principerna för Azure Well-Architected Framework. Det här mönstret bygger på Reliable Web App-mönstret.
Varför ska du använda det moderna webbappsmönstret?
Det moderna webbappsmönstret hjälper dig att optimera områden med hög efterfrågan i ditt webbprogram. Den innehåller detaljerad vägledning för frikoppling av dessa områden för att möjliggöra oberoende skalning för kostnadsoptimering. Med den här metoden kan du allokera dedikerade resurser till kritiska komponenter, vilket förbättrar övergripande prestanda. 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. Det möjliggör också oberoende versionshantering av enskilda appkomponenter.
Så här implementerar du det moderna webbappsmönstret
Den här artikeln innehåller vägledning för att implementera det moderna webbappsmönstret. Använd följande länkar för att gå till den specifika vägledning som du behöver:
- Arkitekturvägledning. Lär dig hur du modulariserar webbappskomponenter 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, Queue-Based Load Leveling, Konkurrerande konsumenter och Hälsoslutpunktsövervakning.
- Konfigurationsvägledning. Konfigurera autentisering, auktorisering, automatisk skalning och containerisering för de frikopplade komponenterna.
Dricks
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äver några extra arkitektoniska komponenter. Du behöver en meddelandekö, containerplattform, lagringstjänst och containerregister, enligt följande diagram:
För ett högre servicenivåmål (SLO) kan du lägga till en andra region i webbappens arkitektur. Konfigurera lastbalanseraren för att dirigera trafik till den andra regionen för att stödja antingen en aktiv-aktiv eller en aktiv-passiv konfiguration, beroende på dina affärsbehov. De två regionerna kräver samma tjänster, förutom att en region har ett virtuellt navnätverk. 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 med förbättrad säkerhet. Följande diagram visar den här arkitekturen:
Frikoppla arkitekturen
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. I den här processen ingå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 är en kandidat för avkoppling. Tjänster som representerar distinkta affärsfunktioner och har färre beroenden är bra kandidater.
Utvärdera tjänstfördelar. Fokusera på tjänster som drar mest nytta av oberoende skalning. Ett externt beroende som en e-posttjänstleverantör i ett LOB-program kan till exempel kräva mer isolering från fel. Överväg tjänster som genomgår frekventa uppdateringar eller ändringar. Frikoppling av dessa tjänster möjliggör oberoende distribution och minskar risken för att påverka andra delar av programmet.
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 du hanterar och delar data 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 webbapptjänsten som du tänker extrahera. Mer information finns i avsnittet Välj rätt Azure-tjänster i den här artikeln.
Frikoppla webbapptjänsten. Definiera tydliga gränssnitt och API:er som de nyligen extraherade webbapptjänsterna kan använda för att interagera med andra delar av systemet. Utforma en datahanteringsstrategi som gör att varje tjänst kan hantera sina egna data, men som garanterar konsekvens och integritet. Specifika implementeringsstrategier och designmönster som ska användas under den här extraheringsprocessen finns i avsnittet Code guidance .
Använd oberoende lagring för frikopplade tjänster. För att förenkla versionshantering och distribution kontrollerar du att varje fristående tjänst har sina egna datalager. Referensimplementeringen separerar till exempel e-posttjänsten från webbappen och eliminerar behovet av att tjänsten får åtkomst till databasen. I stället kommunicerar tjänsten e-postleveransstatusen tillbaka till webbappen via ett Azure Service Bus-meddelande, och webbappen sparar en anteckning i databasen.
Implementera separata distributionspipelines för varje frikopplad tjänst. Om du implementerar separata distributionspipelines kan varje tjänst uppdateras enligt sitt eget schema. Om olika team eller organisationer inom ditt företag äger olika tjänster ger användning av 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 komponent i 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 och använd ett av 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 ändringar av resurstillstånd) måste dirigeras till prenumeranter i en publiceringsprenumerantmodell med låg fördröjning. 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 element i ditt program som du vill behålla behöver du en programplattform som stöder containrar. Vägledningen Välj en Azure-containertjänst kan hjälpa dig att välja en. Azure har tre huvudsakliga containertjänster: Azure Container Apps, Azure Kubernetes Service (AKS) och Azure App Service. Börja med Container Apps och använd ett av 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. Webbapp för containrar 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. Vägledningen Introduktion till containerregister i Azure kan hjälpa dig att välja en.
Kodvägledning
För att kunna frikoppla och extrahera en oberoende tjänst måste du uppdatera webbappkoden med följande designmönster: Strangler Fig, Queue-Based Load Leveling, Konkurrerande konsumenter, Hälsoslutpunktsövervakning och Försök igen. Följande diagram visar rollerna för dessa mönster:
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.
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ö.
mönster för konkurrerande konsumenter: Mönstret Konkurrerande konsumenter gör att flera instanser av en frikopplad tjänst kan 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.
Hälsoslutpunktsövervakningsmönster: Mönstret Hälsoslutpunktsövervakning exponerar slutpunkter för övervakning av status och hälsa för olika komponenter i 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.
Å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 i huvudwebbappen, på alla utgående anrop till andra Azure-tjänster, till exempel anrop till meddelandekön 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 av grundpelarna i Well-Architected Framework. Följande tabell innehåller information.
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 och frikopplad tjänst | ✔ | ✔ | ✔ |
RE:07 RE:10 OE:07 PE:05 |
||
Mönster för återförsök | Huvudwebbapp och frikopplad tjänst | ✔ | RE:07 |
Implementera Strangler Fig-mönstret
Använd mönstret Strangler Fig 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 flyttar funktionen till den frikopplade tjänsten dirigerar routningsskiktet alla begäranden till/users
till den nya tjänsten.Hantera funktionsdistribution.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 tjänstens stabilitet och prestanda.
Referensimplementeringen extraherar till exempel funktionen för e-postleverans till en fristående tjänst. Tjänsten kan gradvis introduceras för att hantera en större procentandel av begäranden om att skicka e-postmeddelanden som innehåller Contosos supportguider. När den nya tjänsten visar sin tillförlitlighet och prestanda kan den så småningom ta över hela uppsättningen e-postansvar 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). Se till att den nya frikopplade 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 det möjligt för den frikopplade tjänsten att 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 medan den väntar på att den frikopplade tjänsten ska hantera meddelanden i kön. Om processen kräver resultatet av den frikopplade tjänståtgärden implementerar du ett alternativt sätt att hantera situationen i väntan på att den köade åtgärden ska slutföras. I Spring Boot kan du till exempel använda klassen
StreamBridge
för att asynkront publicera meddelanden i kön utan att blockera den anropande tråden:private final StreamBridge streamBridge; public SupportGuideQueueSender(StreamBridge streamBridge) { this.streamBridge = streamBridge; } // Asynchronously publish a message without blocking the calling thread @Override public void send(String to, String guideUrl, Long requestId) { EmailRequest emailRequest = EmailRequest.newBuilder() .setRequestId(requestId) .setEmailAddress(to) .setUrlToManual(guideUrl) .build(); log.info("EmailRequest: {}", emailRequest); var message = emailRequest.toByteArray(); streamBridge.send(EMAIL_REQUEST_QUEUE, message); log.info("Message sent to the queue"); }
Det här Java-exemplet använder
StreamBridge
för att skicka meddelanden asynkront. Den här metoden säkerställer att huvudprogrammet förblir responsivt och kan hantera andra uppgifter samtidigt som 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. I Spring Boot kan du använda
@StreamListener
eller@KafkaListener
med en unik meddelandeidentifierare för att förhindra duplicerad bearbetning. Eller så kan du organisera affärsprocessen så att den fungerar på ett funktionellt sätt med Spring Cloud Stream, därconsume
metoden definieras på ett sätt som ger samma resultat när den körs upprepade gånger. En lista över inställningar som hanterar beteendet för meddelandeförbrukning finns i Spring Cloud Stream med Service Bus.Hantera ändringar i användarupplevelsen. När du använder asynkron bearbetning kanske aktiviteterna inte slutförs omedelbart. För att ställa in förväntningar och undvika förvirring, se till att användarna vet när deras uppgifter fortfarande bearbetas. 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 den frikopplade tjänsten 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. Mönstret 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 efter det första programmet.
Följ dessa rekommendationer för att implementera mönstret Konkurrerande konsumenter:
Hantera samtidiga meddelanden. När tjänster tar emot meddelanden från en kö skalar systemet förutsägbart genom att konfigurera samtidigheten så att den matchar systemdesignen. Belastningstestresultat kan hjälpa dig att fastställa lämpligt antal samtidiga meddelanden som ska hanteras. Du kan börja från en för att mäta hur systemet presterar.
Inaktivera prefetching. Inaktivera prefetching av meddelanden så att konsumenterna 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 Peek-Lock, som automatiskt försöker skicka meddelanden som inte bearbetas igen. Det här läget ger mer tillförlitlighet än metoder för borttagning 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 problematiska meddelanden till den separata kön. Med Service Bus flyttas meddelanden till kön dead-leter efter ett angivet antal leveransförsök eller vid uttryckligt avvisande av programmet.
Hantera meddelanden som inte är i ordning. Utforma konsumenter för att bearbeta meddelanden som kommer ur sekvens. När du har flera parallella konsumenter kan de 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 separerar 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ö. Detta 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 mönstret Konkurrerande konsumenter på en tillståndslös tjänst som körs i Container Apps för att bearbeta begäranden om e-postleverans från en Service Bus-kö.
Processorn loggar meddelandebearbetningsinformation för att hjälpa till med felsökning och övervakning. Den samlar in deserialiseringsfel och ger insikter som kan vara användbara vid felsökning. Tjänsten skalas på containernivå för att möjliggöra effektiv hantering av meddelandetoppar baserat på kölängd. Här är koden:
@Configuration
public class EmailProcessor {
private static final Logger log = LoggerFactory.getLogger(EmailProcessor.class);
@Bean
Function<byte[], byte[]> consume() {
return message -> {
log.info("New message received");
try {
EmailRequest emailRequest = EmailRequest.parseFrom(message);
log.info("EmailRequest: {}", emailRequest);
EmailResponse emailResponse = EmailResponse.newBuilder()
.setEmailAddress(emailRequest.getEmailAddress())
.setUrlToManual(emailRequest.getUrlToManual())
.setRequestId(emailRequest.getRequestId())
.setMessage("Email sent to " + emailRequest.getEmailAddress() + " with URL to manual " + emailRequest.getUrlToManual())
.setStatus(Status.SUCCESS)
.build();
return emailResponse.toByteArray();
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Error parsing email request message", e);
}
};
}
}
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. Spring Boot-ställdonet har inbyggt stöd för hälsokontroller. Den kan exponera slutpunkter för hälsokontroll för viktiga beroenden som databaser, meddelandeköer och lagringssystem. Följ dessa rekommendationer för att implementera hälsoslutpunktsövervakningsmönstret:
Implementera hälsokontroller. Använd Spring Boot-ställdon för att tillhandahålla slutpunkter för hälsokontroll. Ställdonet exponerar en
/actuator/health
slutpunkt som innehåller inbyggda hälsoindikatorer och anpassade kontroller för olika beroenden. Om du vill aktivera hälsoslutpunkten lägger du tillspring-boot-starter-actuator
-beroendet i dinpom.xml
- ellerbuild.gradle
-fil:<!-- Add Spring Boot Actuator dependency --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Konfigurera hälsoslutpunkten i
application.properties
enligt referensimplementeringen:management.endpoints.web.exposure.include=metrics,health,info,retry,retryevents
Verifiera beroenden. Spring Boot-aktuator innehåller hälsoindikatorer för olika beroenden som databaser, meddelandeköer (RabbitMQ eller Kafka) och lagringstjänster. Om du vill verifiera tillgängligheten för Azure-tjänster som Azure Blob Storage eller Service Bus använder du tekniker som Azure Spring Apps eller Micrometer, som tillhandahåller hälsoindikatorer för dessa tjänster. Om du behöver anpassade kontroller kan du implementera dem genom att skapa en anpassad
HealthIndicator
böna:import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; @Component public class CustomAzureServiceBusHealthIndicator implements HealthIndicator { @Override public Health health() { // Implement your health check logic here (for example, ping Service Bus). boolean isServiceBusHealthy = checkServiceBusHealth(); return isServiceBusHealthy ? Health.up().build() : Health.down().build(); } private boolean checkServiceBusHealth() { // Implement health check logic (pinging or connecting to the service). return true; // Placeholder. Implement the actual logic. } }
Konfigurera Azure-resurser. Konfigurera Azure-resursen så att den använder appens url:er för hälsokontroll för att bekräfta liveness och beredskap. Du kan till exempel använda Terraform för att bekräfta live-funktionen och beredskapen för appar som distribueras till Container Apps. Mer information finns i Hälsoavsökningar i Container Apps.
Implementera återförsöksmönstret
Mönstret försök igen gör att program kan återställas från tillfälliga fel. Det här mö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ärdas 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. Se till att konfigurera klienten som ansvarar för interaktioner med meddelandekö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 den exponentiella backoff-strategin för återförsök. Den här strategin innebär att öka tiden mellan varje återförsök exponentiellt, vilket bidrar till att minska belastningen på systemet under perioder med höga felfrekvenser.
Använd SDK-återförsöksfunktioner. För tjänster som har specialiserade SDK:er, till exempel Service Bus eller Blob Storage, använder du de inbyggda mekanismerna för återförsök. De här inbyggda mekanismerna är optimerade för tjänstens vanliga användningsfall, kan hantera återförsök mer effektivt och kräver mindre konfiguration.
Använd standardbibliotek för motståndskraft för HTTP-klienter. För HTTP-klienter kan du använda Resilience4j tillsammans med Spring RestTemplate eller WebClient för att hantera återförsök i HTTP-kommunikation. Du kan omsluta RestTemplate med Resilience4js logik för återförsök för att effektivt hantera tillfälliga HTTP-fel.
Hantera meddelandelåsning. För meddelandebaserade system implementerar du strategier för meddelandehantering som stöder återförsök utan dataförlust. Använd till exempel peek-lock-lägen när de är tillgängliga. Kontrollera att misslyckade meddelanden görs om effektivt och flyttas till en kö med obeställbara meddelanden efter upprepade fel.
Konfigurationsvägledning
Följande avsnitt innehåller vägledning för att implementera konfigurationsuppdateringarna. Varje avsnitt överensstämmer med en eller flera av grundpelarna i Well-Architected Framework.
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 om du vill konfigurera autentisering och auktorisering för 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 inkludera känslig information som anslutningssträngar 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 dem efter behov. Använd olika identiteter för olika roller, till exempel distribution och program. Om du gör det begränsas den potentiella skadan om en identitet komprometteras.
Använd infrastruktur som kod (IaC). Använd Bicep eller ett liknande IaC-verktyg som Terraform 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 bara har 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 felkonfigurationer och onödiga behörigheter och åtgärda eller ta bort 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 genom att definiera roller för Container Registry-pushar och hämtningar. Här är koden:
resource "azurerm_role_assignment" "container_app_acr_pull" {
principal_id = var.aca_identity_principal_id
role_definition_name = "AcrPull"
scope = azurerm_container_registry.acr.id
}
resource "azurerm_user_assigned_identity" "container_registry_user_assigned_identity" {
name = "ContainerRegistryUserAssignedIdentity"
resource_group_name = var.resource_group
location = var.location
}
resource "azurerm_role_assignment" "container_registry_user_assigned_identity_acr_pull" {
scope = azurerm_container_registry.acr.id
role_definition_name = "AcrPull"
principal_id = azurerm_user_assigned_identity.container_registry_user_assigned_identity.principal_id
}
# For demo purposes, allow the current user to access the container registry.
# Note: When running as a service principal, this is also needed.
resource "azurerm_role_assignment" "acr_contributor_user_role_assignement" {
scope = azurerm_container_registry.acr.id
role_definition_name = "Contributor"
principal_id = data.azuread_client_config.current.object_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 webbappen innehåller sessionstillstånd under processen kan du externalisera den 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 som gör 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 kallstarter konfigurerar du inställningar för automatisk skalning så att minst en replik bibehålls. En kall start är initieringen av en tjänst från ett stoppat tillstånd. En kallstart fördröjer ofta svaret. 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å längden på meddelandekön för begäran. Scaler försöker 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 automatiskt skala Container App baserat på längden på Service Bus-kön. Skalningsregeln, med namnet service-bus-queue-length-rule
, justerar antalet tjänstrepliker baserat på antalet meddelanden i den angivna Service Bus-kön. Parametern messageCount
är inställd på 10, vilket konfigurerar skalningsappen för att lägga till en replik för varje 10 meddelanden i kön. Det maximala antalet repliker (max_replicas
) är inställt på 10. Det minsta antalet repliker är implicit 0 om det inte åsidosätts. Med den här konfigurationen kan tjänsten skalas ned till noll när det inte finns några meddelanden i kön. Anslutningssträngen för Service Bus-kön lagras som en hemlighet i Azure med namnet azure-servicebus-connection-string
, som används för att autentisera skalningsappen till Service Bus. Här är Terraform-koden:
max_replicas = 10
min_replicas = 1
custom_scale_rule {
name = "service-bus-queue-length-rule"
custom_rule_type = "azure-servicebus"
metadata = {
messageCount = 10
namespace = var.servicebus_namespace
queueName = var.email_request_queue_name
}
authentication {
secret_name = "azure-servicebus-connection-string"
trigger_parameter = "connection"
}
}
Distribution av containertjänster
Containerisering är inkapslingen av alla beroenden som behövs av appen 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. Det 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 Java-tjänster använder du officiella OpenJDK-basavbildningar. Dessa avbildningar innehåller bara den minimala uppsättning paket som Java behöver köra. Genom att använda dessa bilder minimeras både paketstorleken och attackytan.
Använd Dockerfiles i flera steg. Använd en Dockerfile i flera steg för att separera byggtidstillgångar från runtime-containeravbildningen. Med den här typen av fil kan du hålla produktionsbilderna små och säkra. Du kan också använda en förkonfigurerad byggserver och kopiera JAR-filen till containeravbildningen.
Kör som en icke-root-användare. Kör dina Java-containrar som en icke-root-användare (via användarnamn eller UID $APP_UID) för att anpassa till principen om lägsta behörighet. Detta begränsar de potentiella effekterna av en komprometterad container.
Lyssna på port 8080. När du kör containrar som en icke-root-användare konfigurerar du programmet så att det lyssnar på port 8080. Detta är en vanlig konvention för icke-root-användare.
Kapsla in beroenden. Se till att alla beroenden som appen behöver kapslas in 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 visar en Docker-byggprocess för att containerisera ett Java-program. Dockerfile använder en enstegsversion med OpenJDK-basavbildningen (mcr.microsoft.com/openjdk/jdk:17-ubuntu
), som tillhandahåller den nödvändiga Java-körningsmiljön.
Dockerfile innehåller följande steg:
- Deklarera volymen. En tillfällig volym (
/tmp
) definieras. Den här volymen tillhandahåller tillfällig fillagring som är separat från containerns huvudfilsystem. - Kopierar artefakter. Programmets JAR-fil (
email-processor.jar
) kopieras till containern, tillsammans med Application Insights-agenten (applicationinsights-agent.jar
) som används för övervakning. - Ange startpunkten. Containern är konfigurerad för att köra programmet med Application Insights-agenten aktiverad. Koden använder
java -javaagent
för att övervaka programmet under körningen.
Dockerfile håller avbildningen liten genom att endast inkludera körningsberoenden. Den är lämplig för distributionsmiljöer som Container Apps som stöder Linux-baserade containrar.
# Use the OpenJDK 17 base image on Ubuntu as the foundation.
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu
# Define a volume to allow temporary files to be stored separately from the container's main file system.
VOLUME /tmp
# Copy the packaged JAR file into the container.
COPY target/email-processor.jar app.jar
# Copy the Application Insights agent for monitoring.
COPY target/agent/applicationinsights-agent.jar applicationinsights-agent.jar
# Set the entrypoint to run the application with the Application Insights agent.
ENTRYPOINT ["java", "-javaagent:applicationinsights-agent.jar", "-jar", "/app.jar"]
Distribuera referensimplementeringen
Distribuera referensimplementeringen av det moderna webbappmönstret för Java. Det finns instruktioner för både utvecklings- och produktionsdistribution på lagringsplatsen. När du har distribuerat implementeringen kan du simulera och observera designmönster.
Följande diagram visar arkitekturen för referensimplementeringen:
Ladda ned en Visio-fil av den här arkitekturen.