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 Sie vermehrt Sperrkonflikte bemerken, wenn Sie die Anwendung aufskalieren und weitere Instanzen hinzufügen. 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, nimmt eine neue Instanz die Verarbeitung an der Stelle auf, an der die vorherige Instanz aufgehört hat, aber es entstehen keine Konflikte durch mehrere Instanzen.

Empfehlungen

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

Implementieren Sie die letztliche Konsistenz. Wenn Daten verteilt werden, braucht es die Koordination, um starke Konsistenzgarantien durchzusetzen. Angenommen, ein Vorgang aktualisiert zwei Datenbanken. Anstatt den Vorgang in einem einzelnen Transaktionsbereich zu platzieren, ist es besser, wenn das System für letztliche Konsistenz sorgen kann – eventuell durch Verwendung des Musters Ausgleichende Transaktion –, um nach einem Fehler einen logischen Rollback ausführen zu können.

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.

  • Beim Event Sourcing-Muster werden Zustandsänderungen als eine Reihe von Ereignissen in einem nur zum Anfügen vorgesehenen 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 lesegeschützte Speicher im CQRS-Muster das Event Sourcing verwendet, kann der schreibgeschützte Speicher auf die gleichen Ereignisse lauschen, um eine lesbare, für Abfragen optimierte Momentaufnahme des aktuellen Zustands zu erstellen. 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. Entwerfen Sie Vorgänge nach Möglichkeit idempotent. Auf diese Weise können sie mit einer „Least-Once“-Semantik verarbeitet 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. Bei der optimistischen Parallelität ä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-Datenbank 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.