YAMVM - Noch ein Monolith gegen Microservices

Von: Erik Landerholm

Ahh ... die Frage, mit der wir alle zu kämpfen haben: Microservices oder Monolith? OK, nicht wirklich. Wo immer Sie arbeiten, finden Sie höchstwahrscheinlich Beispiele für beides. Bei TrueCar wollten wir, als wir uns auf den Weg von unserem Rechenzentrum zu AWS machten, nichts heben und verschieben. Wir mussten die gesamte Anwendung und den unterstützenden Code neu schreiben, um die Vorteile moderner Entwickler-Tools und -Prozesse nutzen zu können. Unser oberstes Ziel war es, aus unseren Rechenzentren in AWS auszusteigen und ein agiles Entwicklungsteam zu werden. Letztendlich wurde CI / CD (eine weitere Reihe von Blogposts, die wir in Kürze veröffentlichen werden) zum Ziel, um das sich jeder sammeln konnte.

Wir werden dieses Thema in zwei Teile aufteilen: Warum und Wie. Es gibt bereits viele Blog-Posts, die genau beschreiben, warum Sie sich für Monolithic vs. SOA vs. Microservices entscheiden könnten. Wir möchten genauer darauf eingehen, warum ein in Rails geschriebenes Backend mit monolithischer API für uns das Richtige ist und wie wir einige der inhärenten Nachteile eines Monolithen (undichte Abstraktionen usw.) vermieden oder minimiert haben. Die ABP-API (Auto Buying Platform) von Monolith ist nicht die einzige Anwendung oder Codebasis im Unternehmen. Es bietet jedoch alles, womit Kunden auf TrueCar.com, unseren über 700 Partner-Websites, all unseren Händler-Tools und unseren mobilen Angeboten interagieren.

Warum

Die Art und Weise, wie das alte ABP „aufgebaut“ wurde, machte dies unmöglich. Wir mussten alles, was TrueCar.com und unsere über 700 Partner-Websites, Händlerprodukte und mobilen Angebote unterstützt, komplett neu schreiben, auf AWS verlagern und einen agilen Entwicklungsprozess implementieren. Die Details darüber, warum dies notwendig war, liegen außerhalb des Bereichs dieses Blogposts. Wir werden uns darauf konzentrieren, wie und warum wir ein monolithisches Back-End verwendet haben, das mit Ruby on Rails erstellt wurde, um den Prozess so einfach wie möglich zu gestalten.

Unabhängig davon, ob Sie eine Anwendung von Grund auf neu erstellen oder eine Legacy-Anwendung neu schreiben, müssen Sie über eine Architektur nachdenken und sich letztendlich für eine entscheiden, die von Ihrer Philosophie bestimmt wird, die wiederum von den Mechanismen der Anwendung bestimmt wird. Die meisten Webanwendungen fallen in eines von zwei Camps (ich verallgemeinere gerade viel):

a) Große Verkehrsmengen mit einem relativ einfachen Datenmodell.

b) Weniger Verkehr und ein relativ kompliziertes Datenmodell.

TrueCar.com ist mehr als a. Unsere Datenbank wird auch bei mehr als 700 Mitarbeitern und einem Umsatz von ca. 400 Mio. USD unser Engpass sein.

  • Unser Marktplatz ist effizient, aber kompliziert.
  • Unser Back-End-Entwicklungsteam besteht aus Dutzenden, nicht aus Hunderten oder Tausenden.
  • Autodaten sind notorisch chaotisch und tief, mit vielen Beziehungsebenen, die sich perfekt für ein RDBMS eignen.
  • Wir haben Millionen von Nutzern und mehrere Millionen UVs pro Monat, aber keine Milliarden.

Als ich 2014 bei TrueCar anfing, verlief unser Entwicklungsprozess bekanntermaßen langsam, mit einer einzelnen Bereitstellung alle acht Tage. Nach fünf Jahren des schnellen Vorlaufs führen wir jede Woche mehr als einhundert Bereitstellungen durch, wobei unser neues, mit Rails erstelltes ABP-Back-End eine Codeabdeckung von 98% aufweist. Wir wussten, dass wir unsere APIs auf ein akzeptables Leistungsniveau bringen können. Was wir optimieren mussten, war die Entwicklergeschwindigkeit!

Vorteile eines Monolithen

Bevor wir speziell darüber sprechen, wie wir unseren Monolithen strukturiert haben, um einige der Nachteile einer monolithischen Codebasis zu minimieren, wollen wir seine Vorteile erläutern. Einer der größten (und am meisten unterschätzten, bis Sie sie nicht mehr haben) Gewinne in einem monolithischen Back-End, das ein RDBMS verwendet, ist die Fähigkeit, JOINS zu verwenden. Stellen Sie sich vor, Sie haben ein Benutzermodell und möchten alle Fahrzeuge abrufen, die sie sich angesehen haben. Bei einer monolithischen Datenbank und Anwendung wird dies durch einen oder mehrere JOINS von Benutzern -> Fahrzeugen (in Wirklichkeit ist dies komplizierter) erreicht, anstatt mehrere Dienste zu authentifizieren und zu autorisieren und aufzurufen.

Sie können sich JOINS als Ihre API zwischen Diensten vorstellen. Es ist offensichtlich, dass diese Art des Denkens Einschränkungen unterliegt, genauso wie Sie in einer Microservice-Architektur nicht unbegrenzt viele Aufrufe an verschiedene Service-APIs tätigen können. Diese Netzwerkaufrufe, Suchvorgänge, JSON-Serialisierungen (die sich wahrscheinlich von JSON für die Kommunikation zwischen Diensten entfernen müssen) usw. für mehrere Dienste sind nicht kostenlos.

Die Fähigkeit, alle erforderlichen Daten zu authentifizieren und zu autorisieren, API-Aufrufe vorzunehmen und zu sammeln und sie für die Anzeige in einem einzigen Service (ABP) zu organisieren, bedeutet, dass wir die Sprache und das Framework auswählen können, die uns die höchste Entwicklerproduktivität bieten, anstatt dies zu erfordern auf Laufzeitgeschwindigkeit optimieren. Die schnellsten Entwickler zu haben, ist ein enormer Wettbewerbsvorteil!

Abgesehen von den beiden oben aufgeführten großen Vorteilen gibt es noch viele weitere, wenn sich der gesamte Code und die gesamte Funktionalität an einem Ort befinden:

  • Keine Bereitstellungsabhängigkeiten (Abhängigkeitshölle!): Welche Version dieses Dienstes ist mit diesem Dienst kompatibel? Shared Libraries oder nicht? Wie finde ich heraus, welche Dienste verfügbar sind und was sie genau leisten? Dies ist in einem gut dokumentierten und organisierten Monolithen viel einfacher - wenn Sie so wollen, in einem majestätischen Monolithen.
  • Fehlerverfolgung: Gesamte Transaktionen werden von einem Ort zu einem Ort protokolliert.
  • Testen: Wir haben eine Testabdeckung von 98% für den Monolith, und der FE Monolith führt vollständige App-Integrationstests durch. Mehr als einen Monolithen zu haben, ist kein Widerspruch!
  • Keine Silos: Für Entwickler ist es sehr einfach, an verschiedenen Teilen der App zu arbeiten, da sie alle auf eine gemeinsame Art und Weise mit denselben Tools und ohne verteilte Computerkenntnisse erstellt wurden.
  • Freigegebener Code: Keine freigegebenen Bibliotheken oder Transaktionen ohne Wissen, bei denen der gesamte für den Betrieb des Dienstes erforderliche Umfang zusammen mit jeder Anforderung gesendet wird.
  • Querschnittsthemen: Wir haben viele davon; Sie wahrscheinlich auch! Wenn Sie viel Zeit damit verbringen, Services zu definieren, die nicht ineinander übergehen, können Sie Dinge aufbauen, die Ihren Kunden helfen!

Alle oben genannten Vorteile können mit Microservices gemindert oder sogar realisiert werden, was jedoch mit Kosten verbunden ist. Diese Kosten sind komplizierter beim Entwickeln, Testen, Überwachen und Bereitstellen. Microservices-Architektur ist wie XML (oder Gewalt): Verwenden Sie es sparsam und nur, wenn es wirklich benötigt wird!

Nachteile eines Monolithen

Nichts ist im Leben frei oder perfekt. Jede Entscheidung hat Vor- und Nachteile und Nebenwirkungen, von denen einige schwer oder gar nicht vorhersehbar sind. Eine monolithische Softwarearchitektur ist nicht anders. Im Folgenden sind einige der Nachteile aufgeführt, die wir in unserem "Wie" -Post erläutern werden:

  • Leistung: Dies ist wahrscheinlich der größte aller „Nachteile“, da dies auch ein Vorteil ist. Wir optimieren unsere Datenbank, speichern viel im Cache, verwenden Sidekiq, um alles zu ermöglichen, was wir asynchronisieren können, und leiten letztendlich Nachrichten außerhalb des Monolithen mit einem so genannten Wurmloch (Enterprise Service Bus, der mit Kinesis erstellt wurde) weiter.
  • Beweglichkeit: Auch ein Vorteil des Monolithen, aber je nach Teamgröße, Anzahl der Dienste und Größe kann dies zu einem Nachteil werden. Ihr Entwicklungsprozess kann dieses Problem erheblich verringern.
  • Leakiness: Wenn Sie mehr und mehr Funktionen in Ihre monolithische Codebasis schreiben, ist es unvermeidlich, dass ein Teil davon nicht auf der Spur bleibt. Abstraktionen und Funktionen dringen in andere Teile des Monolithen vor. Es gibt Möglichkeiten, dies zu mildern, und wir werden uns eingehend damit befassen, wie wir mit diesem Problem umgehen. Das entsprechende Problem in einer Microservices-Architektur ist, dass Sie häufig Funktionen und Code in Diensten duplizieren.
  • Testen: Mit Microservices können Sie Ihre Bedenken in verschiedenen Codebasen isolieren. Dies hat Vorteile beim Testen von Einheiten, die Komplikation tritt jedoch beim Testen der Integration auf.
  • Bereitstellungen: Wie beim Testen ist die Bereitstellung eines einzelnen Dienstes einfacher und schneller als bei einem Monolith, der aus vielen Diensten besteht, aber der Teufel ist in der Koordination, wenn Sie über viele Dienste verfügen. Dies kann in einem Monolithen durch eine gute Service- / Funktionsisolation erleichtert werden.

Hoffentlich gibt Ihnen dies einen Einblick in unsere Überlegungen, warum wir uns entschieden haben, eine monolithische Back-End-Architektur für unser Umschreiben im Gegensatz zu Microservices zu verwenden. Bleiben Sie dran für unseren Beitrag darüber, wie wir es gemacht haben. Ich denke, es wird Ihnen einige wirklich nützliche Einblicke geben, wie Sie Ihren eigenen Magnificent Monolith ™ verwalten können (als Markenzeichen, da Majestic took bereits von jemandem eingenommen wurde).

Seien Sie gespannt auf die nächste Ausgabe, alles über das „Wie!“. Wenn Ihnen diese Ausgabe gefallen hat, werden Sie unsere bevorstehenden Posts über Spacepods (unsere Bereitstellungsplattform), CI / CD, Armatron (unsere ETL-Plattform) und Gluestick (unser FE-Framework) lieben , gebaut mit React), Wurmloch (unser ESB) und mehr!