Hallo allerseits!
Kürzlich hat einer der Redner auf einer PGConf-Konferenz in Moskau eine „Microservice“ -Architektur demonstriert und nebenbei erwähnt, dass alle Microservices von einer gemeinsamen Basisklasse erben. Obwohl es keine Erklärungen für die Implementierung gab, schien es, dass in diesem Unternehmen der Begriff „Microservices“ nicht so verstanden wurde, wie es uns die Klassiker zu lehren schienen. Heute werden wir uns mit einem der interessanten Probleme befassen - was kann der gemeinsame Code in Microservices sein und ob es überhaupt sein kann.
Was ist ein Microservice? Dies ist eine eigenständige Anwendung. Kein Modul, kein Prozess, nicht etwas, das einfach separat bereitgestellt wird, sondern eine vollständige, echte, separate Anwendung. Es hat eine eigene Hauptfunktion, ein eigenes Repository im Git, eigene Tests, eine eigene API, einen eigenen Webserver, eine eigene README-Datei, eine eigene Datenbank, eine eigene Version, eigene Entwickler.
Wie bei Containern wurden Microservices eingesetzt, als die Rechenleistung von HW und die Zuverlässigkeit von Netzwerken ein solches Niveau erreichten, dass Sie sich einen Funktionsaufruf leisten können, der 100-mal länger dauert als zuvor. Sie können sich einen 100-mal höheren Speicherverbrauch und Luxus leisten jede "Großmutter" nicht nur in einer separaten "Wohnung", sondern in einem separaten "Haus" niederzulassen. Wie bei jeder Architekturlösung wird auch bei der Architektur von Microservices die Leistung beeinträchtigt, wodurch die Wartbarkeit des Codes für Entwickler verbessert wird. Da jedoch die Person und die Geschwindigkeit ihrer Reaktion gleich geblieben sind, erfüllen die Systeme weiterhin die Anforderungen.
Warum in separate Anwendungen aufteilen? Weil wir einen Teil der Komplexität des Systems bereits auf der Ebene der Systemarchitektur verteilen. Der Programmierprozess ist im Allgemeinen ein schrittweises „Abbeißen“ des großen anfänglichen „Stücks Komplexität“, und die Zerlegung (in Klassen, Module, Funktionen und in unserem Fall ganze Anwendungen) ist die Implementierung eines Teils dieser Komplexität in Form einer Struktur. Als wir das System in Microservices unterteilt haben, haben wir eine Architekturentscheidung getroffen (erfolgreich oder nicht), die die Entwickler bei der Implementierung bestimmter Teile der Funktionalität in Zukunft nicht mehr treffen müssen. Es ist bekannt, dass dieser spezielle Microservice für das Senden von E-Mails verantwortlich ist, und dieser - für die Autorisierung - wurde bereits eingerichtet, sodass alle meine neuen Funktionen ohne Diskussion auf dieses Muster „fallen“.
Ein wesentlicher Aspekt von Microservices ist die schlechte Konnektivität. Microservices sollten unabhängig vom Wort "vollständig" sein. Sie haben keine gemeinsamen Datenstrukturen, und jeder Mikrodienst kann / sollte seine eigene Architektur, Technologie, Montagemethode (usw.) haben. Per Definition. Weil es eine unabhängige Anwendung ist. Änderungen im Code eines Microservices sollten sich in keiner Weise auf die anderen auswirken, es sei denn, die API ist betroffen. Wenn ich N Microservices in Java geschrieben habe, sollte es keine einschränkenden Faktoren geben, den N + 1st Microservice nicht in Python zu schreiben, wenn dies aus irgendeinem Grund plötzlich rentabel ist. Sie sind lose miteinander verbunden und daher ein Entwickler, der mit einem bestimmten Microservice arbeitet:
a) überwacht seine API sehr empfindlich, da es die einzige Komponente ist, die von außen sichtbar ist;
b) Fühlt sich beim Refactoring völlig frei;
c) Verstehen Sie den Zweck von Microservice (hier erinnern wir uns an SRP) und implementieren Sie eine neue Funktion entsprechend
d) wählt die am besten geeignete Persistenzmethode aus;
usw.
All dies ist gut und klingt logisch und harmonisch, wie viele Ideologien und Theorien (und hier macht der ideologische Theoretiker ein Ende und geht zum Abendessen), aber wir üben. Der Code muss nicht auf
martinfowler.com geschrieben
werden . Und früher oder später sehen wir uns mit der Tatsache konfrontiert, dass alle Microservices:
- Protokollinformationen;
- Autorisierung enthalten;
- Zugriff auf Nachrichtenbroker
- die richtigen Fehlermeldungen zurückgeben;
- muss irgendwie die allgemeinen Entitäten im System verstehen, wenn überhaupt;
- muss mit einem gemeinsamen Nachrichtenformat (und Protokoll) arbeiten;
und mach es identisch.
Und irgendwann kommt der ideologische Architekt am Morgen zur Arbeit und entdeckt, dass nachts eine „Bibliothek“ im System aufgetaucht ist - ein neues Repository mit einem gemeinsamen Code, der in vielen Mikrodiensten verwendet wird. Sollte ein Architekt entsetzt sein?
Es kommt darauf an.
Um die Situation richtig einzuschätzen, sollten wir zur Hauptidee zurückkehren: Microservices sind eine Sammlung unabhängiger Anwendungen, die über eine (Netzwerk-) API miteinander interagieren. Darin sehen wir den Hauptvorteil und die Einfachheit der Architektur. Und diesen Vorteil wollen wir unter keinen Umständen verlieren. Beeinträchtigt der allgemeine Code, der in die "Bibliothek" gestellt wurde, dies? Schauen wir uns einige Beispiele an.
1. Die Benutzerklasse (oder eine andere Geschäftseinheit) befindet sich in der Bibliothek.
- d.h. Eine Geschäftseinheit ist nicht in einem Mikroservice gekapselt, sondern unterschiedlich verteilt (warum sollte sie andernfalls in eine gemeinsam genutzte Codebibliothek gestellt werden?).
- d.h. Microservices werden über diese Geschäftseinheit verbunden. Eine Änderung der Logik für die Arbeit mit einer Entität wirkt sich auf mehrere Microservices aus.
- Es ist schlecht, sehr schlecht, es ist überhaupt kein Microservices, obwohl es kein "großer Schlammball" ist, aber sehr schnell wird das Weltbild des Teams zu einem "großen Ball aus verteiltem Schlamm" führen.
- Aber Microservices im System arbeiten mit denselben Konzepten, und Konzepte sind oft Entitäten oder nur Strukturen mit Feldern. Was soll ich tun? Lesen Sie DDD. Es geht genau darum, wie Entitäten in Microservices gekapselt werden, damit sie nicht durch die API „fliegen“.
Leider hat jede Geschäftslogik, die in einer gemeinsam genutzten Bibliothek abgelegt wird, einen solchen Effekt. Allgemeine Codebibliotheken neigen dazu zu wachsen, was dazu führt, dass die Mitte des Systems einen logischen „Tumor“ bildet, der keinem bestimmten Mikrodienst angehört, und die Architektur abstürzt. Der "Schwerpunkt" des Systems beginnt sich in ein Repo mit einem gemeinsamen Code zu verwandeln, und wir erhalten eine höllische Mischung aus Monolithen und Mikrodiensten, und wir müssen überhaupt nicht dorthin gehen.
2. Der Parsing-Code für das Nachrichtenformat wird in die Bibliothek gestellt.
- Der Code ist höchstwahrscheinlich in Java, wenn alle Microservices in Java geschrieben sind.
- Wenn ich morgen einen Dienst in Python schreibe, kann ich den Parser nicht verwenden, aber es scheint überhaupt kein Problem zu sein. Ich schreibe eine Python-Version.
- Wichtigster Punkt: Wenn ich einen neuen Microservice in Java schreibe, muss ich diesen Parser verwenden? Ja, wahrscheinlich nicht. Vielleicht bin ich nicht verpflichtet, obwohl es als Microservice-Entwickler sehr nützlich sein kann. Nun, als hätte ich im Maven Repository etwas Nützliches gefunden.
Ein Nachrichtenparser oder ein verbesserter Logger oder ein umschlossener Client zum Senden von Daten an RabbitMQ - es ist wie Helfer, Hilfskomponenten. Sie sind den Standardbibliotheken von NuGet, Maven oder NPM ebenbürtig. Der Microservice-Entwickler ist immer der König. Er entscheidet, ob er die Standardbibliothek verwendet, seinen eigenen neuen Code erstellt oder den Code aus der gemeinsam genutzten Hilfsbibliothek verwendet. Wie es für ihn bequemer sein wird, weil er eine SEPARATE UND UNABHÄNGIGE APP schreibt. Kann sich ein bestimmter Helfer entwickeln? Vielleicht wird er wahrscheinlich Versionen haben. Lassen Sie den Entwickler auf eine bestimmte Version in seinem Dienst verweisen. Niemand zwingt ihn, den Dienst zu aktualisieren. Wenn Sie Helfer aktualisieren, ist dies eine Frage für diejenigen, die den Dienst unterstützen.
3. Java-Schnittstelle, abstrakte Basisklasse, Merkmal.
- Oder ein anderes Stück aus der Kategorie "zerrissenes Stück Code";
- Das heißt, Ich bin hier, unabhängig und unabhängig, und ein Stück meiner Leber liegt woanders.
- Hier erscheint die Kohärenz von Microservices auf Codeebene, daher werden wir sie nicht empfehlen.
- In der Anfangsphase wird dies wahrscheinlich keine greifbaren Probleme mit sich bringen, aber die Essenz der architektonischen Gestaltung ist die Garantie für Komfort (oder Unbehagen) für die kommenden Jahre.
Das Team, das mit der Arbeit an einem neuen Produkt beginnt, legt den Grundstein für die Architektur und hat den größten Einfluss darauf, welche Trends das Produkt haben wird. Wenn die Prinzipien von SRP, erfolgreicher Zerlegung, geringer Konnektivität usw. anfänglich in das System integriert werden, hat es die Chance, sich korrekt weiterzuentwickeln. Wenn nicht, wird die Zentrifugalbeschleunigung der „Zeitfaktoren“ (ein anderes Team, wenig Zeit, dringende Patches, fehlende Dokumentation) dieses System schneller als es scheint weiter an den Rand gedrängt.
Die Frage nach einem gemeinsamen Code in Microservices bleibt schwierig, da er mit einem Kompromiss verbunden ist: Wir wägen ab, was in Zukunft rentabler sein wird - der Grad der Unabhängigkeit von Microservices, weniger Wiederholungen im Code, die Qualifikation von Ingenieuren, die Einfachheit des Systems usw. Jedes Mal sind dies Reflexionen und Diskussionen, die zu unterschiedlichen spezifischen Architekturentscheidungen führen können. Lassen Sie uns dennoch einige der Empfehlungen zusammenfassen:
Empfehlung 0: Nennen Sie Microservices keine Dinge, die in unabhängig voneinander existierende Teile zerlegt sind. Nicht jede Tabelle mit Spalten ist eine Matrix. Verwenden wir die Begriffe korrekt.
Empfehlung 1: Es ist sehr wünschenswert, dass Microservices überhaupt keinen gemeinsamen Code haben.
Empfehlung 2: Wenn es noch einen gemeinsamen Code gibt, soll es sich um eine Sammlung (Bibliothek) optionaler „Helfer“ handeln. Der Serviceentwickler entscheidet, ob er sie verwendet oder seinen eigenen Code schreibt.
Empfehlung 3: Der allgemeine Code darf unter keinen Umständen eine Geschäftslogik enthalten. Die gesamte Geschäftslogik ist in Microservices zusammengefasst.
Empfehlung 4: Lassen Sie die gemeinsame Codebibliothek als Standardpaket (NuGet, Maven, NPM usw.) mit der Option der Versionierung (oder noch besser mehrerer separater Pakete) entworfen werden.
Empfehlung 5: Der „Schwerpunkt“ des Systems sollte immer in den Mikrodiensten selbst und nicht im allgemeinen Code verbleiben.
Empfehlung 6: Wenn Sie im Format von Microservices schreiben möchten, stimmen Sie im Voraus ab, dass der Code zwischen ihnen manchmal dupliziert wird. Bis zu einem gewissen Grad muss unser natürlicher „TROCKENER Instinkt“ unterdrückt werden.
Vielen Dank für Ihre Aufmerksamkeit und erfolgreiche Microservices.