Minimieren der Koordination

Azure Storage
Azure SQL-Datenbank
Azure Cosmos DB

Minimieren sie die Koordination, um Skalierbarkeit zu erzielen

Die meisten Cloudanwendungen bestehen aus mehreren Anwendungsdiensten, z. B. Web-Front-Ends, Datenbanken, Geschäftsprozesse und Berichterstellung und Analyse. Um Skalierbarkeit und Zuverlässigkeit zu erreichen, sollte jeder dieser Dienste auf mehreren Instanzen ausgeführt werden.

Unkoordinierte Systeme, bei denen Arbeit unabhängig voneinander verarbeitet werden kann, ohne dass Nachrichten zwischen Computern übergeben werden müssen, sind im Allgemeinen einfacher zu skalieren. Die Koordination ist in der Regel kein binärer Zustand, sondern ein Spektrum. Koordination erfolgt auf verschiedenen Ebenen, z. B. Daten oder Berechnen.

Was geschieht, wenn zwei Instanzen versuchen, gleichzeitige Operationen auszuführen, die sich auf einen geteilten Zustand auswirken? In einigen Fällen muss die Koordination über Knoten hinweg erfolgen, z. B. um ACID-Garantien zu erhalten. In diesem Diagramm wartet Node2 darauf, dass Node1 eine Datenbanksperre freigibt.

Datenbanksperrdiagramm

Die Koordinierung begrenzt die Vorteile horizontaler Skalierung und schafft Engpässe. In diesem Beispiel werden beim Skalieren der Anwendung und Hinzufügen weiterer Instanzen verstärkte Sperrverknüpfungen beobachtet. Im schlimmsten Fall verbringen die Front-End-Instanzen die meiste Zeit mit dem Warten auf Sperren.

Die "Genau einmal"-Semantik ist eine weitere häufige Quelle der Koordination. Beispielsweise muss eine Bestellung genau einmal verarbeitet werden. Zwei Mitarbeiter lauschen auf neue Aufträge. Worker1 nimmt eine Bestellung zur Bearbeitung auf. Die Anwendung muss sicherstellen, dass Worker2 die Arbeit nicht dupliziert, aber auch, wenn Worker1 abstürzt, geht die Reihenfolge nicht verloren.

Koordinationsdiagramm

Sie können ein Muster wie Scheduler Agent Supervisor verwenden, um zwischen den Mitarbeitern zu koordinieren, aber in diesem Fall könnte ein besserer Ansatz darin liegen, die Arbeit zu partitionieren. Jedem Mitarbeiter wird ein bestimmter Auftragsbereich zugewiesen (z. B. nach Abrechnungsregion). Wenn ein Worker abstürzt, übernimmt eine neue Instanz genau dort, wo die vorherige Instanz aufgehört hat, allerdings befinden sich mehrere Instanzen nicht im Wettbewerb.

Empfehlungen

Verwenden Sie entkoppelte Komponenten, die asynchron kommunizieren. Komponenten sollten im Idealfall Ereignisse verwenden, um miteinander zu kommunizieren.

Setzen Sie auf schlussendliche Konsistenz. Wenn Daten verteilt werden, braucht es die Koordination, um starke Konsistenzgarantien durchzusetzen. Angenommen, ein Vorgang aktualisiert zwei Datenbanken. Anstatt sie in einen einzelnen Transaktionsbereich zu setzen, ist es besser, wenn das System eventuale Konsistenz unterstützen kann, z. B. mithilfe des Musters 'Ausgleichstransaktion', um nach einem Fehler ein logisches Rollback durchzuführen.

Verwenden Sie Domänenereignisse, um den Zustand zu synchronisieren. Ein Domänenereignis ist ein Ereignis, das erfasst, wenn etwas geschieht, das eine Bedeutung innerhalb der Domäne hat. Interessierte Dienste können auf das Ereignis lauschen, anstatt eine globale Transaktion zu verwenden, um mehrere Dienste zu koordinieren. Wenn dieser Ansatz verwendet wird, muss das System letztendliche Konsistenz tolerieren (siehe vorheriges Element).

Berücksichtigen Sie Muster wie CQRS und Event Sourcing. Diese beiden Muster können dazu beitragen, Konflikte zwischen Lesevorgängen und Schreibvorgängen zu reduzieren.

  • Das CQRS-Muster trennt Lesevorgänge von Schreibvorgängen. In einigen Implementierungen werden die Lesedaten physisch von den Schreibdaten getrennt.

  • Im Event Sourcing-Muster werden Zustandsänderungen in Form von Ereignissen an einen nur anhängegeschützten Datenspeicher aufgezeichnet. Das Anfügen eines Ereignisses an den Datenstrom ist ein atomiger Vorgang, der eine minimale Sperrung erfordert.

Diese beiden Muster ergänzen sich gegenseitig. Wenn der nur-schreibende Speicher in CQRS die Ereignisquellen verwendet, kann der nur-lesende Speicher auf dieselben Ereignisse lauschen, um eine lesbare Momentaufnahme des aktuellen Zustands zu erstellen, die für Abfragen optimiert ist. Bevor Sie CQRS oder Event Sourcing einführen, sollten Sie sich jedoch der Herausforderungen dieses Ansatzes bewusst sein.

Partitionsdaten und -zustand. Vermeiden Sie das Einfügen aller Daten in ein Datenschema, das für viele Anwendungsdienste freigegeben ist. Eine Microservices-Architektur setzt diesen Grundsatz durch, indem jeder Dienst für seinen eigenen Datenspeicher verantwortlich macht. Innerhalb einer einzelnen Datenbank kann die Partitionierung der Daten in Shards die Parallelität verbessern, da ein Dienst, der in einen Shard geschrieben wird, keinen Einfluss auf einen Dienst hat, der in einen anderen Shard geschrieben wird. Obwohl die Partitionierung einen gewissen Grad an Koordination hinzufügt, können Sie Partitionierung verwenden, um die Parallelität für eine bessere Skalierbarkeit zu erhöhen. Partitionieren Sie den monolithischen Zustand in kleinere Blöcke, damit die Daten unabhängig voneinander verwaltet werden können.

Entwerfen von idempotenten Vorgängen. Wenn möglich, entwerfen Sie Vorgänge, die idempotent sein sollen. Auf diese Weise können sie mit mindestens einmaler Semantik behandelt werden. Beispielsweise können Sie Arbeitsaufgaben in eine Warteschlange einfügen. Wenn ein Mitarbeiter in der Mitte eines Vorgangs abstürzt, übernimmt ein anderer Mitarbeiter die Arbeitsaufgabe. Wenn der Worker Daten aktualisieren und andere Nachrichten als Teil seiner Logik ausgeben muss, sollte das idempotente Nachrichtenverarbeitungsmuster verwendet werden.

Verwenden Sie nach Möglichkeit optimistische Parallelität. Pessimistische Parallelitätssteuerung verwendet Datenbanksperren, um Konflikte zu verhindern. Dies kann zu einer schlechten Leistung führen und die Verfügbarkeit verringern. Mit optimistischer Parallelitätssteuerung ändert jede Transaktion eine Kopie oder Momentaufnahme der Daten. Wenn die Transaktion zugesichert wird, überprüft das Datenbankmodul die Transaktion und lehnt alle Transaktionen ab, die sich auf die Datenbankkonsistenz auswirken würden.

Azure SQL Database und SQL Server unterstützen optimistische Parallelität durch Snapshot Isolation. Einige Azure-Speicherdienste unterstützen optimistische Parallelität durch die Verwendung von Etags, einschließlich Azure Cosmos DB und Azure Storage.

Betrachten Sie MapReduce oder andere parallele, verteilte Algorithmen. Je nachdem, welche Daten und Welche Art von Arbeit ausgeführt werden soll, können Sie die Arbeit möglicherweise in unabhängige Aufgaben aufteilen, die von mehreren Knoten parallel ausgeführt werden können. Sehen Sie sich den Stil der Big Compute-Architektur an.

Verwenden Sie die Wahl des Leiters für die Koordinierung. Stellen Sie in Fällen, in denen Sie Vorgänge koordinieren müssen, sicher, dass der Koordinator nicht zu einem einzigen Fehlerpunkt in der Anwendung wird. Unter Verwendung des Wahlmusters "Leader" ist eine Instanz jederzeit der Leiter und fungiert als Koordinator. Wenn der Leiter fehlschlägt, wird eine neue Instanz als Leiter gewählt.