Ein paar Worte zur Verteidigung des Monolithen

Wir vergleichen die Merkmale von Microservice und monolithischer Architektur mit ihren Vor- und Nachteilen. Der Artikel wurde für Habr auf der Grundlage der Materialien unseres Meta Hot Backend erstellt , das am 9. Februar 2019 in Samara stattfand. Wir betrachten die Faktoren der Wahl der Architektur in Abhängigkeit von der spezifischen Aufgabe.

Noch vor 5 Jahren hatte niemand von Microservices gehört. Laut Statistiken von Google Trends nimmt ihre Beliebtheit jedoch von Jahr zu Jahr zu.



Monolith und Microservices: Beispiele


Wenn das Projekt eine monolithische Architektur verwendet, verfügt der Entwickler nur über eine Anwendung, deren Komponenten und Module auf einer einzigen Basis arbeiten.



Bei der Microservice-Architektur wird in Module unterteilt, die als separate Prozesse ausgeführt werden und separate Server haben können. Jeder Mikrodienst arbeitet mit einer eigenen Datenbank, und alle diese Dienste können sowohl synchron (http) als auch asynchron miteinander kommunizieren. Darüber hinaus ist es zur Optimierung der Architektur wünschenswert, die Beziehung zwischen Diensten zu minimieren.

Das folgende Diagramm ist vereinfacht und spiegelt zunächst die Geschäftskomponenten wider.



Microservices: Vorteile


Die Microservice-Architektur bietet mindestens vier Vorteile:

Unabhängige Skalierung und Bereitstellung

Für jeden Microservice wird eine unabhängige Bereitstellung bereitgestellt. Dies ist praktisch, wenn einzelne Module aktualisiert werden. Wenn die Belastung des Moduls zunimmt, kann der entsprechende Mikrodienst skaliert werden, ohne die anderen zu beeinflussen. Auf diese Weise können Sie die Last flexibel verteilen und Ressourcen sparen.

Unabhängige Entwicklung

Jeder Microservice (z. B. ein Speichermodul) kann von einem Team entwickelt werden, um die Erstellung eines Softwareprodukts zu beschleunigen.

Nachhaltigkeit

Der Ausfall eines Mikrodienstes wirkt sich nicht auf die Leistung anderer Module aus.

Heterogenität

Jedes Team kann seine eigene Sprache und Technologie für die Implementierung von Microservices auswählen. Es ist jedoch wünschenswert, dass es über kompatible Schnittstellen verfügt.

Unter Entwicklern kann man die Meinung hören, dass monolithische Architektur veraltet ist, schwer zu warten und zu skalieren ist, schnell in einem „großen Schmutzklumpen“ wächst und praktisch antipattern ist, dh ihre Anwesenheit im Code ist unerwünscht. Große Unternehmen wie Netflix, die in ihren Projekten auf Microservice-Architektur umgestellt haben, werden häufig als Beweis für diese Meinung angeführt.

Mal sehen, ob wirklich jeder nach dem Vorbild der größten Marken von einem Monolithen zu Microservices wechseln sollte.

Umstellung auf Microservices: Mögliche Schwierigkeiten


Problem Eins: Zersetzung

Idealerweise sollte die Anwendung in Mikrodienste unterteilt werden, damit sie so wenig wie möglich miteinander interagieren, da sonst die Anwendung schwierig zu warten ist. Gleichzeitig ist die Zerlegung zu Beginn der Entwicklung schwierig zu implementieren, wenn sich geschäftliche Probleme und der Themenbereich mit dem Aufkommen neuer Anforderungen noch ändern können. Refactoring ist teuer.

Wenn es notwendig wird, einen Teil der Funktionen von Dienst A auf Dienst B zu übertragen, sind hier Schwierigkeiten möglich: Beispielsweise werden Dienste in verschiedenen Sprachen ausgeführt, interne Aufrufe von Diensten werden zum Netzwerk, andere Bibliotheken müssen verbunden werden. Wir können die Richtigkeit des Refactorings nur mit Hilfe von Tests überprüfen.



Problem zwei: Transaktionen

Ein weiteres Problem besteht darin, dass Microservices nicht das Konzept verteilter Transaktionen haben. Wir können die architektonische Integrität eines Geschäftsbetriebs nur innerhalb eines Mikrodienstes garantieren. Wenn der Vorgang mehrere Mikrodienste umfasst, können dort verschiedene Datenbanken verwendet werden, und eine solche Transaktion muss abgebrochen werden. Um dieses Problem zu lösen, werden im Geschäftsleben verschiedene Methoden verwendet, bei denen die Barrierefreiheit wichtiger ist als die Integrität. Gleichzeitig werden Ausgleichsmechanismen bereitgestellt, falls etwas schief geht. Wenn die Waren beispielsweise nicht auf Lager sind, müssen Sie eine Rückerstattung auf das Konto des Käufers vornehmen.

Wenn ein Monolith uns automatisch architektonische Integrität verleiht, müssen Sie bei Microservices einen eigenen Mechanismus entwickeln und Bibliotheken mit vorgefertigten Lösungen verwenden. Wenn Sie eine Operation auf Dienste verteilen, ist es besser, Daten synchron anzufordern und nachfolgende Aktionen asynchron auszuführen. Wenn es nicht möglich ist, auf einen der Dienste zuzugreifen, wird das Team in die Warteschlange gestellt, sobald es wieder verfügbar ist.

In dieser Hinsicht ist es notwendig, den Ansatz für die Benutzeroberfläche zu überarbeiten. Der Benutzer sollte benachrichtigt werden, dass einige Aktionen nicht sofort, sondern innerhalb einer bestimmten Zeit ausgeführt werden. Wenn der Antrag bearbeitet wird, erhält er eine Einladung, die Ergebnisse anzuzeigen.



Problem drei: Berichterstattung

Wenn wir eine monolithische Architektur mit einer einzigen Datenbank verwenden, können Sie zum Erstellen eines komplexen Berichts mehrere Datenbeschriftungen auswählen und aufrufen: Früher oder später werden sie angezeigt. Auf Microservices können diese Daten jedoch auf verschiedenen Basen gestreut werden.

Zum Beispiel müssen wir Unternehmen mit bestimmten Metriken auflisten. Mit einer einfachen Liste von Unternehmen funktioniert alles. Und wenn Sie Metriken hinzufügen müssen, die in einer anderen Datenbank liegen? Ja, wir können eine zusätzliche Anfrage stellen und Metriken per TIN anfordern. Und wenn diese Liste gefiltert und sortiert werden muss? Die Liste der Unternehmen kann sehr umfangreich sein, und dann müssen wir einen zusätzlichen Service mit einer eigenen Datenbank einführen - Berichte.



Problem 4: Hohe Entwicklungskomplexität

Die Arbeit an verteilten Diensten ist komplizierter: Alle Anforderungen werden über das Netzwerk gestellt und können deaktiviert werden. Sie müssen einen Rückrufmechanismus bereitstellen (wird der Anruf erneut getätigt? Wie oft?). Dies sind die „Bausteine“, die sich allmählich ansammeln und zur Erhöhung der Komplexität des Projekts beitragen.

Services können von mehreren verschiedenen Teams entwickelt werden. Sie müssen sie dokumentieren, die Dokumentation auf dem neuesten Stand halten und andere Teams warnen, wenn Sie die Version ändern. Dies sind zusätzliche Arbeitskosten.

Wenn jedes Team über eine unabhängige Bereitstellung verfügt, müssen Sie mindestens die vorherige Version beibehalten und erst deaktivieren, nachdem alle Benutzer des Dienstes auf die neue API umgestellt haben.
Natürlich können wir alle APIs in eine Art Artefakt umwandeln, das öffentlich verfügbar sein wird. Erstens können Dienste jedoch in verschiedenen Sprachen geschrieben werden, und zweitens wird dies nicht empfohlen. In einem unserer Projekte haben wir dies beispielsweise auf Wunsch des Kunden aus Sicherheitsgründen abgelehnt. Jeder Microservice verfügt über ein separates Repository, auf das der Kunde keinen Zugriff gewährt.

Im Entwicklungsprozess kann alles richtig funktionieren und dann - nein. Es kommt vor, dass die Anwendung im Falle von Ausnahmen unendlich versucht, sie zu verarbeiten, und dies führt zu einer großen Belastung - das gesamte System "legt sich". Um solche Situationen zu vermeiden, müssen Sie alles konfigurieren, z. B. um die Anzahl der Versuche zu begrenzen, um diesen Anruf nicht in derselben Sekunde an die Warteschlange zurückzugeben usw.





Das fünfte Problem: die Komplexität des Testens, Verfolgens und Debuggens

Um ein Problem zu testen, müssen Sie alle beteiligten Microservices herunterladen. Das Debuggen wird zu einer nicht trivialen Aufgabe, und alle Protokolle müssen irgendwo an einem Ort gesammelt werden. In diesem Fall benötigen Sie so viele Protokolle wie möglich, um herauszufinden, was passiert ist. Um das Problem zu verfolgen, müssen Sie den gesamten Pfad verstehen, den die Nachricht zurückgelegt hat. Unit-Tests reichen hier nicht aus, da Fehler an der Kreuzung der Dienste wahrscheinlich sind. Bei Änderungen kann die Funktionsfähigkeit erst nach dem Laufen auf dem Stand überprüft werden. Wir können jeden Mikrodienst auf eine bestimmte Speichermenge (z. B. 500 Megabyte) beschränken, aber es gibt Zeiten mit Spitzenlast, in denen bis zu zwei Gigabyte benötigt werden. Es gibt Zeiten, in denen das System langsamer wird. Infolgedessen können Ressourcen für etwas ausgegeben werden, das nicht zu den unmittelbaren Aufgaben des Kunden gehört: Beispielsweise gibt es nur zwei Business-Microservices, und die Hälfte der Ressourcen wird für drei zusätzliche Microservices ausgegeben, die die Arbeit der anderen unterstützen.



Microservice oder Monolith: Auswahlkriterien


Wenn Sie zwischen monolithischer und Microservice-Architektur wählen, müssen Sie zunächst von der Komplexität des Themenbereichs und der Notwendigkeit der Skalierung ausgehen.

Wenn der Themenbereich einfach ist und keine globale Zunahme der Benutzerzahl zu erwarten ist, können ohne Zweifel Microservices verwendet werden. In anderen Fällen ist es besser, mit der Entwicklung eines Monolithen zu beginnen und Ressourcen zu sparen, wenn keine Skalierung erforderlich ist. Wenn der Themenbereich komplex ist und in der Anfangsphase die endgültigen Anforderungen nicht definiert sind, ist es auch besser, mit einem Monolithen zu beginnen, um die Mikrodienste nicht mehrmals zu wiederholen. Mit der Weiterentwicklung des Projekts ist es möglich, seine einzelnen Teile in Microservices zu unterscheiden.



Ein Plus ist das Vorhandensein von Grenzen zu Beginn des Projekts, da dies dazu beiträgt, diese während des Entwicklungsprozesses nicht zu durchbrechen. Es ist auch sinnvoll, mit einer Datenbank zu beginnen, aber für jedes Modul ein Schema zu definieren (z. B. ein Zahlungsschema). Anschließend wird dies dazu beitragen, die Unterteilung von Modulen in Microservices zu vereinfachen. In diesem Fall beobachten wir die Grenzen der Module und können Microservices verwenden.

Jedes Modul muss über eine eigene API verfügen, damit es später zugewiesen und zu einem Microservice gemacht werden kann.



Nachdem Sie die Grenzen der Module festgelegt haben, können Sie bei Bedarf in Mikrodienste zerlegen. In etwa 90% der Fälle ist es möglich, auf dem Monolithen zu bleiben, aber bei Bedarf ist es einfacher und billiger, die Architektur zu ändern.

In unserer Praxis der Arbeit mit Monolithen und Mikrodiensten kamen wir zu folgenden Schlussfolgerungen:
  • Wechseln Sie nicht zu Microservices, nur weil diese von Netflix, Twitter, Facebook verwendet werden
  • Beginnen Sie mit zwei oder drei Mikrodiensten, die miteinander interagieren, arbeiten Sie alle nicht funktionalen Anforderungen (Sicherheit, Fehlertoleranz, Skalierbarkeit usw.) detailliert aus und wechseln Sie erst dann zu anderen Diensten
  • Automatisieren Sie alles Mögliche
  • Überwachung einrichten
  • Schreiben Sie Autotests
  • Verwenden Sie keine verteilten Transaktionen (dies ist jedoch kein Grund, die Garantie der Datenintegrität abzulehnen).
  • Wenn Sie eine Microservice-Architektur verwenden möchten, müssen Sie sich darauf einstellen, dass die Entwicklung etwa dreimal so viel kosten kann wie bei einem Monolithen. Beide Technologien haben jedoch ihre eigenen Nachteile und Vorteile, und jede von ihnen hat ihre eigene Nische.

Source: https://habr.com/ru/post/de453932/


All Articles