Ein Programm oder viele: Monolithen und Services
Ein System in Services aufzuteilen ist ein Tausch, kein Upgrade. In dieser Lektion geht es darum, was jede Form bringt und was sie kostet.
Du hast jetzt einen Server, einen API-Vertrag und Caches davor. All das passt noch in ein einziges Programm: ein Prozess, einmal gestartet, der jede Anfrage beantwortet. Das ist ein Monolith, und so starten fast alle Systeme. Die spannende Frage ist nicht, ob ein Monolith altmodisch ist. Die Frage ist, was mit diesem einen Programm passiert, wenn die Firma wächst, und ob es wirklich hilft, es in Teile zu zerlegen.
Ein Monolith ist eine einzige auslieferbare Einheit. Der Code, der die Seite rendert, der Code, der die Karte belastet, und der Code, der die E-Mail verschickt, leben alle im selben Programm, teilen denselben Speicher und werden zusammen veröffentlicht. Wenn ein Teil einen anderen aufruft, ist das ein ganz normaler Funktionsaufruf: Mikrosekunden, und entweder er kehrt zurück oder er wirft einen Fehler. Es gibt eine Datenbank, also ist das Verknüpfen von Daten über mehrere Funktionen hinweg eine Abfrage und kein Netzwerk-Roundtrip. Es gibt eine Logdatei zum Lesen.
MONOLITH SERVICES
---------------------- ----------------------
processes to run one many
deploys one for everything one per service
where you debug one stack trace a trace across five logs
scaling copy the whole thing copy only the busy part
data one database, easy joins split, joins become calls
failure one crash hurts all one crash stays containedJeder Gewinn links wird rechts zu einem Kostenpunkt; keine der beiden Spalten ist gratis.Was ein Monolith dir gibt, ist enorm und wird leicht unterschätzt. Ausliefern ist eine einzige Aktion, also gehen eine Änderung am Zahlungscode und eine Änderung am Text der Seite zusammen raus und werden zusammen getestet. Fehlersuche ist ein einziger Stacktrace: Wenn eine Anfrage scheitert, verfolgst du sie durch dasselbe Programm, in einem Log, ohne die Frage, welche Version welches Service gerade lief. Eine Transaktion kann echt sein: Die Karte belasten und die Bestellung als bezahlt markieren – entweder passiert beides oder keins von beidem. Und Refactoring ist billig – eine Funktion zwischen zwei Dateien zu verschieben ist eine Codeänderung, kein Projekt.
Und hier schlagen die Kosten wirklich zu. Skalierung: Du kopierst nicht mehr ein Programm, sondern entscheidest, welche Teile du kopierst und wie sie sich gegenseitig finden. Ausfall: Ein Funktionsaufruf kehrt zurück oder wirft einen Fehler, aber ein Netzwerkaufruf hat ein drittes Ergebnis – er hängt, oder er klappt und die Antwort geht auf dem Rückweg verloren. Plötzlich brauchst du Timeouts, Wiederholungen und die Disziplin, dass ein erneuter Zahlungsversuch nicht zweimal abbucht. Fehlersuche: Eine Nutzeraktion läuft jetzt über fünf Services, und der Stacktrace, auf den du dich verlassen hast, wird durch eine Suche über fünf Logs ersetzt – und die klappt nur, wenn jemand dafür gesorgt hat, dass jeder Service eine gemeinsame Anfrage-ID schreibt. Konsistenz: Die Datenbank ist aufgeteilt, also muss die Transaktion, die früher gratis war, jetzt von Hand gebaut werden, und sie heißt Saga.
Die tiefste dieser Kosten ist die, die Anfänger nie kommen sehen: Das Netzwerk ist nicht gratis und nicht zuverlässig. Innerhalb eines Programms ist ein Funktionsaufruf so billig und so sicher, dass du aufhörst, darüber nachzudenken. Zwischen zwei Services kann jeder Aufruf hundertmal länger dauern, für sich allein scheitern und so langsam sein, dass er einen Thread auffrisst und den Aufrufer mitreißt. Code, der eine einfache Schleife über fünf Elemente war, wird zu fünf Roundtrips hintereinander. Architektur auf dieser Ebene ist größtenteils das Studium der Frage, welche Aufrufe du dir über eine Grenze hinweg leisten kannst.
Praxis
Die Kasse ruft den Lager-Service auf, um einen Artikel zu reservieren. Was kann hier schiefgehen, was innerhalb eines einzigen Programms nicht schiefgehen könnte?
Hinweise
- Ein Funktionsaufruf in einem Programm liefert entweder ein Ergebnis oder wirft einen Fehler; halb beantwortet gibt es nicht.
- Ein entfernter Aufruf läuft übers Netzwerk, und das Netzwerk hat einen dritten Ausgang.
15 XP
Ordne jedes Problem der Form zu, durch die es schlimmer wird.
Hinweise
- Der Ärger beim Debugging kommt von der Zahl der Stellen, an die du schauen musst, nicht von der Menge an Code.
- Der Ärger beim Skalieren kommt daher, dass du Teile mitkopieren musst, die gar nicht ausgelastet sind.
15 XP
Bringe den ehrlichen Weg von einem Programm zu mehreren Services in die richtige Reihenfolge.
Hinweise
- Du kannst keine Grenze um etwas ziehen, das du noch nicht gefunden hast.
- Der letzte Schritt ist der teure, deshalb kommt er erst, nachdem die günstigen bewiesen haben, dass er sich lohnt.
18 XP