Bearbeiten

Freigeben über


Modernes Web-App-Muster für .NET

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

In diesem Artikel erfahren Sie, wie Sie das moderne Web-App-Muster implementieren. Das moderne Web-App-Muster definiert, wie Sie Web-Apps in der Cloud modernisieren und eine dienstorientierte Architektur einführen sollten. Das Modern Web App-Muster bietet präskriptive Architektur, Code und Konfigurationsanleitungen, die den Prinzipien des Azure Well-Architected Framework entsprechen und auf dem zuverlässigen Web App-Muster basieren.

Gründe für die Verwendung des modernen Web-App-Musters

Das moderne Web-App-Muster hilft bei der Optimierung von Bereichen mit hoher Nachfrage in einer Web-App. Es bietet detaillierte Anleitungen, um diese Bereiche zu entkoppeln und eine unabhängige Skalierung für die Kostenoptimierung zu ermöglichen. Mit diesem Ansatz können Sie dedizierte Ressourcen kritischen Komponenten zuordnen und die Gesamtleistung verbessern. Die Entkopplung von separierbaren Diensten kann die Zuverlässigkeit verbessern, indem verhindert wird, dass Verlangsamungen in einem Teil der App andere Teile beeinträchtigen. Die Entkoppelung ermöglicht auch die unabhängige Versionsverwaltung einzelner App-Komponenten.

Implementieren des modernen Web-App-Musters

Dieser Artikel enthält Architektur-, Code- und Konfigurationsinformationen zum Implementieren des modernen Web-App-Musters. Verwenden Sie die folgenden Links, um zu den benötigten Anleitungen zu navigieren:

  • Architekturleitfaden: Erfahren Sie, wie Sie Web-App-Komponenten modularisieren und geeignete Plattform als Dienstlösungen (PaaS) auswählen.
  • Anleitung zu Code: Implementieren Sie vier Entwurfsmuster zur Optimierung der entkoppelten Komponenten: Strangler Fig, Warteschlangenbasiertes Lastenausgleich, Konkurrierende Verbraucher und Muster für Integritätsendpunktüberwachung.
  • Anleitung zur Konfiguration: Konfigurieren Sie die Authentifizierung, Autorisierung, automatische Skalierung und Containerisierung für die entkoppelten Komponenten.

Tipp

GitHub-Logo Es gibt eine Referenzimplementierung (Beispiel-App) des modernen Web-App-Musters. Sie stellt den Endzustand der Implementierung der modernen Web-App dar. Es handelt sich um eine Web-App mit Produktionsniveau, die alle in diesem Artikel beschriebenen Code-, Architektur- und Konfigurationsupdates enthält. Stellen Sie die Referenzimplementierung bereit, um die Implementierung des modernen Web-App-Musters anzuleiten.

Anleitung zur Architektur

Das moderne Web-App-Muster basiert auf dem zuverlässigen Web-App-Muster. Für die Implementierung sind einige zusätzliche Architekturkomponenten erforderlich. Sie benötigen eine Nachrichtenwarteschlange, eine Containerplattform, einen entkoppelten Dienstdatenspeicher und eine Containerregistrierung (siehe Abbildung 1).

Diagramm mit der Basisarchitektur des modernen Web-App-Musters.Abbildung 1. Grundlegende Architekturelemente des modernen Web-App-Musters.

Für ein höheres Ziel auf Dienstebene (SLO) können Sie Ihrer Web-App-Architektur eine zweite Region hinzufügen. Für eine zweite Region müssen Sie den Lastenausgleich so konfigurieren, dass der Datenverkehr an die zweite Region weitergeleitet wird, um eine aktive oder passive Konfiguration zu unterstützen. Verwenden Sie eine Hub-and-Spoke-Netzwerktopologie, um freigegebene Ressourcen wie eine Netzwerkfirewall zu zentralisieren und gemeinsam zu nutzen. Greifen Sie über das virtuelle Hubnetzwerk auf das Container-Repository zu. Wenn Sie über virtuelle Computer verfügen, fügen Sie dem virtuellen Hubnetzwerk einen Bastionhost hinzu, um sie sicher zu verwalten (siehe Abbildung 2).

Diagramm mit der Architektur des modernen Web-App-Musters mit zweiter Region und Hub-and-Spoke-Netzwerktopologie.Abbildung 2. Die Architektur des modernen Web-App-Musters mit zweiter Region und Hub-and-Spoke-Netzwerktopologie.

Entkopplungsarchitektur

Um das moderne Web-App-Muster zu implementieren, müssen Sie die vorhandene Web-App-Architektur entkoppeln. Die Entkoppelung der Architektur umfasst das Aufteilen einer monolithischen Anwendung in kleinere, unabhängige Dienste, die jeweils für ein bestimmtes Feature oder eine bestimmte Funktionalität verantwortlich sind. Dieser Prozess beinhaltet die Auswertung der aktuellen Web-App, das Ändern der Architektur und schließlich das Extrahieren des Web-App-Codes in eine Containerplattform. Ziel ist es, Anwendungsdienste systematisch zu identifizieren und zu extrahieren, die von der entkoppelten Entkoppelung am meisten profitieren. Befolgen Sie die folgenden Empfehlungen, um Ihre Architektur zu entkoppeln:

  • Identifizieren sie Dienstgrenzen. Wenden Sie domänengesteuerte Designprinzipien an, um gebundene Kontexte innerhalb Ihrer monolithischen Anwendung zu identifizieren. Jeder gebundene Kontext stellt eine logische Grenze dar und kann ein Kandidat für einen separaten Dienst sein. Dienste, die unterschiedliche Geschäftsfunktionen darstellen und weniger Abhängigkeiten aufweisen, sind gute Kandidaten für die Entkoppelung.

  • Evaluieren Sie die Dienstvorteile. Konzentrieren Sie sich auf Dienste, die am meisten von der unabhängigen Skalierung profitieren. Die Entkopplung dieser Dienste und die Umstellung von synchronen auf asynchrone Verarbeitungsaufgaben ermöglicht ein effizienteres Ressourcenmanagement, unterstützt unabhängige Bereitstellungen und verringert das Risiko, dass andere Teile der Anwendung bei Aktualisierungen oder Änderungen beeinträchtigt werden. Sie können z. B. das Bestellchecken von der Auftragsverarbeitung trennen.

  • Bewerten Sie die technische Machbarkeit. Untersuchen Sie die aktuelle Architektur, um technische Einschränkungen und Abhängigkeiten zu identifizieren, die sich auf den Entkopplungsprozess auswirken können. Planen Sie, wie Daten über Dienste hinweg verwaltet und gemeinsam genutzt werden. Entkoppelte Dienste sollten ihre eigenen Daten verwalten und den direkten Datenbankzugriff über Dienstgrenzen hinweg minimieren.

  • Stellen Sie Azure-Dienste bereit. Wählen Sie die Azure-Dienste aus, die Sie benötigen, um den zu extrahierenden Web-App-Dienst zu unterstützen, und stellen Sie sie bereit. Eine Anleitung dazu finden Sie im Abschnitt Auswählen des richtigen Azure-Dienstes.

  • Decouple Web App-Dienste. Definieren Sie klare Schnittstellen und APIs für die neu extrahierten Web-App-Dienste für die Interaktion mit anderen Teilen des Systems. Entwerfen Sie eine Datenverwaltungsstrategie, mit der jeder Dienst seine eigenen Daten verwalten kann und gleichzeitig Konsistenz und Integrität gewährleistet. Spezifische Implementierungsstrategien und Entwurfsmuster, die während dieses Extraktionsprozesses verwendet werden sollen, finden Sie im Abschnitt Anleitung zum Code.

  • Verwenden Sie einen unabhängigen Speicher für entkoppelte Dienste. Jeder entkoppelte Dienst sollte über einen eigenen isolierten Datenspeicher verfügen, um eine unabhängige Versionsverwaltung, Bereitstellung, Skalierbarkeit und Verwaltung der Datenintegrität zu ermöglichen. Die Referenzimplementierung trennt beispielsweise den Ticketrenderingdienst von der Web-API und beseitigt die Notwendigkeit, dass der Dienst auf die API-Datenbank zugreifen kann. Stattdessen kommuniziert der Dienst die URL, in der Ticketbilder über eine Azure Service Bus-Nachricht zurück zur Web-API generiert wurden, und die API behält den Pfad zu ihrer Datenbank bei.

  • Implementieren Sie separate Bereitstellungspipelines für jeden entkoppelten Dienst. Durch separate Bereitstellungspipelines kann jeder Dienst in seinem eigenen Tempo aktualisiert werden. Wenn verschiedene Teams oder Organisationen innerhalb Ihres Unternehmens unterschiedliche Dienste besitzen, gibt eine separate Bereitstellungspipeline jedem Team die Kontrolle über seine eigenen Bereitstellungen. Verwenden Sie fortlaufende Integrations- und Continuous Delivery-Tools (CI/CD), z. B. Jenkins, GitHub-Aktionen oder Azure-Pipelines, um diese Pipelines einzurichten.

  • Überarbeiten Sie die Sicherheitskontrollen. Stellen Sie sicher, dass Ihre Sicherheitssteuerelemente aktualisiert werden, um die neue Architektur zu berücksichtigen, einschließlich Firewallregeln und Zugriffssteuerungen.

Auswählen der richtigen Azure-Dienste

Konsultieren Sie für jeden Azure-Dienst in Ihrer Architektur die entsprechende Anleitung zum Azure-Dienst im Well-Architected Framework. Für das moderne Web-App-Muster benötigen Sie ein Messaging-System, das asynchrones Messaging unterstützt, eine Anwendungsplattform, die Containerisierung unterstützt, und ein Container-Image-Repository.

  • Wählen Sie eine Nachrichtenwarteschlange aus. Eine Nachrichtenwarteschlange ist ein wichtiger Bestandteil dienstorientierter Architekturen. Sie entkoppelt Nachrichtensender und Empfänger, um asynchrones Messaging zu aktivieren. Verwenden Sie die Anleitungen zum Auswählen eines Azure-Messagingdiensts, um ein Azure-Messagingsystem auszuwählen, das Ihre Entwurfsanforderungen unterstützt. Azure verfügt über drei Messagingdienste: Azure Event Grid, Azure Event Hubs und Service Bus. Beginnen Sie mit Service Bus als Standardauswahl, und verwenden Sie die anderen beiden Optionen, wenn Service Bus Ihre Anforderungen nicht erfüllt.

    Dienst Anwendungsfall
    Service Bus Wählen Sie Service Bus für eine zuverlässige, sortierte und möglicherweise transaktionsbezogene Zustellung von hochwertigen Nachrichten in Unternehmensanwendungen aus.
    Event Grid Wählen Sie "Ereignisraster" aus, wenn Sie eine große Anzahl diskreter Ereignisse effizient behandeln müssen. Ereignisraster ist für ereignisgesteuerte Anwendungen skalierbar, bei denen viele kleine, unabhängige Ereignisse (z. B. Ressourcenstatusänderungen) in einem Modell mit geringer Latenz an Abonnenten weitergeleitet werden müssen.
    Event Hubs Wählen Sie Event Hubs für eine massive Datenaufnahme mit hohem Durchsatz aus – z. B. Telemetrie, Protokolle oder Echtzeitanalysen. Event Hubs sind für Streamingszenarien optimiert, in denen Massendaten kontinuierlich aufgenommen und verarbeitet werden müssen.
  • Implementieren Sie einen Containerdienst. Für die Teile Ihrer Anwendung, die Sie containerisieren möchten, benötigen Sie eine Anwendungsplattform, die Container unterstützt. Verwenden Sie die Anleitung zum Auswählen eines Azure-Containerdiensts, um Ihre Entscheidung zu treffen. Azure verfügt über drei Prinzipalcontainerdienste: Azure Container Apps, Azure Kubernetes Service (AKS) und Azure-App Service. Beginnen Sie mit Container-Apps als Standardauswahl, und verwenden Sie die anderen beiden Optionen, wenn Container-Apps Ihre Anforderungen nicht erfüllen.

    Dienst Anwendungsfall
    Container-Apps Wählen Sie Container-Apps aus, wenn Sie eine serverlose Plattform benötigen, die Container in ereignisgesteuerten Anwendungen automatisch skaliert und verwaltet.
    AKS Wählen Sie AKS aus, wenn Sie eine detaillierte Kontrolle über Kubernetes-Konfigurationen und erweiterte Features für Skalierung, Netzwerk und Sicherheit benötigen.
    Web-Apps für Container Wählen Sie Web App für Container im App-Dienst für die einfachste PaaS-Oberfläche aus.
  • Implementieren Sie ein Container-Repository. Wenn Sie einen containerbasierten Computedienst verwenden, müssen Sie über ein Repository verfügen, um die Containerimages zu speichern. Sie können eine öffentliche Containerregistrierung wie Docker Hub oder eine verwaltete Registrierung wie Azure Container Registry verwenden. Verwenden Sie die Einführung in Containerregistrierungen in Azure , um Ihre Entscheidung zu treffen.

Anleitung zum Code

Um einen unabhängigen Dienst erfolgreich zu entkoppeln und zu extrahieren, müssen Sie Ihren Web-App-Code mit den folgenden Entwurfsmustern aktualisieren: Strangler Fig, Warteschlangenbasierter Lastenabgleich, Konkurrierende Verbraucher, Überwachung der Integrität von Endpunkten und das Wiederholungsmuster.

Diagramm, das die Rolle der Entwurfsmuster in der Architektur des modernen Web-App-Musters zeigt.Abbildung 3. Rolle der Entwurfsmuster.

  1. Strangler Fig-Muster: Das Strangler Fig-Muster migriert Funktionen inkrementell von einer monolithischen Anwendung zum entkoppelten Dienst. Implementieren Sie dieses Muster in der Hauptweb-App, um die Funktionalität schrittweise auf unabhängige Dienste zu migrieren, indem Sie den Datenverkehr basierend auf Endpunkten leiten.

  2. Warteschlangenbasiertes Lastenausgleichsmuster: Das Muster für das warteschlangenbasierte Lastenausgleich verwaltet den Nachrichtenfluss zwischen dem Produzenten und dem Consumer mithilfe einer Warteschlange als Puffer. Implementieren Sie dieses Muster im Produzententeil des entkoppelten Diensts, um den Nachrichtenfluss asynchron mithilfe einer Warteschlange zu verwalten.

  3. Konkurrierende Verbraucher: Das Muster konkurrierender Verbraucher ermöglicht es mehreren Instanzen des entkoppelten Dienstes, unabhängig voneinander aus derselben Nachrichtenwarteschlange zu lesen und um die Verarbeitung von Nachrichten zu konkurrieren. Implementieren Sie dieses Muster im entkoppelten Dienst, um Aufgaben über mehrere Instanzen hinweg zu verteilen.

  4. Überwachung der Integrität von Endpunkten: Das Muster der Überwachung der Integrität von Endpunkten macht Endpunkte zur Überwachung des Status und der Integrität verschiedener Teile der Web-App verfügbar. (4a) Implementieren Sie dieses Muster in der Hauptweb-App. (4b) Implementieren Sie sie auch im entkoppelten Dienst, um den Status von Endpunkten nachzuverfolgen.

  5. Wiederholungsmuster: Das Wiederholungsmuster behandelt vorübergehende Fehler durch Wiederholungsvorgänge, die zeitweise fehlschlagen können. (5a) Implementieren Sie dieses Muster für alle ausgehenden Aufrufe an andere Azure-Dienste in der Hauptweb-App, z. B. Aufrufe an die Nachrichtenwarteschlange und die privaten Endpunkte. (5b) Implementieren Sie dieses Muster auch im entkoppelten Dienst, um vorübergehende Fehler bei Aufrufen an die privaten Endpunkte zu behandeln.

Jedes Entwurfsmuster bietet Vorteile, die an einer oder mehreren Säulen des Well-Architected Framework ausgerichtet sind (siehe folgende Tabelle).

Entwurfsmuster Implementierungsspeicherort Zuverlässigkeit (Reliability, RE) Sicherheit (Security, SE) Kostenoptimierung (Cost Optimization, CO) Erstklassige Betriebsprozesse (Operational Excellence, OE) Leistungseffizienz (Performance Efficiency, PE) Unterstützung von Grundsätzen des well-Architected Framework
Strangler-Muster Hauptweb-App RE:08
CO:07
CO:08
OE:06
OE:11
Muster „Warteschlangenbasierter Lastenausgleich“ Hersteller des entkoppelten Dienstes RE:06
RE:07
CO:12
PE:05
Muster mit konkurrierenden Consumern Entkoppelter Dienst RE:05
RE:07
CO:05
CO:07
PE:05
PE:07
Integritätsendpunktüberwachungsmuster Hauptweb-App & entkoppelter Dienst RE:07
RE:10
OE:07
PE:05
Wiederholungsmuster Hauptweb-App & entkoppelter Dienst RE:07

Implementieren des Strangler Fig-Musters

Verwenden Sie das Strangler Fig-Muster, um die Funktionalität schrittweise von der monolithischen Codebasis zu neuen unabhängigen Diensten zu migrieren. Extrahieren Sie die neuen Dienste aus der vorhandenen monolithischen Codebasis, und modernisieren Sie langsam kritische Teile der Web-App. Befolgen Sie die folgenden Empfehlungen, um das Strangler Fig-Muster zu implementieren:

  • Richten Sie eine Routingebene ein. Implementieren Sie in der monolithischen Web-App-Codebasis eine Routingebene, die datenverkehrsbasiert auf Endpunkten leitet. Verwenden Sie die benutzerdefinierte Routinglogik nach Bedarf, um bestimmte Geschäftsregeln für das Weiterleiten von Datenverkehr zu behandeln. Wenn Sie beispielsweise einen /users Endpunkt in Ihrer monolithischen App haben und diese Funktionalität in den entkoppelten Dienst verschoben haben, leitet die Routingebene alle Anforderungen an /users den neuen Dienst weiter.

  • Verwalten Sie den Featurerollout. Verwenden Sie die .NET Feature Management-Bibliotheken, um Feature-Flags und gestaffeltes Rollout zu implementieren, um die entkoppelten Dienste schrittweise einzuführen. Das vorhandene monolithische App-Routing sollte steuern, wie viele Anforderungen die entkoppelten Dienste empfangen. Beginnen Sie mit einem kleinen Prozentsatz an Anfragen und erhöhen Sie die Nutzung im Laufe der Zeit, wenn Sie Vertrauen in die Stabilität und Leistung gewonnen haben. Die Referenzimplementierung extrahiert beispielsweise die Ticketrenderingfunktion in einen eigenständigen Dienst, der schrittweise eingeführt werden kann, um einen größeren Teil der Ticketrenderinganforderungen zu verarbeiten. Wenn der neue Dienst seine Zuverlässigkeit und Leistung unter Beweis gestellt hat, kann er schließlich die gesamte Funktionalität der Ticketausgabe vom Monolithen übernehmen und den Übergang abschließen.

  • Verwenden Sie bei Bedarf einen Fassadenservice. Ein Fassadendienst ist nützlich, wenn eine einzelne Anforderung mit mehreren Diensten interagieren muss, oder wenn Sie die Komplexität des zugrunde liegenden Systems vom Client ausblenden möchten. Wenn der entkoppelte Dienst jedoch keine öffentlich zugänglichen APIs enthält, ist möglicherweise kein Fassadendienst erforderlich. Implementieren Sie in der monolithischen Web-App-Codebasis einen Fassadendienst, um Anforderungen an das entsprechende Back-End (Monolith oder Microservice) weiterzuleiten. Stellen Sie im neuen entkoppelten Dienst sicher, dass er Anforderungen unabhängig verarbeiten kann, wenn über die Fassade zugegriffen wird.

Implementieren des Muster für den wartenschlangenbasierten Lastenausgleich

Implementieren Sie das Warteschlangenbasierte Lastenausgleichsmuster auf dem Produzententeil des entkoppelten Diensts, um Aufgaben asynchron zu verarbeiten, die keine sofortigen Antworten benötigen. Dieses Muster verbessert die Reaktionsfähigkeit und Skalierbarkeit des gesamten Systems, indem eine Warteschlange zum Verwalten der Workloadverteilung verwendet wird. Es ermöglicht dem entkoppelten Dienst das Verarbeiten von Anforderungen mit einer konsistenten Rate. Befolgen Sie die folgenden Empfehlungen, um dieses Muster effektiv zu implementieren:

  • Verwenden Sie nicht blockierende Nachrichtenwarteschlangen. Stellen Sie sicher, dass der Prozess, der Nachrichten an die Warteschlange sendet, keine anderen Prozesse blockiert, während er darauf wartet, dass der entkoppelte Dienst die Nachrichten in der Warteschlange verarbeitet. Wenn der Prozess das Ergebnis des entkoppelten Servicevorgangs erfordert, sollten Sie eine alternative Möglichkeit haben, die Situation zu bewältigen, während Sie auf den Abschluss des in der Warteschlange befindlichen Vorgangs warten. Die Referenzimplementierung verwendet z. B. Service Bus und das await Schlüsselwort zum messageSender.PublishAsync() asynchronen Veröffentlichen von Nachrichten in der Warteschlange, ohne den Thread zu blockieren, der diesen Code ausführt:

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

    Mit diesem Ansatz wird sichergestellt, dass die Hauptanwendung reaktionsfähig bleibt und gleichzeitig andere Aufgaben ausführen kann, während der entkoppelte Dienst die in die Warteschlange gestellten Anforderungen mit überschaubarer Geschwindigkeit verarbeitet.

  • Implementieren Sie den Wiederholungs- und Entfernungsvorgang für Nachrichten. Implementieren Sie einen Mechanismus zum Wiederholen der Verarbeitung von Nachrichten in die Warteschlange, die nicht erfolgreich verarbeitet werden können. Wenn Fehler weiterhin bestehen, sollten diese Nachrichten aus der Warteschlange entfernt werden. Beispielsweise verfügt Service Bus über integrierte Funktionen für wiederholungs- und inaktive Warteschlangen.

  • Konfigurieren Sie die idempotente Nachrichtenverarbeitung. Die Logik, die Nachrichten aus der Warteschlange verarbeitet, muss idempotent sein, um Fälle zu behandeln, in denen eine Nachricht möglicherweise mehrmals verarbeitet wird. Die Referenzimplementierung verwendet ServiceBusClient.CreateProcessor z. B. und AutoCompleteMessages = true ReceiveMode = ServiceBusReceiveMode.PeekLock stellt sicher, dass Nachrichten nur einmal verarbeitet werden und bei Fehlern erneut verarbeitet werden können (siehe den folgenden Code).

    // 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
    });
    
  • Verwalten Sie Änderungen an der Oberfläche. Die asynchrone Verarbeitung kann dazu führen, dass Aufgaben nicht sofort abgeschlossen werden. Benutzer sollten darauf hingewiesen werden, wenn ihre Aufgabe noch bearbeitet wird, um falsche Erwartungen zu vermeiden und Verwirrung zu vermeiden. Verwenden Sie visuelle Hinweise oder Meldungen, um hinzuweisen, dass eine Aufgabe ausgeführt wird. Geben Sie Benutzern die Möglichkeit, Benachrichtigungen zu erhalten, wenn ihre Aufgabe abgeschlossen ist, z. B. in Form einer E-Mail oder Pushbenachrichtigung.

Implementieren des Musters „Konkurrierende Verbraucher“

Implementieren Sie das Muster Konkurrierende Verbraucher in den entkoppelten Diensten, um eingehende Aufgaben aus der Nachrichtenwarteschlange zu verwalten. Dieses Muster umfasst die Verteilung von Aufgaben über mehrere Instanzen entkoppelter Dienste. Diese Dienste verarbeiten Nachrichten aus der Warteschlange, verbessern den Lastenausgleich und erhöhen die Kapazität des Systems zur Behandlung gleichzeitiger Anforderungen. Das Muster „Konkurrierende Verbraucher“ ist wirksam, wenn:

  • Die Abfolge der Nachrichtenverarbeitung ist nicht entscheidend.
  • Die Warteschlange bleibt von nicht wohlgeformten Nachrichten unberührt.
  • Der Verarbeitungsvorgang ist idempotent, d. h. er kann mehrmals angewendet werden, ohne das Ergebnis über die ursprüngliche Anwendung hinaus zu ändern.

Befolgen Sie die folgenden Empfehlungen, um das Muster konkurrierender Verbraucher zu implementieren:

  • Verarbeiten Sie gleichzeitig eingehende Nachrichten. Stellen Sie beim Empfangen von Nachrichten aus einer Warteschlange sicher, dass Ihr System so konzipiert ist, dass mehrere Nachrichten gleichzeitig verarbeitet werden. Setzen Sie die maximale Anzahl gleichzeitiger Aufrufe auf 1, damit jede Nachricht von einem separaten Verbraucher bearbeitet wird.

  • Deaktivieren Sie das Vorabrufen. Deaktivieren Sie das Vorabrufen von Nachrichten, damit Verbraucher Nachrichten nur dann abrufen, wenn sie dazu bereit sind.

  • Verwenden Sie zuverlässige Nachrichtenverarbeitungsmodi. Verwenden Sie einen zuverlässigen Verarbeitungsmodus, z. B. PeekLock (oder seine Entsprechung), mit dem Nachrichten, die bei der Verarbeitung fehlschlagen, automatisch erneut angezeigt werden. Dieser Modus erhöht die Zuverlässigkeit gegenüber Methoden, bei denen zuerst gelöscht wird. Wenn ein Worker eine Nachricht nicht verarbeiten kann, muss ein anderer in der Lage sein, sie ohne Fehler zu verarbeiten, auch wenn die Nachricht mehrmals bearbeitet wird.

  • Implementieren Sie die Fehlerbehandlung. Leiten Sie falsch formatierte oder nicht verarbeitete Nachrichten an eine separate, inaktive Warteschlange weiter. Dieses Design verhindert die wiederholte Verarbeitung. Sie können beispielsweise Ausnahmen während der Nachrichtenverarbeitung abfangen und die problematische Nachricht in die separate Warteschlange verschieben.

  • Bearbeiten Sie nicht ordnungsgemäße Nachrichten. Entwerfen Sie Verbraucher so, dass sie die Nachrichten auch dann verarbeiten, wenn diese nicht in der richtigen Reihenfolge ankommen. Mehrere parallele Verbraucher bedeuten, dass sie Nachrichten möglicherweise nicht mehr gemäß der Reihenfolge verarbeiten.

  • Skalieren Sie basierend auf der Warteschlangenlänge. Dienste, die Nachrichten aus einer Warteschlange verwenden, sollten basierend auf der Warteschlangenlänge automatisch skalieren. Die skalierungsbasierte Autoskalierung ermöglicht eine effiziente Verarbeitung von Spitzen eingehender Nachrichten.

  • Verwenden Sie eine Nachrichtenantwortwarteschlange. Wenn das System Benachrichtigungen für die Verarbeitung nach der Nachricht benötigt, richten Sie eine dedizierte Antwort- oder Antwortwarteschlange ein. Mit diesem Setup wird das Operative Messaging von Benachrichtigungsprozessen getrennt.

  • Verwenden Sie zustandslose Dienste. Erwägen Sie die Verwendung zustandsloser Dienste zum Verarbeiten von Anforderungen aus einer Warteschlange. Dies ermöglicht eine einfache Skalierung und eine effiziente Ressourcennutzung.

  • Konfigurieren Sie die Protokollierung. Integration der Protokollierung und spezifischer Ausnahmebehandlung im Nachrichtenverarbeitungsworkflow. Konzentrieren Sie sich auf die Erfassung von Serialisierungsfehlern und das Weiterleiten dieser problematischen Nachrichten auf einen Mechanismus mit Einem Totbuchstaben. Diese Protokolle bieten wertvolle Erkenntnisse für die Problembehandlung.

Die Referenzimplementierung verwendet beispielsweise das Muster "Konkurrierende Verbraucher" für einen zustandslosen Dienst, der in Container-Apps ausgeführt wird, um Ticketrenderinganforderungen aus einer ServiceBus-Warteschlange zu verarbeiten. Dieser konfiguriert einen Warteschlangenprozessor mit:

  • AutoCompleteMessages: Schließt Nachrichten automatisch ab, wenn sie ohne Fehler verarbeitet werden.
  • ReceiveMode: Verwendet den PeekLock-Modus und stellt Nachrichten erneut zu, wenn sie nicht verarbeitet wurden.
  • MaxConcurrentCalls: Stellen Sie den Wert auf 1, damit jeweils eine Nachricht verarbeitet wird.
  • PrefetchCount: Legen Sie diesen Wert auf 0, um das Vorabrufen von Nachrichten zu vermeiden.

Der Prozessor protokolliert Nachrichtenverarbeitungsdetails, die zur Problembehandlung und Überwachung dienen. Er erfasst Deserialisierungsfehler und leitet ungültige Nachrichten an eine Dead-Letter-Warteschlange weiter, wodurch die wiederholte Verarbeitung fehlerhafter Nachrichten verhindert wird. Der Dienst wird auf der Containerebene skaliert und ermöglicht eine effiziente Behandlung von Nachrichtenspitzen basierend auf der Warteschlangenlänge.

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

Implementieren des Musters für Überwachung der Integrität von Endpunkten

Implementieren Sie das Muster für Überwachung der Integrität von Endpunkten im Haupt-App-Code und dem entkoppelten Dienstcode, um den Status von Anwendungsendpunkten nachzuverfolgen. Orchestratoren wie AKS oder Container-Apps können diese Endpunkte abfragen, um den Dienststatus zu überprüfen und fehlerhafte Instanzen neu zu starten. ASP.NET Core-Apps können Middleware für die dedizierte Integritätsprüfung hinzufügen, um Endpunktintegritätsdaten und wichtige Abhängigkeiten effizient zu bedienen. Befolgen Sie die folgenden Empfehlungen, um das Muster für Überwachung der Integrität von Endpunkten zu implementieren:

  • Implementieren Sie die Integritätsprüfungen. Verwenden Sie ASP.NET Core-Middleware für die Integritätsprüfung, um Endpunkte für die Integritätsprüfung bereitzustellen.

  • Überprüfen Sie Abhängigkeiten. Stellen Sie sicher, dass die Integritätsprüfungen die Verfügbarkeit wichtiger Abhängigkeiten überprüfen, z. B. die Datenbank, den Speicher und das Messagingsystem. Das Nicht-Microsoft-Paket AspNetCore.Diagnostics.HealthChecks kann Abhängigkeitsprüfungen für viele gängige App-Abhängigkeiten implementieren.

    Die Referenzimplementierung verwendet z. B. ASP.NET Core Health Check Middleware, um Endpunkte zur Integritätsprüfung mithilfe der AddHealthChecks()-Methode für das builder.Services-Objekt verfügbar zu machen. Der Code überprüft die Verfügbarkeit von Schlüsselabhängigkeiten, Azure Blob Storage und Service Bus-Warteschlange mit den AddAzureBlobStorage() Methoden und AddAzureServiceBusQueue() Methoden, die Teil des AspNetCore.Diagnostics.HealthChecks Pakets sind. Container-Apps ermöglichen die Konfiguration von Integritätssonden , die überwacht werden, um zu messen, ob Apps fehlerfrei oder recyclingbedürftigend sind.

    // 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");
    
  • Konfigurieren Sie Azure-Ressourcen. Konfigurieren Sie die Azure-Ressourcen, um die Integritätsprüfungs-URLs der App zu verwenden, um die Liveität und Bereitschaft zu bestätigen. Beispielsweise verwendet die Referenzimplementierung Bicep, um die Integritätsprüfungs-URLs zu konfigurieren, um die Aktivität und Bereitschaft der Azure-Ressource zu bestätigen. Ein Livetest des /health-Endpunkts alle 10 Sekunden nach einer anfänglichen Verzögerung von 2 Sekunden ausgeführt wird.

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

Implementieren des Wiederholungsmusters

Das Wiederholungsmuster ermöglicht es Anwendungen, sich von vorübergehenden Fehlern zu erholen. Das Wiederholungsmuster ist für das zuverlässige Web-App-Muster zentral, sodass Ihre Web-App bereits das Wiederholungsmuster verwenden sollte. Wenden Sie das Wiederholungsmuster auf Anforderungen an die Messagingsysteme und Anforderungen an, die von den entkoppelten Diensten ausgegeben werden, die Sie aus der Web-App extrahieren. Befolgen Sie die folgenden Empfehlungen, um das Wiederholungsmuster zu implementieren:

  • Konfigurieren Sie Wiederholungsoptionen. Stellen Sie bei der Integration in eine Nachrichtenwarteschlange sicher, dass Sie den Client konfigurieren, der für Interaktionen mit der Warteschlange mit den entsprechenden Wiederholungseinstellungen verantwortlich ist. Geben Sie die Parameter an, z. B. die maximale Anzahl von Wiederholungen, Verzögerung zwischen den Wiederholungen und die maximale Verzögerung.

  • Verwenden Sie das exponentielle Backoff. Implementieren Sie eine exponentielle Backoffstrategie für Wiederholungsversuche. Dies bedeutet, dass die Zeit zwischen jedem Wiederholungsvorgang exponentiell erhöht wird, was dazu beiträgt, die Auslastung des Systems in Zeiten hoher Fehlerraten zu verringern.

  • Verwenden Sie die SDK-Wiederholungsfunktion. Verwenden Sie für Dienste mit spezialisierten SDKs wie Service Bus oder Blob Storage die integrierten Wiederholungsmechanismen. Die integrierten Wiederholungsmechanismen sind für die typischen Anwendungsfälle des Diensts optimiert und können Wiederholungsversuche effektiver verarbeiten, wobei weniger Konfiguration erforderlich ist. Die Referenzimplementierung verwendet z. B. die integrierte Wiederholungsfunktionalität des Service Bus SDK (ServiceBusClient und ServiceBusRetryOptions). Das ServiceBusRetryOptions-Objekt ruft Einstellungen ab, um Einstellungen aus MessageBusOptions abzurufen und Wiederholungseinstellungen wie MaxRetries, Delay, MaxDelay und TryTimeout zu konfigurieren.

    // 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);
    });
    
  • Übernehmen Sie Standardresilienzbibliotheken für HTTP-Clients. Integrieren Sie für die HTTP-Kommunikation eine Standardresilienzbibliothek wie Polly oder Microsoft.Extensions.Http.Resilience. Diese Bibliotheken bieten umfassende Wiederholungsmechanismen, die für die Verwaltung der Kommunikation mit externen Webdiensten von entscheidender Bedeutung sind.

  • Behandeln Sie die Nachrichtensperre. Implementieren Sie für nachrichtenbasierte Systeme Strategien zur Nachrichtenverarbeitung, die Wiederholungsversuche ohne Datenverlust unterstützen, z. B. mithilfe von „Peek Lock“-Modi, sofern verfügbar. Stellen Sie sicher, dass fehlgeschlagene Nachrichten effektiv wiederholt und nach wiederholten Fehlern in eine Warteschleife verschoben werden.

Implementieren der verteilten Ablaufverfolgung

Da Anwendungen dienstorientiert werden und ihre Komponenten entkoppelt sind, ist die Überwachung des Ausführungsflusses zwischen den Diensten von entscheidender Bedeutung. Das Modern Web App-Muster verwendet Application Insights und Azure Monitor für die Sichtbarkeit der Anwendungsintegrität und -leistung über OpenTelemetry-APIs, die die verteilte Ablaufverfolgung unterstützen.

Die verteilte Ablaufverfolgung verfolgt eine Benutzeranforderung, während sie mehrere Dienste durchläuft. Wenn eine Anforderung empfangen wird, wird sie mit einem Ablaufverfolgungsbezeichner gekennzeichnet, der über HTTP-Header an andere Komponenten übergeben wird, und service bus-Eigenschaften während des Aufrufs von Abhängigkeiten. Ablaufverfolgungen und Protokolle enthalten dann sowohl den Ablaufverfolgungsbezeichner als auch einen Aktivitätsbezeichner (oder einen Span-Bezeichner), der der spezifischen Komponente und der übergeordneten Aktivität entspricht. Überwachungstools wie Application Insights verwenden es, um eine Struktur von Aktivitäten und Protokollen über verschiedene Dienste hinweg anzuzeigen, die für die Überwachung verteilter Anwendungen von entscheidender Bedeutung sind.

  • Installieren Sie OpenTelemetry-Bibliotheken. Verwenden Sie Instrumentationsbibliotheken, um die Ablaufverfolgung und Metriken aus allgemeinen Komponenten zu aktivieren. Fügen Sie bei Bedarf benutzerdefinierte Instrumentierung mit System.Diagnostics.ActivitySource System.Diagnostics.Activity hinzu. Verwenden Sie Exporterbibliotheken, um die OpenTelemetry-Diagnose zu überwachen und sie in beständigen Speicher aufzuzeichnen. Verwenden Sie vorhandene Exporteure, oder erstellen Sie eigene mit System.Diagnostics.ActivityListener.

  • Richten Sie OpenTelemetry ein. Verwenden Sie die Azure Monitor-Verteilung von OpenTelemetry (Azure.Monitor.OpenTelemetry.AspNetCore). Stellen Sie sicher, dass die Diagnose in Application Insights exportiert wird und die integrierte Instrumentierung für allgemeine Metriken, Ablaufverfolgungen, Protokolle und Ausnahmen von der .NET-Runtime und ASP.NET Core enthält. Schließen Sie weitere OpenTelemetry-Instrumentierungspakete für SQL-, Redis- und Azure SDK-Clients ein.

  • Überwachung und Analyse Stellen Sie nach der Konfiguration sicher, dass Protokolle, Ablaufverfolgungen, Metriken und Ausnahmen erfasst und an Application Insights gesendet werden. Stellen Sie sicher, dass Ablaufverfolgungs-, Aktivitäts- und übergeordnete Aktivitätsbezeichner enthalten sind, sodass Application Insights die Sichtbarkeit der End-to-End-Ablaufverfolgung über HTTP- und Service Bus-Grenzen hinweg ermöglicht. Verwenden Sie dieses Setup, um die Aktivitäten Ihrer Anwendung über Dienste hinweg effektiv zu überwachen und zu analysieren.

Im Beispiel der modernen Web-App wird die Azure Monitor-Verteilung von OpenTelemetry (Azure.Monitor.OpenTelemetry.AspNetCore) verwendet. Weitere Instrumentierungspakete werden für SQL-, Redis- und Azure SDK-Clients verwendet. OpenTelemetry ist im Modern Web App-Beispiel-Ticketrenderingdienst wie folgt konfiguriert:

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

Die builder.Logging.AddOpenTelemetry-Methode leitet die gesamte Protokollierung über OpenTelemetry weiter und stellt eine konsistente Ablaufverfolgung und Protokollierung für die gesamte Anwendung sicher. Durch die Registrierung von OpenTelemetry-Diensten bei builder.Services.AddOpenTelemetryist die Anwendung für das Sammeln und Exportieren von Diagnosen eingerichtet, die dann über UseAzureMonitorApplication Insights an Application Insights gesendet werden. Darüber hinaus wird die Clientinstrumentation für Komponenten wie Service Bus und HTTP-Clients über WithMetrics und WithTracingkonfiguriert, wodurch automatische Metriken und Ablaufverfolgungssammlungen aktiviert werden, ohne dass Änderungen an der vorhandenen Clientnutzung erforderlich sind, nur eine Aktualisierung der Konfiguration.

Anleitung zur Konfiguration

In den folgenden Abschnitten finden Sie Anleitungen zum Implementieren der Konfigurationsupdates. Jeder Abschnitt richtet sich an einer oder mehreren Säulen des Well-Architected Frameworks aus.

Konfiguration Zuverlässigkeit (Reliability, RE) Sicherheit (Security, SE) Kostenoptimierung (Cost Optimization, CO) Erstklassige Betriebsprozesse (Operational Excellence, OE) Leistungseffizienz (Performance Efficiency, PE) Unterstützung von Grundsätzen des well-Architected Framework
Konfigurieren der Authentifizierung und Autorisierung SE:05
OE:10
Implementieren der unabhängigen automatischen Skalierung RE:06
CO:12
PE:05
Containerisieren der Dienstbereitstellung CO:13
PE:09
PE:03

Konfigurieren der Authentifizierung und Autorisierung

Befolgen Sie die folgenden Empfehlungen, um die Authentifizierung und Autorisierung für alle neuen Azure-Dienste (Workloadidentitäten) zu konfigurieren, die Sie der Web-App hinzufügen:

  • Verwenden Sie verwaltete Identitäten für jeden neuen Dienst. Jeder unabhängige Dienst sollte über eine eigene Identität verfügen und verwaltete Identitäten für die Dienst-zu-Dienst-Authentifizierung verwenden. Verwaltete Identitäten machen die Verwaltung von Anmeldeinformationen in Ihrem Code überflüssig und verringern das Risiko von Datenlecks. Sie helfen Ihnen, vertrauliche Informationen wie Verbindungszeichenfolgen in Ihren Code- oder Konfigurationsdateien zu vermeiden.

  • Gewähren Sie jedem neuen Dienst die geringsten Berechtigungen. Weisen Sie jeder neuen Dienstidentität nur erforderliche Berechtigungen zu. Wenn beispielsweise eine Identität nur an eine Containerregistrierung übertragen werden muss, erteilen Sie ihr keine Pullberechtigungen. Überprüfen Sie diese Berechtigungen regelmäßig, und passen Sie sie bei Bedarf an. Verwenden Sie unterschiedliche Identitäten für unterschiedliche Rollen, z. B. Bereitstellung und die Anwendung. Dies begrenzt den potenziellen Schaden, wenn eine Identität kompromittiert wird.

  • Verwenden Sie Infrastruktur als Code (Infrastructure as Code, IaC). Verwenden Sie Bicep oder ähnliche IaC-Tools, um Ihre Cloudressourcen zu definieren und zu verwalten. IaC stellt eine konsistente Anwendung von Sicherheitskonfigurationen in Ihren Bereitstellungen sicher und ermöglicht es Ihnen, die Infrastruktureinrichtung zu steuern.

Befolgen Sie die folgenden Empfehlungen, um die Authentifizierung und Autorisierung für Benutzer (Benutzeridentitäten) zu konfigurieren:

  • Gewähren Sie Benutzern die geringsten Berechtigungen. Stellen Sie genau wie bei Diensten sicher, dass Benutzern nur die Berechtigungen erteilt werden, die sie zum Ausführen ihrer Aufgaben benötigen. Überprüfen und passen Sie diese Berechtigungen regelmäßig an.

  • Führen Sie regelmäßige Sicherheitsüberprüfungen durch. Überprüfen Sie regelmäßig Ihre Sicherheitseinrichtung. Suchen Sie nach Fehlkonfigurationen oder unnötigen Berechtigungen, und korrigieren Sie sie sofort.

Die Referenzimplementierung verwendet IaC, um verwaltete Identitäten hinzugefügten Diensten und bestimmten Rollen jeder Identität zuzuweisen. Er definiert Rollen und Berechtigungen für den Bereitstellungszugriff (containerRegistryPushRoleId), anwendungsbesitzer (containerRegistryPushRoleId) und container-Apps () (containerRegistryPullRoleIdsiehe den folgenden Code).

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
    }
\]

Die Referenzimplementierung weist die verwaltete Identität als neue Container-Apps-Identität bei der Bereitstellung zu (siehe den folgenden Code).

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

Konfigurieren der unabhängigen automatischen Skalierung

Das moderne Web-App-Muster beginnt mit dem Aufbrechen der monolithischen Architektur und führt die Dienstentkopplung ein. Wenn Sie eine Web-App-Architektur entkoppeln, können Sie entkoppelte Dienste unabhängig voneinander skalieren. Die Skalierung der Azure-Dienste zur Unterstützung eines unabhängigen Web-App-Dienstes anstelle einer vollständigen Web-App optimiert die Skalierungskosten und erfüllt gleichzeitig die Anforderungen. Befolgen Sie die folgenden Empfehlungen, um Container automatisch zu skalieren:

  • Verwenden Sie zustandslose Dienste. Stellen Sie sicher, dass Ihre Dienste zustandslos sind. Wenn Ihre .NET-Anwendung den In-Process-Sitzungszustand enthält, externalisieren Sie sie in einen verteilten Cache wie Redis oder eine Datenbank wie SQL Server.

  • Konfigurieren Sie die Regeln zur automatischen Skalierung. Verwenden Sie die automatischen Skalierungskonfigurationen, die die kostengünstigste Kontrolle über Ihre Dienste bieten. Bei containerisierten Diensten bietet eine ereignisbasierte Skalierung, wie z. B. Kubernetes Event-Driven Autoscaler (KEDA), oft eine granulare Kontrolle, sodass Sie anhand von Ereignismetriken skalieren können. Container-Apps und AKS unterstützen KEDA. Verwenden Sie für Dienste, die KEDA nicht unterstützen, z . B. App Service, die automatischen Skalierungsfeatures, die von der Plattform selbst bereitgestellt werden. Diese Features umfassen häufig die Skalierung basierend auf metrikbasierten Regeln oder HTTP-Datenverkehr.

  • Konfigurieren Sie Mindestreplikate. Um einen Kaltstart zu verhindern, konfigurieren Sie die Einstellungen für die automatische Skalierung, um mindestens ein Replikat beizubehalten. Ein Kaltstart liegt vor, wenn Sie einen Dienst aus einem angehaltenen Zustand initialisieren, was häufig zu einer verzögerten Reaktion führt. Wenn die Minimierung von Kosten eine Priorität ist und Sie Verzögerungen beim Kaltstart tolerieren können, legen Sie beim Konfigurieren der automatischen Skalierung die Mindestreplikatanzahl auf 0 fest.

  • Konfigurieren Sie eine Abkühlzeit. Wenden Sie eine geeigneten Abkühlzeit an, um eine Verzögerung zwischen Skalierungsereignissen einzuführen. Ziel ist es, übermäßige Skalierungsaktivitäten zu verhindern, die durch temporäre Lastspitzen ausgelöst werden.

  • Konfigurieren Sie die warteschlangenbasierte Skalierung. Wenn Ihre Anwendung eine Nachrichtenwarteschlange wie Service Bus verwendet, konfigurieren Sie Ihre Einstellungen für die automatische Skalierung basierend auf der Länge der Warteschlange mit Anforderungsnachrichten. Die Skalierung zielt darauf ab, ein Replikat des Diensts für alle N-Nachrichten in der Warteschlange zu verwalten (aufgerundet).

Die Referenzimplementierung verwendet z. B. den Service Bus KEDA-Scaler , um die Container-App basierend auf der Länge der Warteschlange zu skalieren. Der service-bus-queue-length-rule Dienst wird basierend auf der Länge einer angegebenen Servicebus-Warteschlange skaliert. Der messageCount-Parameter ist auf 10 festgelegt, sodass die Skalierung ein Dienstreplikat für alle 10 Nachrichten in der Warteschlange hat. Die Parameter scaleMaxReplicas und scaleMinReplicas legen die maximale und minimale Anzahl von Replikaten für den Dienst fest. Der queue-connection-string geheime Schlüssel, der die Verbindungszeichenfolge für die ServiceBus-Warteschlange enthält, wird aus Azure Key Vault abgerufen. Dieser geheime Schlüssel wird verwendet, um die Skalierung beim Service Bus zu authentifizieren.

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

Containerisieren der Dienstbereitstellung

Containerisierung bedeutet, dass alle Abhängigkeiten, die für die Funktion der App erforderlich sind, in einem schlanken Image zusammengefasst werden, das zuverlässig auf einer Vielzahl von Hosts bereitgestellt werden kann. Befolgen Sie die folgenden Empfehlungen, um die Bereitstellung zu containerisieren:

  • Identifizieren Sie Domänengrenzen. Beginnen Sie, indem Sie die Domänengrenzen innerhalb Ihrer monolithischen Anwendung identifizieren. Auf diese Weise können Sie ermitteln, welche Teile der Anwendung in separate Dienste extrahiert werden können.

  • Erstellen Sie Docker-Images. Verwenden Sie beim Erstellen von Docker-Images für Ihre .NET-Dienste Chiseled-Basisimages. Diese Images enthalten nur den minimalen Satz von Paketen, die für die Ausführung von .NET erforderlich sind, wodurch sowohl die Paketgröße als auch der Angriffsfläche minimiert werden.

  • Verwenden Sie mehrstufige Dockerfiles. Implementieren Sie mehrstufige Dockerfiles, um Buildzeitressourcen vom Laufzeitcontainerimage zu trennen. Es hilft, Ihre Produktionsimages klein und sicher zu halten.

  • Führen Sie die Ausführung als nichtroot-Benutzer aus. Führen Sie Ihre .NET-Container als Nicht-Root-Benutzer (über Benutzername oder UID, $APP_UID) aus, um dem Prinzip der geringsten Privilegien zu entsprechen. Es beschränkt die potenziellen Auswirkungen eines kompromittierten Containers.

  • Überwachen Sie Port 8080. Wenn Sie als Nicht-Root-Benutzer arbeiten, konfigurieren Sie Ihre Anwendung so, dass sie Port 8080 überwacht. Es ist eine gängige Konvention für Nicht-Root-Benutzer.

  • Kapseln Sie Abhängigkeiten. Stellen Sie sicher, dass alle Abhängigkeiten für die Funktion der App im Docker-Containerimage gekapselt sind. Die Kapselung ermöglicht es der App, zuverlässig in einer Vielzahl von Hosts bereitgestellt zu werden.

  • Wählen Sie die richtigen Basisimages aus. Das ausgewählte Basisimage hängt von Ihrer Bereitstellungsumgebung ab. Wenn Sie beispielsweise in Container-Apps bereitstellen, müssen Sie Linux-Docker-Images verwenden.

Die Referenzimplementierung verwendet z. B. einen mehrstufigen Buildprozess. Die ersten Phasen kompilieren und erstellen die Anwendung mit einem vollständigen SDK-Image (mcr.microsoft.com/dotnet/sdk:8.0-jammy). Das endgültige Laufzeitimage wird aus dem chiseled-Basisimage erstellt, das das SDK und Buildartefakte ausschließt. Der Dienst wird als Nicht-Root-Benutzer (USER $APP_UID) ausgeführt und macht Port 8080 verfügbar. Die Abhängigkeiten, die für den Betrieb der Anwendung erforderlich sind, sind im Docker-Image enthalten, wie durch die Befehle zum Kopieren von Projektdateien und Wiederherstellen von Paketen belegt. Durch die Verwendung linuxbasierter Images (mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled) wird die Kompatibilität mit Container-Apps sichergestellt, für die Linux-Container für die Bereitstellung erforderlich sind.

# 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"]

Bereitstellen der Referenzimplementierung

Stellen Sie die Referenzimplementierung des modernen Web-App-Musters für .NET bereit. Es gibt Anweisungen für die Entwicklungs- und Produktionsbereitstellung im Repository. Nach der Bereitstellung können Sie Entwurfsmuster simulieren und beobachten.

Diagramm, das die Architektur der Referenzimplementierung zeigt.Abbildung 3: Architektur der Referenzimplementierung: Laden Sie eine Visio-Datei dieser Architektur herunter.