Bewerken

Delen via


Modern Web App-patroon voor Java

Azure App Service
Azure Front Door
Azure Cache for Redis

In dit artikel wordt beschreven hoe u het patroon moderne web-apps implementeert. Het patroon Moderne web-app definieert hoe u web-apps in de cloud moet moderniseren en een servicegerichte architectuur moet introduceren. Het patroon Moderne web-app biedt prescriptieve architectuur, code en configuratierichtlijnen die zijn afgestemd op de principes van het Goed ontworpen Framework van Azure en bouwt voort op het patroon Reliable Web App.

Waarom het patroon Moderne web-app gebruiken?

Het patroon Moderne web-app helpt bij het optimaliseren van gebieden met hoge vraag van uw webtoepassing. Het biedt gedetailleerde richtlijnen voor het loskoppelen van deze gebieden, waardoor onafhankelijk schalen voor kostenoptimalisatie mogelijk is. Met deze aanpak kunt u toegewezen resources toewijzen aan kritieke onderdelen, waardoor de algehele prestaties worden verbeterd. Het loskoppelen van afzonderlijke services kan de betrouwbaarheid verbeteren door te voorkomen dat vertragingen in één deel van de app van invloed zijn op andere en afzonderlijke app-onderdelen onafhankelijk van elkaar inschakelen.

Het patroon Moderne web-app implementeren

Dit artikel bevat richtlijnen voor architectuur, code en configuratie voor het implementeren van het patroon Moderne web-app. Gebruik de volgende koppelingen om naar de richtlijnen te navigeren die u nodig hebt:

  • Architectuurrichtlijnen: Leer hoe u onderdelen van web-apps modulariseert en de juiste PaaS-oplossingen (Platform as a Service) selecteert.
  • Coderichtlijnen: Implementeer vier ontwerppatronen om de losgekoppelde onderdelen te optimaliseren: Strangler Fig, load leveling op basis van wachtrij, concurrerende consumenten en statuseindpuntbewakingspatronen.
  • Configuratierichtlijnen: Verificatie, autorisatie, automatisch schalen en containerisatie configureren voor de losgekoppelde onderdelen.

Tip

GitHub-logo Er is een referentie-implementatie (voorbeeld-app) van het patroon Moderne web-app. Het vertegenwoordigt de eindstatus van de implementatie van de moderne web-app. Het is een web-app op productieniveau die alle code, architectuur en configuratie-updates bevat die in dit artikel worden besproken. Implementeer en gebruik de referentie-implementatie om uw implementatie van het moderne web-app-patroon te begeleiden.

Uitleg bij architectuur

Het patroon Moderne web-app is gebaseerd op het patroon Reliable Web App. Er zijn enkele extra architectuuronderdelen nodig om te implementeren. U hebt een berichtenwachtrij, containerplatform, opslagservice en een containerregister nodig (zie afbeelding 1).

Diagram met de basislijnarchitectuur van het patroon Moderne web-app.Afbeelding 1. Essentiële architecturale elementen van het patroon Moderne web-app.

Voor een hogere serviceniveaudoelstelling (SLO) kunt u een tweede regio toevoegen aan uw web-app-architectuur. Configureer uw load balancer om verkeer naar de tweede regio te routeren om een actief-actief- of actief-passieve configuratie te ondersteunen, afhankelijk van uw bedrijfsbehoefte. Voor de twee regio's zijn dezelfde services vereist, behalve één regio, een virtueel hubnetwerk dat verbinding maakt. Gebruik een hub-and-spoke-netwerktopologie om resources, zoals een netwerkfirewall, te centraliseren en te delen. Open de containeropslagplaats via het virtuele hubnetwerk. Als u virtuele machines hebt, voegt u een bastionhost toe aan het virtuele hubnetwerk om deze veilig te beheren (zie afbeelding 2).

Diagram met de architectuur van het moderne web-app-patroon met de tweede regio en hub-and-spoke-netwerktopologie.Afbeelding 2. De architectuur van het moderne web-app-patroon met de tweede regio en hub-and-spoke-netwerktopologie.

Architectuur loskoppelen

Als u het patroon Moderne web-app wilt implementeren, moet u de bestaande web-app-architectuur loskoppelen. Het loskoppelen van de architectuur omvat het opsplitsen van een monolithische toepassing in kleinere, onafhankelijke services, die elk verantwoordelijk zijn voor een specifieke functie of functionaliteit. Dit proces omvat het evalueren van de huidige web-app, het wijzigen van de architectuur en ten slotte het extraheren van de code van de web-app naar een containerplatform. Het doel is om toepassingsservices die het meeste profiteren van ontkoppeld, systematisch te identificeren en te extraheren. Volg deze aanbevelingen om uw architectuur los te koppelen:

  • Identificeer servicegrenzen Door domeingestuurde ontwerpprincipes toepassen om gebonden contexten binnen uw monolithische toepassing te identificeren. Elke gebonden context vertegenwoordigt een logische grens en kan een kandidaat zijn voor een afzonderlijke service. Services die afzonderlijke bedrijfsfuncties vertegenwoordigen en minder afhankelijkheden hebben, zijn goede kandidaten voor ontkoppeling.

  • Evalueer servicevoordelen. Richt u op services die het meest profiteren van onafhankelijk schalen. Een externe afhankelijkheid, zoals een e-mailserviceprovider in een LOB-toepassing, vereist bijvoorbeeld mogelijk meer isolatie van fouten. Overweeg services die regelmatig updates of wijzigingen ondergaan. Door deze services te ontkoppelen, kan de implementatie onafhankelijk worden geïmplementeerd en wordt het risico verkleind dat andere onderdelen van de toepassing worden beïnvloed.

  • Technische haalbaarheid beoordelen. Bekijk de huidige architectuur om technische beperkingen en afhankelijkheden te identificeren die van invloed kunnen zijn op het ontkoppelingsproces. Plan hoe gegevens worden beheerd en gedeeld tussen services. Losgekoppelde services moeten hun eigen gegevens beheren en directe databasetoegang over servicegrenzen minimaliseren.

  • Azure-services implementeren. Selecteer en implementeer de Azure-services die u nodig hebt om de web-app-service te ondersteunen die u wilt extraheren. Gebruik de volgende sectie Azure-services selecteren voor hulp.

  • De web-app-service loskoppelen. Definieer duidelijke interfaces en API's voor de zojuist geëxtraheerde web-app-services om te communiceren met andere onderdelen van het systeem. Ontwerp een strategie voor gegevensbeheer waarmee elke service zijn eigen gegevens kan beheren en tegelijkertijd consistentie en integriteit kan garanderen. Raadpleeg de sectie Code-richtlijnen voor specifieke implementatiestrategieën en ontwerppatronen die tijdens dit extractieproces moeten worden gebruikt.

  • Gebruik onafhankelijke opslag voor losgekoppelde services. Elke losgekoppelde service moet zijn eigen gegevensarchieven hebben om versiebeheer en implementatie te vereenvoudigen. De referentie-implementatie scheidt bijvoorbeeld de e-mailservice van de web-app en elimineert de noodzaak voor de service om toegang te krijgen tot de database. In plaats daarvan communiceert de service de status van de e-mailbezorging terug naar de web-app via een Azure Service Bus-bericht en slaat de web-app een notitie op in de database.

  • Implementeer afzonderlijke implementatiepijplijnen voor elke losgekoppelde service. Met afzonderlijke implementatiepijplijnen kan elke service in zijn eigen tempo worden bijgewerkt. Als verschillende teams of organisaties binnen uw bedrijf eigenaar zijn van verschillende services, heeft u afzonderlijke implementatiepijplijnen die elk team controle geven over hun eigen implementaties. Gebruik hulpprogramma's voor continue integratie en continue levering (CI/CD), zoals Jenkins, GitHub Actions of Azure Pipelines om deze pijplijnen in te stellen.

  • Besturingselementen voor beveiliging herzien. Zorg ervoor dat uw beveiligingscontroles worden bijgewerkt om rekening te houden met de nieuwe architectuur, waaronder firewallregels en toegangsbeheer.

De juiste Azure-services selecteren

Raadpleeg voor elke Azure-service in uw architectuur de relevante Azure-servicehandleiding in het Well-Architected Framework. Voor het patroon Moderne web-app hebt u een berichtensysteem nodig om asynchrone berichten te ondersteunen, een toepassingsplatform dat ondersteuning biedt voor containerisatie en een opslagplaats voor containerinstallatiekopieën.

  • Kies een berichtenwachtrij. Een berichtenwachtrij is een belangrijk onderdeel van servicegerichte architecturen. Hiermee worden afzenders en ontvangers van berichten ontkoppeld om asynchrone berichten in te schakelen. Gebruik de richtlijnen voor het kiezen van een Azure Messaging-service om een Azure Messaging-systeem te kiezen dat uw ontwerpbehoeften ondersteunt. Azure heeft drie berichtenservices: Azure Event Grid, Azure Event Hubs en Service Bus. Begin met Service Bus als standaardoptie en gebruik de andere twee opties als Service Bus niet aan uw behoeften voldoet.

    Service Gebruiksscenario
    Service Bus Kies Service Bus voor betrouwbare, geordende en mogelijk transactionele levering van hoogwaardige berichten in bedrijfstoepassingen.
    Event Grid Kies Event Grid wanneer u een groot aantal discrete gebeurtenissen efficiënt moet afhandelen. Event Grid is schaalbaar voor gebeurtenisgestuurde toepassingen waarbij veel kleine, onafhankelijke gebeurtenissen (zoals wijzigingen in de resourcestatus) moeten worden gerouteerd naar abonnees in een model met lage latentie en publiceren-abonneren.
    Event Hubs Kies Event Hubs voor enorme gegevensopname met hoge doorvoer, zoals telemetrie, logboeken of realtime analyse. Event Hubs is geoptimaliseerd voor streamingscenario's waarbij bulkgegevens continu moeten worden opgenomen en verwerkt.
  • Een containerservice implementeren. Voor de onderdelen van uw toepassing die u in een container wilt plaatsen, hebt u een toepassingsplatform nodig dat containers ondersteunt. Gebruik de richtlijnen voor een Azure-containerservice kiezen om uw beslissing te nemen. Azure heeft drie principal-containerservices: Azure Container Apps, Azure Kubernetes Service (AKS) en Azure-app Service. Begin met Container Apps als de standaardoptie en gebruik de andere twee opties als Container Apps niet aan uw behoeften voldoet.

    Service Gebruiksscenario
    Container Apps Kies Container Apps als u een serverloos platform nodig hebt dat containers automatisch schaalt en beheert in gebeurtenisgestuurde toepassingen.
    AKS Kies AKS als u gedetailleerde controle nodig hebt over Kubernetes-configuraties en geavanceerde functies voor schalen, netwerken en beveiliging.
    Web Apps voor container Kies Web App for Containers in App Service voor de eenvoudigste PaaS-ervaring.
  • Implementeer een containeropslagplaats. Wanneer u een rekenservice op basis van containers gebruikt, moet u een opslagplaats hebben om de containerinstallatiekopieën op te slaan. U kunt een openbaar containerregister gebruiken, zoals Docker Hub of een beheerd register, zoals Azure Container Registry. Gebruik de inleiding tot containerregisters in Azure-richtlijnen om uw beslissing te nemen.

Richtlijnen voor code

Als u een onafhankelijke service wilt loskoppelen en extraheren, moet u de code van uw web-app bijwerken met de volgende ontwerppatronen: het patroon Strangler Fig, het patroon Load Leveling op basis van wachtrij, het patroon Concurrerende consumenten, het patroon Statuseindpuntbewaking en het patroon Opnieuw proberen.

Diagram met de rol van de ontwerppatronen in de architectuur van het moderne web-app-patroon.Afbeelding 3. Rol van de ontwerppatronen.

  1. Strangler Fig-patroon: Het Strangler Fig-patroon migreert incrementeel functionaliteit van een monolithische toepassing naar de losgekoppelde service. Implementeer dit patroon in de hoofdweb-app om functionaliteit geleidelijk te migreren naar onafhankelijke services door verkeer te leiden op basis van eindpunten.

  2. Patroon load leveling op basis van wachtrij: het patroon Load Leveling op basis van wachtrij beheert de stroom van berichten tussen de producent en de consument door een wachtrij als buffer te gebruiken. Implementeer dit patroon op het producentgedeelte van de ontkoppelde service om de berichtstroom asynchroon te beheren met behulp van een wachtrij.

  3. Patroon concurrerende consumenten: Met het patroon Concurrerende consumenten kunnen meerdere exemplaren van de losgekoppelde service onafhankelijk van dezelfde berichtenwachtrij lezen en concurreren om berichten te verwerken. Implementeer dit patroon in de losgekoppelde service om taken over meerdere exemplaren te distribueren.

  4. Patroon Statuseindpuntbewaking: Het patroon Statuseindpuntbewaking toont eindpunten voor het bewaken van de status en status van verschillende onderdelen van de web-app. (4a) Implementeer dit patroon in de hoofdweb-app. (4b) Implementeer deze ook in de losgekoppelde service om de status van eindpunten bij te houden.

  5. Patroon voor opnieuw proberen: het patroon Opnieuw proberen verwerkt tijdelijke fouten door bewerkingen opnieuw uit te voeren die af en toe kunnen mislukken. (5a) Implementeer dit patroon voor alle uitgaande aanroepen naar andere Azure-services in de hoofdweb-app, zoals aanroepen naar berichtenwachtrij en privé-eindpunten. (5b) Implementeer dit patroon ook in de ontkoppelde service om tijdelijke fouten in aanroepen naar de privé-eindpunten af te handelen.

Elk ontwerppatroon biedt voordelen die zijn afgestemd op een of meer pijlers van het Well-Architected Framework (zie de volgende tabel).

Ontwerppatroon Implementatielocatie Betrouwbaarheid (RE) Beveiliging (SE) Kostenoptimalisatie (CO) Operational Excellence (OE) Prestatie-efficiëntie (PE) Goed ontworpen frameworkprincipes ondersteunen
Strangler Fig-patroon Hoofdweb-app RE:08
CO:07
CO:08
OE:06
OE:11
Patroon Load Leveling op basis van wachtrij Producent van losgekoppelde service RE:06
RE:07
CO:12
PE:05
Patroon concurrerende consumenten Losgekoppelde service RE:05
RE:07
CO:05
CO:07
PE:05
PE:07
Patroon Statuseindpuntbewaking Hoofdweb-app en losgekoppelde service RE:07
RE:10
OE:07
PE:05
Patroon opnieuw proberen Hoofdweb-app en losgekoppelde service RE:07

Het strangler Fig-patroon implementeren

Gebruik het strangler fig-patroon om de functionaliteit van de monolithische codebasis geleidelijk te migreren naar nieuwe onafhankelijke services. Extraheer nieuwe services uit de bestaande monolithische codebasis en moderniseer langzaam kritieke onderdelen van de web-app. Volg deze aanbevelingen om het strangler Fig-patroon te implementeren:

  • Stel een routeringslaag in. Implementeer in de codebasis van de monolithische web-app een routeringslaag die verkeer omleidt op basis van eindpunten. Gebruik zo nodig aangepaste routeringslogica voor het afhandelen van specifieke bedrijfsregels voor het omleiden van verkeer. Als u bijvoorbeeld een /users eindpunt in uw monolithische app hebt en deze functionaliteit hebt verplaatst naar de losgekoppelde service, stuurt de routeringslaag alle aanvragen naar /users de nieuwe service.

  • Implementatie van functies beheren. Implementeer functievlagmen en gefaseerde implementatie om de losgekoppelde services geleidelijk uit te rollen. De bestaande monolithische app-routering moet bepalen hoeveel aanvragen de losgekoppelde services ontvangen. Begin met een klein percentage aanvragen en verhoog het gebruik in de loop van de tijd naarmate u vertrouwen krijgt in de stabiliteit en prestaties.

    De referentie-implementatie extraheert bijvoorbeeld de functionaliteit voor e-mailbezorging in een zelfstandige service, die geleidelijk kan worden geïntroduceerd voor het afhandelen van een groter deel van de aanvragen voor het verzenden van e-mailberichten met ondersteuningsgidsen van Contoso. Omdat de nieuwe service de betrouwbaarheid en prestaties bewijst, kan deze uiteindelijk de volledige set e-mailverantwoordelijkheden van de monolithische gegevens overnemen, waardoor de overgang wordt voltooid.

  • Gebruik een gevelservice (indien nodig). Een gevelservice is handig wanneer één aanvraag moet communiceren met meerdere services of wanneer u de complexiteit van het onderliggende systeem van de client wilt verbergen. Als de ontkoppelde service echter geen openbare API's heeft, is een façadeservice mogelijk niet nodig.

    Implementeer in de codebasis van de monolithische web-app een façadeservice om aanvragen naar de juiste back-end (monolith of microservice) te routeren. Zorg ervoor dat de nieuwe service in de nieuwe ontkoppelde service aanvragen onafhankelijk kan verwerken wanneer deze toegankelijk is via de gevel.

Het patroon Load Leveling op basis van wachtrij implementeren

Implementeer het patroon load leveling op basis van wachtrijen op het producentgedeelte van de losgekoppelde service om taken die niet direct hoeven te worden beantwoord, asynchroon af te handelen. Dit patroon verbetert de algehele reactiesnelheid en schaalbaarheid van het systeem met behulp van een wachtrij voor het beheren van de workloaddistributie. Hiermee kan de ontkoppelde service aanvragen met een consistente snelheid verwerken. Volg deze aanbevelingen om dit patroon effectief te implementeren:

  • Gebruik niet-blokkerende berichtenwachtrijen. Zorg ervoor dat het proces waarmee berichten naar de wachtrij worden verzonden, andere processen niet blokkeert terwijl wordt gewacht tot de losgekoppelde service berichten in de wachtrij verwerkt. Als voor het proces het resultaat van de losgekoppelde servicebewerking is vereist, implementeert u een alternatieve manier om de situatie af te handelen terwijl wordt gewacht tot de bewerking in de wachtrij is voltooid. In Spring Boot kunt u de StreamBridge klasse bijvoorbeeld gebruiken om asynchroon berichten naar de wachtrij te publiceren zonder de aanroepende thread te blokkeren (zie voorbeeldcode):

    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");
    }
    

    In dit Java-voorbeeld worden StreamBridge berichten asynchroon verzonden. Deze aanpak zorgt ervoor dat de hoofdtoepassing responsief blijft en andere taken gelijktijdig kan verwerken, terwijl de losgekoppelde service de aanvragen in de wachtrij met een beheersbaar tarief verwerkt.

  • Implementeer het opnieuw proberen en verwijderen van berichten. Implementeer een mechanisme voor het opnieuw verwerken van berichten in de wachtrij die niet kunnen worden verwerkt. Als er fouten optreden, moeten deze berichten uit de wachtrij worden verwijderd. Service Bus heeft bijvoorbeeld ingebouwde functies voor nieuwe pogingen en wachtrijen met dode letters.

  • Configureer idempotent berichtverwerking. De logica waarmee berichten uit de wachtrij worden verwerkt, moet idempotent zijn voor het afhandelen van gevallen waarin een bericht meerdere keren kan worden verwerkt. In Spring Boot kunt u of @KafkaListener met een unieke bericht-id gebruiken @StreamListener om dubbele verwerking te voorkomen. Of u kunt het bedrijfsproces organiseren om te werken in een functionele benadering met Spring Cloud Stream, waarbij de consume methode wordt gedefinieerd op een manier die hetzelfde resultaat produceert wanneer deze herhaaldelijk wordt uitgevoerd. Lees Spring Cloud Stream met Service Bus voor een lijst met instellingen die het gedrag beheren van hoe berichten worden gebruikt.

  • Wijzigingen in de ervaring beheren. Asynchrone verwerking kan ertoe leiden dat taken niet onmiddellijk worden voltooid. Gebruikers moeten zich bewust worden gemaakt wanneer hun taak nog steeds wordt verwerkt om de juiste verwachtingen in te stellen en verwarring te voorkomen. Gebruik visuele aanwijzingen of berichten om aan te geven dat een taak wordt uitgevoerd. Geef gebruikers de mogelijkheid om meldingen te ontvangen wanneer hun taak is voltooid, zoals een e-mail of pushmelding.

Het patroon Concurrerende consumenten implementeren

Implementeer het patroon Concurrerende consumenten in de ontkoppelde service om binnenkomende taken uit de berichtenwachtrij te beheren. Dit patroon omvat het distribueren van taken over meerdere instanties van losgekoppelde services. Deze services verwerken berichten uit de wachtrij, verbeteren de taakverdeling en stimuleren de capaciteit van het systeem om gelijktijdige aanvragen te verwerken. Het patroon Concurrerende consumenten is effectief wanneer:

  • De volgorde van berichtverwerking is niet van cruciaal belang.
  • De wachtrij blijft niet beïnvloed door verkeerd gevormde berichten.
  • De verwerkingsbewerking is idempotent, wat betekent dat deze meerdere keren kan worden toegepast zonder het resultaat te wijzigen buiten de eerste toepassing.

Volg deze aanbevelingen om het patroon Concurrerende consumenten te implementeren:

  • Gelijktijdige berichten verwerken. Wanneer u berichten van een wachtrij ontvangt, moet u ervoor zorgen dat uw systeem voorspelbaar wordt geschaald door de gelijktijdigheid zo te configureren dat deze overeenkomt met uw systeemontwerp. Met de resultaten van de belastingtest kunt u bepalen hoeveel gelijktijdige berichten moeten worden verwerkt en u kunt beginnen met één om te meten hoe het systeem presteert.

  • Schakel prefetching uit. Schakel het afhalen van berichten uit, zodat gebruikers alleen berichten ophalen wanneer ze klaar zijn.

  • Gebruik betrouwbare berichtverwerkingsmodi. Gebruik een betrouwbare verwerkingsmodus, zoals PeekLock (of het equivalent daarvan), waarmee berichten die niet kunnen worden verwerkt, automatisch opnieuw worden uitgevoerd. Deze modus verbetert de betrouwbaarheid ten opzichte van verwijderings-eerste methoden. Als een werkrol een bericht niet kan verwerken, moet een andere werknemer het zonder fouten kunnen verwerken, zelfs als het bericht meerdere keren wordt verwerkt.

  • Foutafhandeling implementeren. Verkeerd gevormde of niet-verwerkte berichten doorsturen naar een afzonderlijke wachtrij met dode letters. Dit ontwerp voorkomt terugkerende verwerking. U kunt bijvoorbeeld uitzonderingen vangen tijdens het verwerken van berichten en het problematische bericht verplaatsen naar de afzonderlijke wachtrij. Voor Service Bus worden berichten na een opgegeven aantal bezorgingspogingen of bij expliciete afwijzing door de toepassing verplaatst naar de wachtrij met dead-leter.

  • Berichten buiten de volgorde verwerken. Ontwerp consumenten om berichten te verwerken die niet op volgorde aankomen. Meerdere parallelle consumenten betekent dat ze berichten mogelijk niet op volgorde verwerken.

  • Schalen op basis van de lengte van de wachtrij. Services die berichten uit een wachtrij gebruiken, moeten automatisch worden geschaald op basis van de lengte van de wachtrij. Automatische schaalaanpassing op basis van schaal zorgt voor een efficiënte verwerking van pieken in binnenkomende berichten.

  • Gebruik de berichtenwachtrij. Als voor het systeem meldingen zijn vereist voor het verwerken van berichten, stelt u een speciale antwoord- of antwoordwachtrij in. Deze installatie verdeelt operationele berichten van meldingsprocessen.

  • Gebruik stateless services. Overweeg om stateless services te gebruiken om aanvragen uit een wachtrij te verwerken. Het maakt eenvoudig schalen en efficiënt resourcegebruik mogelijk.

  • Logboekregistratie configureren. Integreer logboekregistratie en specifieke uitzonderingsafhandeling in de werkstroom voor berichtverwerking. Richt u op het vastleggen van serialisatiefouten en het doorsturen van deze problematische berichten naar een mechanisme voor dode letters. Deze logboeken bieden waardevolle inzichten voor probleemoplossing.

De referentie-implementatie maakt bijvoorbeeld gebruik van het patroon Concurrerende consumenten voor een staatloze service die wordt uitgevoerd in Container Apps om de aanvragen voor e-mailbezorging vanuit een Service Bus-wachtrij te verwerken.

De processor registreert berichtverwerkingsgegevens, hulp bij het oplossen van problemen en bewaking. Hiermee worden deserialisatiefouten vastgelegd en worden inzichten geboden die nodig zijn bij het opsporen van fouten in het proces. De service wordt geschaald op containerniveau, zodat berichtenpieken efficiënt kunnen worden verwerkt op basis van de wachtrijlengte (zie de volgende code).

@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);
            }
        };
    }
}

Het patroon Statuseindpuntbewaking implementeren

Implementeer het patroon Statuseindpuntbewaking in de hoofd-app-code en losgekoppelde servicecode om de status van toepassingseindpunten bij te houden. Orchestrators zoals AKS of Container Apps kunnen deze eindpunten peilen om de servicestatus te controleren en beschadigde exemplaren opnieuw op te starten. Spring Boot biedt ingebouwde ondersteuning voor statuscontroles met Spring Boot Actuator, waarmee statuscontrole-eindpunten kunnen worden weergegeven voor belangrijke afhankelijkheden, zoals databases, berichtbrokers en opslagsystemen. Volg deze aanbevelingen om het patroon Statuseindpuntbewaking te implementeren:

  • Statuscontroles implementeren. Spring Boot Actuator gebruiken om statuscontrole-eindpunten te bieden. Spring Boot Actuator toont een eindpunt /actuator/health met ingebouwde statusindicatoren en aangepaste controles voor verschillende afhankelijkheden. Als u het statuseindpunt wilt inschakelen, voegt u de spring-boot-starter-actuator afhankelijkheid toe aan uw pom.xml of build.gradle bestand.

    <!-- Add Spring Boot Actuator dependency -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    Configureer het statuseindpunt in application.properties zoals wordt weergegeven in de referentie-implementatie: txt management.endpoints.web.exposure.include=metrics,health,info,retry,retryevents

  • Afhankelijkheden valideren. Spring Boot Actuator bevat statusindicatoren voor verschillende afhankelijkheden zoals databases, berichtbrokers (RabbitMQ of Kafka) en opslagservices. Als u de beschikbaarheid van Azure-services, zoals Azure Blob Storage of Service Bus, wilt valideren, gebruikt u community-invoegtoepassingen zoals Azure Spring Apps of Micrometer-integraties, die statusindicatoren voor deze services bieden. Als aangepaste controles nodig zijn, kunt u deze implementeren door een aangepaste HealthIndicator bean te maken.

    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 (e.g., 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 actual logic
        }
    }
    
  • Azure-resources configureren. Configureer de Azure-resource om de URL's van de statuscontrole van de app te gebruiken om de liveness en gereedheid te bevestigen. U kunt terraform bijvoorbeeld gebruiken om de URL's voor statuscontrole te gebruiken om de liveheid en gereedheid van apps te bevestigen die zijn geïmplementeerd in Container Apps. Zie Statustests in Container Apps voor meer informatie.

Het patroon Opnieuw proberen implementeren

Met het patroon Opnieuw proberen kunnen toepassingen herstellen van tijdelijke fouten. Het patroon Opnieuw proberen is centraal in het patroon Betrouwbare web-app, dus uw web-app moet het patroon Opnieuw proberen al gebruiken. Pas het patroon Opnieuw proberen toe op aanvragen op de berichtensystemen en aanvragen die zijn uitgegeven door de losgekoppelde services die u uit de web-app haalt. Volg deze aanbevelingen om het patroon Opnieuw proberen te implementeren:

  • Configureer opties voor opnieuw proberen. Wanneer u integreert met een berichtenwachtrij, moet u ervoor zorgen dat u de client configureert die verantwoordelijk is voor interacties met de wachtrij met de juiste instellingen voor opnieuw proberen. Geef parameters op, zoals het maximum aantal nieuwe pogingen, vertraging tussen nieuwe pogingen en maximale vertraging.

  • Gebruik exponentieel uitstel. Implementeer de strategie voor exponentieel uitstel voor nieuwe pogingen. Dit betekent dat de tijd tussen elke nieuwe poging exponentieel toeneemt, waardoor de belasting van het systeem tijdens perioden met hoge storingsfrequenties wordt verminderd.

  • Gebruik de sdk-functionaliteit voor opnieuw proberen. Gebruik de ingebouwde mechanismen voor opnieuw proberen voor services met gespecialiseerde SDK's, zoals Service Bus of Blob Storage. De ingebouwde mechanismen voor opnieuw proberen zijn geoptimaliseerd voor de typische gebruiksscenario's van de service en kunnen nieuwe pogingen effectiever verwerken met minder configuratie die voor u vereist is.

  • Gebruik standaardbibliotheken voor tolerantie voor HTTP-clients. Voor HTTP-clients kunt u Resilience4* samen met Spring RestTemplate of WebClient gebruiken om nieuwe pogingen in HTTP-communicatie af te handelen. De RestTemplate van Spring kan worden verpakt met de logica voor opnieuw proberen van Resilience4j om tijdelijke HTTP-fouten effectief af te handelen.

  • Afhandelen van berichtvergrendeling. Voor systemen op basis van berichten implementeert u strategieën voor het verwerken van berichten die ondersteuning bieden voor nieuwe pogingen zonder gegevensverlies, zoals het gebruik van 'peek-lock'-modi waar beschikbaar. Zorg ervoor dat mislukte berichten effectief opnieuw worden geprobeerd en worden verplaatst naar een wachtrij met onbestelbare berichten na herhaalde fouten.

Configuratierichtlijnen

De volgende secties bevatten richtlijnen voor het implementeren van de configuratie-updates. Elke sectie wordt uitgelijnd met een of meer pijlers van het Well-Architected Framework.

Configuratie Betrouwbaarheid (RE) Beveiliging (SE) Kostenoptimalisatie (CO) Operational Excellence (OE) Prestatie-efficiëntie (PE) Goed ontworpen frameworkprincipes ondersteunen
Verificatie en autorisatie configureren SE:05
OE:10
Onafhankelijke automatische schaalaanpassing implementeren RE:06
CO:12
PE:05
Service-implementatie containeriseren CO:13
PE:09
PE:03

Verificatie en autorisatie configureren

Als u verificatie en autorisatie wilt configureren voor nieuwe Azure-services (workloadidentiteiten) die u aan de web-app toevoegt, volgt u deze aanbevelingen:

  • Gebruik beheerde identiteiten voor elke nieuwe service. Elke onafhankelijke service moet een eigen identiteit hebben en beheerde identiteiten gebruiken voor service-naar-service-verificatie. Beheerde identiteiten elimineren de noodzaak om referenties in uw code te beheren en het risico op het lekken van referenties te verminderen. Het helpt u te voorkomen dat gevoelige informatie, zoals verbindingsreeks s, in uw code- of configuratiebestanden wordt gebruikt.

  • Ververleent minimale bevoegdheden aan elke nieuwe service. Wijs alleen de benodigde machtigingen toe aan elke nieuwe service-id. Als een identiteit bijvoorbeeld alleen naar een containerregister hoeft te pushen, geeft u deze geen pull-machtigingen. Controleer deze machtigingen regelmatig en pas deze indien nodig aan. Gebruik verschillende identiteiten voor verschillende rollen, zoals implementatie en de toepassing. Dit beperkt de mogelijke schade als één identiteit wordt aangetast.

  • Infrastructuur als code (IaC) gebruiken. Gebruik Bicep of vergelijkbare IaC-hulpprogramma's zoals Terraform om uw cloudresources te definiëren en te beheren. IaC zorgt voor een consistente toepassing van beveiligingsconfiguraties in uw implementaties en stelt u in staat om de installatie van uw infrastructuur te beheren.

Volg deze aanbevelingen om verificatie en autorisatie voor gebruikers (gebruikersidentiteiten) te configureren:

  • Geef gebruikers minimale bevoegdheden. Net als bij services moet u ervoor zorgen dat gebruikers alleen de machtigingen krijgen die ze nodig hebben om hun taken uit te voeren. Controleer en pas deze machtigingen regelmatig aan.

  • Voer regelmatig beveiligingscontroles uit. Controleer uw beveiligingsconfiguratie regelmatig en controleer deze. Zoek naar onjuiste configuraties of onnodige machtigingen en corrigeer ze onmiddellijk.

De referentie-implementatie maakt gebruik van IaC om beheerde identiteiten toe te wijzen aan toegevoegde services en specifieke rollen aan elke identiteit. Het definieert rollen en machtigingentoegang voor implementatie door rollen te definiëren voor Container Registry push en pull (zie de volgende code).

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 current user access to 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
}

Onafhankelijke automatische schaalaanpassing configureren

Het patroon Moderne web-app begint met het opsplitsen van de monolithische architectuur en introduceert serviceontdubbeling. Wanneer u een web-app-architectuur loskoppelt, kunt u losgekoppelde services onafhankelijk schalen. Door de Azure-services te schalen ter ondersteuning van een onafhankelijke web-app-service, in plaats van een volledige web-app, worden de schaalkosten geoptimaliseerd terwijl aan de eisen wordt tegemoet getreden. Volg deze aanbevelingen om containers automatisch te schalen:

  • Gebruik stateless services. Zorg ervoor dat uw services staatloos zijn. Als uw web-app de sessiestatus in behandeling bevat, moet u deze externaliseren naar een gedistribueerde cache zoals Redis of een database zoals SQL Server.

  • Configureer regels voor automatisch schalen. Gebruik de configuraties voor automatisch schalen die de meest rendabele controle over uw services bieden. Voor containerservices biedt schaalaanpassing op basis van gebeurtenissen, zoals Kubernetes Event Driven Autoscaler (KEDA), vaak gedetailleerde controle, zodat u kunt schalen op basis van metrische gegevens van gebeurtenissen. Container Apps en AKS ondersteunen KEDA. Voor services die KEDA niet ondersteunen, zoals App Service, gebruikt u de functies voor automatisch schalen die door het platform zelf worden geleverd. Deze functies omvatten vaak schalen op basis van regels op basis van metrische gegevens of HTTP-verkeer.

  • Configureer minimale replica's. Als u een koude start wilt voorkomen, configureert u instellingen voor automatisch schalen om minimaal één replica te behouden. Een koude start is wanneer u een service initialiseert vanuit een gestopte status, waardoor vaak een vertraagd antwoord ontstaat. Als het minimaliseren van de kosten een prioriteit is en u koude startvertragingen kunt tolereren, stelt u het minimumaantal replica's in op 0 bij het configureren van automatische schaalaanpassing.

  • Configureer een afkoelperiode. Pas een geschikte afkoelperiode toe om een vertraging tussen schaalgebeurtenissen te introduceren. Het doel is om overmatige schaalactiviteiten te voorkomen die worden geactiveerd door tijdelijke belastingpieken.

  • Schaalaanpassing op basis van wachtrijen configureren. Als uw toepassing gebruikmaakt van een berichtenwachtrij zoals Service Bus, configureert u de instellingen voor automatisch schalen om te schalen op basis van de lengte van de wachtrij met aanvraagberichten. De scaler is erop gericht om één replica van de service te onderhouden voor elk N-bericht in de wachtrij (afgerond).

De referentie-implementatie maakt bijvoorbeeld gebruik van de Service Bus KEDA-schaalfunctie om container-app automatisch te schalen op basis van de lengte van de Service Bus-wachtrij. De regel voor schalen, benoemd service-bus-queue-length-rule, past het aantal servicereplica's aan, afhankelijk van het aantal berichten in de opgegeven Service Bus-wachtrij. De messageCount parameter is ingesteld op 10, wat betekent dat de scaler één replica toevoegt voor elke 10 berichten in de wachtrij. De maximumreplica's (max_replicas) zijn ingesteld op 10 en minimale replica's zijn impliciet 0, tenzij deze worden overschreven, waardoor de service omlaag kan schalen naar nul wanneer er geen berichten in de wachtrij staan. De verbindingsreeks voor de Service Bus-wachtrij wordt veilig opgeslagen als een geheim in Azure, met de naam azure-servicebus-connection-string, dat wordt gebruikt om de scaler te verifiëren bij de Service Bus.

    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"
      }
    }

Service-implementatie containeriseren

Containerisatie betekent dat alle afhankelijkheden voor de te functioneren app worden ingekapseld in een lichtgewicht installatiekopie die betrouwbaar kan worden geïmplementeerd op een breed scala aan hosts. Volg deze aanbevelingen om de implementatie in containers te plaatsen:

  • Domeingrenzen identificeren. Begin met het identificeren van de domeingrenzen binnen uw monolithische toepassing. Hiermee kunt u bepalen welke onderdelen van de toepassing u in afzonderlijke services kunt extraheren.

  • Docker-installatiekopieën maken. Wanneer u Docker-installatiekopieën voor uw Java-services maakt, gebruikt u officiële OpenJDK-basisinstallatiekopieën. Deze installatiekopieën bevatten alleen de minimale set pakketten die nodig zijn om Java uit te voeren, waardoor zowel de pakketgrootte als het kwetsbaarheid voor aanvallen wordt geminimaliseerd.

  • Gebruik Dockerfiles met meerdere fasen. Gebruik een Dockerfiles met meerdere fasen om buildtime-assets te scheiden van de runtimecontainerinstallatiekopieën. Het helpt om uw productie-installatiekopieën klein en veilig te houden. U kunt ook een vooraf geconfigureerde buildserver gebruiken en het JAR-bestand kopiëren naar de containerinstallatiekopie.

  • Uitvoeren als een niet-basisgebruiker. Voer uw Java-containers uit als een niet-basisgebruiker (via gebruikersnaam of UID, $APP_UID) om te voldoen aan het principe van minimale bevoegdheden. Hiermee worden de mogelijke effecten van een geïnfecteerde container beperkt.

  • Luister op poort 8080. Wanneer u als niet-basisgebruiker werkt, configureert u uw toepassing om te luisteren op poort 8080. Het is een algemene conventie voor niet-basisgebruikers.

  • Afhankelijkheden inkapselen. Zorg ervoor dat alle afhankelijkheden voor de te functioneren app zijn ingekapseld in de Docker-containerinstallatiekopieën. Dankzij inkapseling kan de app betrouwbaar worden geïmplementeerd op een breed scala aan hosts.

  • Kies de juiste basisafbeeldingen. De basisinstallatiekopieën die u kiest, zijn afhankelijk van uw implementatieomgeving. Als u bijvoorbeeld implementeert in Container Apps, moet u Linux Docker-installatiekopieën gebruiken.

De referentie-implementatie demonstreert een Docker-buildproces voor het containeriseren van een Java-toepassing. Dit Dockerfile maakt gebruik van een build met één fase met de OpenJDK-basisinstallatiekopieën (mcr.microsoft.com/openjdk/jdk:17-ubuntu), die de benodigde Java Runtime-omgeving biedt.

Het Dockerfile bevat de volgende stappen:

  1. Volumedeclaratie: er wordt een tijdelijk volume (/tmp) gedefinieerd, waardoor tijdelijke bestandsopslag gescheiden is van het hoofdbestandssysteem van de container.
  2. Artefacten kopiëren: het JAR-bestand van de toepassing (email-processor.jar) wordt gekopieerd naar de container, samen met de Application Insights-agent (applicationinsights-agent.jar) voor bewaking.
  3. Het invoerpunt instellen: de container is geconfigureerd om de toepassing uit te voeren waarvoor de Application Insights-agent is ingeschakeld, met behulp van het bewaken java -javaagent van de toepassing tijdens runtime.

Met dit Dockerfile blijft de installatiekopieën leunen door alleen runtime-afhankelijkheden op te sommen, die geschikt zijn voor implementatieomgevingen zoals Container Apps, die ondersteuning bieden voor op Linux gebaseerde containers.

# Use 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 entry point to run the application with the Application Insights agent
ENTRYPOINT ["java", "-javaagent:applicationinsights-agent.jar", "-jar", "/app.jar"]

De referentie-implementatie implementeren

Implementeer de referentie-implementatie van het moderne web-app-patroon voor Java. Er zijn instructies voor zowel de ontwikkeling als de productie-implementatie in de opslagplaats. Nadat u de implementatie hebt uitgevoerd, kunt u ontwerppatronen simuleren en observeren.

Diagram met de architectuur van de referentie-implementatie.Afbeelding 3. Architectuur van de referentie-implementatie. Download een Visio-bestand van deze architectuur.