JavaRush /Java-Blog /Random-DE /Ein Leitfaden für Java-Microservices. Teil 1: Grundlagen ...

Ein Leitfaden für Java-Microservices. Teil 1: Grundlagen und Architektur von Microservices

Veröffentlicht in der Gruppe Random-DE
In diesem Leitfaden erfahren Sie, was Java-Microservices sind und wie Sie sie entwerfen und erstellen. Außerdem werden Fragen zu Java-Microservice-Bibliotheken und zur Machbarkeit der Verwendung von Microservices behandelt. Übersetzung und Anpassung von Java Microservices: Ein praktischer Leitfaden .

Java Microservices: Grundlagen

Um Microservices zu verstehen, müssen Sie zunächst definieren, was sie nicht sind. Ist es nicht ein „Monolith“ – Java-Monolith: Was ist das und was sind seine Vor- und Nachteile? Ein Leitfaden für Java-Microservices.  Teil 1: Grundlagen und Architektur von Microservices – 1

Was ist ein Java-Monolith?

Stellen Sie sich vor, Sie arbeiten für eine Bank oder ein Fintech-Startup. Sie stellen Benutzern eine mobile App zur Verfügung, mit der sie ein neues Bankkonto eröffnen können. Im Java-Code führt dies zum Vorhandensein einer Controller-Klasse. Vereinfacht sieht es so aus:
@Controller
class BankController {

    @PostMapping("/users/register")
    public void register(RegistrationForm form) {
        validate(form);
        riskCheck(form);
        openBankAccount(form);
        // etc..
    }
}
Sie benötigen den Controller, um:
  1. Bestätigt das Anmeldeformular.
  2. Überprüfte die Risiken der Adresse des Benutzers, um zu entscheiden, ob ihm ein Bankkonto zur Verfügung gestellt werden soll.
  3. Bankkonto eröffnet.
Die Klasse BankControllerwird zusammen mit den restlichen Quellen zur Bereitstellung in eine bank.jar- oder bank.war-Datei gepackt – dies ist ein guter alter Monolith, der den gesamten Code enthält, den Sie zum Betrieb Ihrer Bank benötigen. Als grobe Schätzung liegt die anfängliche Größe einer .jar- (oder .war-)Datei zwischen 1 und 100 MB. Jetzt können Sie einfach die .jar-Datei auf Ihrem Server ausführen … und das ist alles, was Sie tun müssen, um Ihre Java-Anwendung bereitzustellen. Ein Leitfaden für Java-Microservices.  Teil 1: Grundlagen und Architektur von Microservices – 2Bild, Rechteck oben links: Bereitstellung einer mono(lithischen) Bank java -jar bank.jar (cp .war/.ear in Appserver). Rechtes Rechteck: Browser öffnen.

Was ist das Problem mit Java-Monolithen?

An Java-Monolithen ist grundsätzlich nichts auszusetzen. Die Erfahrung zeigt jedoch, dass in Ihrem Projekt:
  • Es arbeiten viele Programmierer/Teams/Berater...
  • ...über den gleichen Monolithen unter dem Druck von Kunden mit sehr vagen Anforderungen...
  • innerhalb von ein paar Jahren...
... dann verwandelt sich in diesem Fall Ihre kleine bank.jar-Datei allein in ein riesiges Gigabyte an Code, dessen Annäherung, geschweige denn die Bereitstellung beängstigend ist.

Wie kann die Größe eines Java-Monolithen reduziert werden?

Es stellt sich natürlich die Frage: Wie kann man den Monolithen verkleinern? Im Moment läuft Ihr bank.jar auf einer JVM, einem Prozess auf einem Server. Nicht mehr und nicht weniger. Und jetzt kommt mir vielleicht ein logischer Gedanke in den Sinn: „Der Risikoverifizierungsdienst kann aber auch von anderen Abteilungen in meinem Unternehmen genutzt werden!“ Es steht nicht in direktem Zusammenhang mit meiner monolithischen Bankanwendung! Vielleicht sollte es aus dem Monolithen herausgeschnitten und als separates Produkt eingesetzt werden? Das bedeutet technisch gesehen, dass es als separater Java-Prozess ausgeführt wird.“

Was ist ein Java-Microservice?

In der Praxis bedeutet dieser Satz, dass der Methodenaufruf nun riskCheck()nicht mehr von BankController aus erfolgt: Diese Methode oder Bean-Komponente mit all ihren Hilfsklassen wird in ein eigenes Maven- oder Gradle-Projekt verschoben. Es wird auch unabhängig vom Bankenmonolithen bereitgestellt und unter Versionskontrolle gestellt. Dieser gesamte Extraktionsprozess verwandelt Ihr neues RiskCheck-Modul jedoch nicht per se in einen Microservice, da die Definition eines Microservices offen für Interpretationen ist. Dies führt zu häufigen Diskussionen innerhalb von Teams und Unternehmen.
  • Sind 5-7 Klassen in einem Projektmikro oder was?
  • 100 oder 1000 Klassen... immer noch Mikro?
  • Hängt Microservice im Allgemeinen mit der Anzahl der Klassen zusammen oder nicht?
Lassen wir die theoretischen Überlegungen hinter uns, bleiben wir stattdessen bei pragmatischen Überlegungen und tun wir Folgendes:
  1. Nennen wir alle separat bereitgestellten Dienste Microservices, unabhängig von ihrer Größe oder Domänengrenzen.
  2. Lassen Sie uns darüber nachdenken, wie wir die Kommunikation zwischen den Diensten organisieren können. Unsere Microservices benötigen Möglichkeiten, miteinander zu kommunizieren.
Um es zusammenzufassen: Früher gab es einen JVM-Prozess, einen soliden Monolithen für den Betrieb der Bank. Jetzt verfügen Sie über einen monolithischen Banking-JVM-Prozess und einen separaten RiskCheck-Mikroservice, der innerhalb seines eigenen JVM-Prozesses ausgeführt wird. Und um nun nach Risiken zu suchen, muss Ihr Monolith diesen Microservice aufrufen. Wie macht man das?

Wie stelle ich die Kommunikation zwischen Java-Microservices her?

Im Allgemeinen gibt es zwei Möglichkeiten – synchrone und asynchrone Kommunikation.

Synchrone Kommunikation: (HTTP)/REST

Typischerweise erfolgt die synchronisierte Kommunikation zwischen Microservices über HTTP und REST-ähnliche Dienste, die XML oder JSON zurückgeben. Natürlich kann es auch andere Optionen geben – nehmen Sie zumindest Google Protocol Buffers . Wenn Sie eine sofortige Antwort benötigen, ist es besser, die REST-Kommunikation zu verwenden. In unserem Beispiel ist genau dies erforderlich, da vor der Kontoeröffnung eine Risikoprüfung erforderlich ist. Ohne Risikoprüfung gibt es kein Konto. Auf die Tools gehen wir weiter unten im Abschnitt „ Welche Bibliotheken eignen sich am besten für synchrone Java-REST-Aufrufe “ ein.

Messaging – Asynchrone Kommunikation

Die asynchrone Microservice-Kommunikation erfolgt typischerweise durch den Austausch von Nachrichten mit einer JMS-Implementierung und/oder durch die Verwendung eines Protokolls wie AMQP . Wir haben hier aus einem bestimmten Grund „normalerweise“ geschrieben: Nehmen wir an, die Anzahl der E-Mail-/SMTP-Integrationen ist nicht zu unterschätzen. Verwenden Sie es, wenn Sie keine sofortige Antwort benötigen. Beispielsweise klickt der Nutzer auf die Schaltfläche „Jetzt kaufen“ und Sie möchten wiederum eine Rechnung erstellen. Dieser Prozess sollte auf keinen Fall innerhalb des Kaufanfrage-Antwort-Zyklus des Benutzers stattfinden. Im Folgenden beschreiben wir, welche Tools für asynchrones Java-Messaging am besten geeignet sind .

Beispiel: REST-API-Aufruf in Java

Nehmen wir an, wir entscheiden uns für die synchrone Microservice-Kommunikation. In diesem Fall sieht unser Java-Code (den wir oben vorgestellt haben) auf einer niedrigen Ebene in etwa so aus. (Mit Low Level meinen wir hier die Tatsache, dass für die Microservice-Kommunikation normalerweise Client-Bibliotheken erstellt werden, die Sie von den eigentlichen HTTP-Aufrufen abstrahieren.)
@Controller
class BankController {

    @Autowired
    private HttpClient httpClient;

    @PostMapping("/users/register")
    public void register(RegistrationForm form) {
        validate(form);
        httpClient.send(riskRequest, responseHandler());
        setupAccount(form);
        // etc..
    }
}
Anhand des Codes wird deutlich, dass wir nun zwei Java-(Mikro-)Dienste bereitstellen müssen: Bank und RiskCheck. Als Ergebnis werden zwei JVM-Prozesse ausgeführt. Ein Leitfaden für Java-Microservices.  Teil 1: Grundlagen und Architektur von Microservices – 3Das ist alles, was Sie zum Entwickeln eines Java-Microservices-Projekts benötigen: Erstellen und implementieren Sie einfach kleinere Blöcke (.jar- oder .war-Dateien) anstelle eines monolithischen. Die Antwort auf die Frage bleibt unklar: Wie sollen wir den Monolithen in Microservices zerlegen? Wie klein sollten diese Stücke sein, wie ermittelt man die richtige Größe? Lass uns das Prüfen.

Java Microservices-Architektur

In der Praxis entwickeln Unternehmen Microservice-Projekte auf unterschiedliche Weise. Der Ansatz hängt davon ab, ob Sie versuchen, einen bestehenden Monolithen in ein Microservices-Projekt umzuwandeln oder das Projekt von Grund auf neu zu starten.

Vom Monolithen zum Microservice

Eine der logischsten Ideen besteht darin, Microservices aus einem vorhandenen Monolithen zu extrahieren. Beachten Sie, dass das Präfix „micro“ hier nicht wirklich bedeutet, dass die extrahierten Dienste wirklich klein sind; das ist nicht unbedingt der Fall. Schauen wir uns den theoretischen Hintergrund an.

Idee: Den Monolithen in Microservices aufteilen

Ein Microservice-Ansatz kann auf Legacy-Projekte angewendet werden. Und deshalb:
  1. Meistens ist es schwierig, solche Projekte aufrechtzuerhalten/zu ändern/zu erweitern.
  2. Jeder, vom Entwickler bis zum Management, wünscht sich eine Vereinfachung.
  3. Sie haben (relativ) klare Domänengrenzen und wissen daher genau, was Ihre Software tun soll.
Um auf unser Beispiel zurückzukommen: Das bedeutet, dass Sie sich Ihren Java-Banking-Monolithen ansehen und versuchen können, ihn über Domänengrenzen hinweg aufzuschlüsseln.
  • Daher wäre es sinnvoll, die Verarbeitung von Benutzerdaten (wie Namen, Adressen, Telefonnummern) in einen separaten Microservice „Account Management“ zu trennen.
  • Oder das oben erwähnte „Risk-Checker-Modul“, das den Risikograd des Benutzers überprüft und von vielen anderen Projekten oder sogar Abteilungen des Unternehmens verwendet werden kann.
  • Oder ein Rechnungsmodul, das Rechnungen im PDF-Format oder per Post versendet.

Eine Idee umsetzen: jemand anderen machen lassen

Der oben beschriebene Ansatz sieht auf Papier und UML-ähnlichen Diagrammen großartig aus. Allerdings ist nicht alles so einfach. Die praktische Umsetzung erfordert eine ernsthafte technische Vorbereitung: Die Lücke zwischen unserem Verständnis dessen, was aus einem Monolithen gewonnen werden könnte, und dem Extraktionsprozess selbst ist enorm. Die meisten Unternehmensprojekte erreichen ein Stadium, in dem Entwickler Angst davor haben, beispielsweise eine 7 Jahre alte Version von Hibernate auf eine neuere Version zu aktualisieren. Auch die Bibliotheken werden aktualisiert, aber es besteht die reale Gefahr, dass etwas kaputt geht. Dieselben Entwickler müssen sich jetzt also durch alten Legacy-Code mit unklaren Datenbanktransaktionsgrenzen wühlen und genau definierte Microservices extrahieren? Meistens ist dieses Problem sehr komplex und kann nicht auf einem Whiteboard oder in Architekturbesprechungen „gelöst“ werden. Ein Leitfaden für Java-Microservices.  Teil 1: Grundlagen und Architektur von Microservices – 4Um den Twitter-Entwickler @simonbrown zu zitieren: Ich sage das immer und immer wieder … wenn die Leute Monolithen nicht richtig bauen können, helfen Microservices nicht. Simon Brown

Projekt von Grund auf basierend auf Microservice-Architektur

Bei neuen Java-Projekten sehen die drei nummerierten Punkte aus dem vorherigen Teil etwas anders aus:
  1. Sie beginnen mit einer sauberen Weste, es gibt also kein „Gepäck“, das Sie verwalten müssen.
  2. Die Entwickler möchten es künftig einfach halten.
  3. Problem: Sie haben ein viel unschärferes Bild von Domänengrenzen: Sie wissen nicht, was Ihre Software eigentlich tun soll (Hinweis: agil ;))
Dies führt dazu, dass Unternehmen neue Projekte mit Java-Microservices ausprobieren.

Technische Microservice-Architektur

Der erste Punkt scheint für Entwickler am offensichtlichsten zu sein, aber es gibt auch diejenigen, die davon dringend abraten. Hadi Hariri empfiehlt das Refactoring „Extract Microservice“ in IntelliJ. Und obwohl das folgende Beispiel sehr vereinfacht ist, weichen die in realen Projekten beobachteten Implementierungen leider nicht allzu weit davon ab. Vor Microservices
@Service
class UserService {

    public void register(User user) {
        String email = user.getEmail();
        String username =  email.substring(0, email.indexOf("@"));
        // ...
    }
}
Mit Substring-Java-Microservice
@Service
class UserService {

    @Autowired
    private HttpClient client;

    public void register(User user) {
        String email = user.getEmail();
        //теперь вызываем substring microservice via http
        String username =  httpClient.send(substringRequest(email), responseHandler());
        // ...
    }
}
Sie verpacken also im Wesentlichen einen Java-Methodenaufruf in einen HTTP-Aufruf, ohne dass dafür ein ersichtlicher Grund erkennbar ist. Ein Grund ist jedoch folgender: mangelnde Erfahrung und der Versuch, einen Java-Microservices-Ansatz zu erzwingen. Empfehlung: Tun Sie dies nicht.

Workfloworientierte Microservice-Architektur

Der nächste gängige Ansatz besteht darin, Java-Microservices in Workflow-basierte Module zu unterteilen. Beispiel aus der Praxis: Wenn Sie in Deutschland einen (öffentlichen) Arzt aufsuchen, muss dieser Ihren Besuch in seinem medizinischen CRM-System erfassen. Um die Zahlung von der Versicherung zu erhalten, sendet er Daten über Ihre Behandlung (und die Behandlung anderer Patienten) per XML an den Vermittler. Der Broker schaut sich diese XML-Datei an und (vereinfacht):
  1. Prüft, ob die richtige XML-Datei empfangen wurde.
  2. Dabei wird die Plausibilität der Eingriffe überprüft: Beispielsweise sieht ein einjähriges Kind, das an einem Tag drei Zahnreinigungen beim Frauenarzt erhalten hat, etwas verdächtig aus.
  3. Kombiniert XML mit einigen anderen bürokratischen Daten.
  4. Leitet die XML-Datei an die Versicherungsgesellschaft weiter, um Zahlungen zu veranlassen.
  5. Und es sendet das Ergebnis an den Arzt und übermittelt ihm die Nachricht „Erfolgreich“ oder „Bitte senden Sie diese Aufzeichnung erneut, sobald es sinnvoll ist.“
Notiz. Die Kommunikation zwischen Microservices spielt in diesem Beispiel keine Rolle, könnte aber durchaus asynchron durch einen Message Broker (z. B. RabbitMQ) erfolgen, da der Arzt ohnehin kein unmittelbares Feedback erhält. Ein Leitfaden für Java-Microservices.  Teil 1: Grundlagen und Architektur von Microservices – 5Auch das sieht auf dem Papier gut aus, aber es stellen sich natürlich Fragen:
  • Ist es notwendig, sechs Anwendungen bereitzustellen, um eine XML-Datei zu verarbeiten?
  • Sind diese Microservices wirklich unabhängig voneinander? Können sie unabhängig voneinander eingesetzt werden? Mit unterschiedlichen Versionen und API-Schemata?
  • Was macht der Glaubwürdigkeits-Microservice, wenn der Verifizierungs-Microservice nicht funktioniert? Funktioniert das System noch?
  • Nutzen diese Microservices dieselbe Datenbank (sie benötigen auf jeden Fall einige gemeinsame Daten in den DB-Tabellen) oder haben sie jeweils ihre eigene?
  • … und vieles mehr.
Interessanterweise sieht das obige Diagramm einfacher aus, da jeder Dienst jetzt seinen eigenen genauen, klar definierten Zweck hat. Früher sah es ungefähr so ​​aus wie dieser gruselige Monolith: Ein Leitfaden für Java-Microservices.  Teil 1: Grundlagen und Architektur von Microservices – 6Während man über die Einfachheit dieser Diagramme streiten kann, müssen Sie jetzt definitiv diese zusätzlichen betrieblichen Herausforderungen lösen.
  • Sie müssen nicht nur eine Anwendung bereitstellen, sondern mindestens sechs.
  • Möglicherweise müssen Sie sogar mehrere Datenbanken bereitstellen, je nachdem, wie weit Sie in die Microservices-Architektur vordringen möchten.
  • Sie müssen sicherstellen, dass jedes System online ist und ordnungsgemäß funktioniert.
  • Sie müssen sicherstellen, dass Ihre Aufrufe zwischen Microservices wirklich widerstandsfähig sind (siehe Wie macht man einen Java-Microservice widerstandsfähig?).
  • Und alles andere, was dieses Setup mit sich bringt – von lokalen Entwicklungseinstellungen bis hin zu Integrationstests.
Die Empfehlung wäre also:
  • Wenn Sie nicht Netflix sind (wahrscheinlich sind Sie nicht Netflix) ...
  • Es sei denn, Sie verfügen über besonders ausgeprägte Arbeitsfähigkeiten, wenn Sie die Entwicklungsumgebung öffnen und dadurch ein Chaos entsteht, das Ihre Produktionsdatenbank wegwirft, die problemlos in 5 Sekunden wiederhergestellt werden kann.
  • oder Sie fühlen sich wie @monzo und sind bereit, 1500 Microservices auszuprobieren, nur weil Sie es können.
→ Tun Sie das nicht. Und jetzt ist es weniger hyperbolisch. Der Versuch, Microservices über Domänengrenzen hinweg zu modellieren, erscheint durchaus sinnvoll. Dies bedeutet jedoch nicht, dass Sie einen Arbeitsablauf in winzige Einzelteile aufteilen müssen (XML empfangen, XML validieren, XML weiterleiten). Wenn Sie also ein neues Projekt mit Java-Microservices starten und die Domänengrenzen noch sehr vage sind, versuchen Sie, die Größe Ihrer Microservices niedrig zu halten. Sie können später jederzeit weitere Module hinzufügen. Und stellen Sie sicher, dass Ihr Team/Unternehmen/Abteilung über fortschrittliche DevOps verfügt, um Ihre neue Infrastruktur zu unterstützen.

Polyglotte oder teamorientierte Microservice-Architektur

Es gibt einen dritten, fast libertären Ansatz zur Entwicklung von Microservices: Teams oder sogar Einzelpersonen ermöglichen, User Stories mithilfe einer beliebigen Anzahl von Sprachen oder Microservices zu implementieren (Vermarkter nennen diesen Ansatz „polyglotte Programmierung“). Somit könnte der oben beschriebene XML-Validierungsdienst in Java geschrieben werden, während der Validierungs-Microservice gleichzeitig in Haskell geschrieben werden könnte (um es mathematisch fundiert zu machen). Für den Versicherungsweiterleitungs-Microservice können Sie Erlang verwenden (da es wirklich skalierbar sein muss ;)). Was aus der Sicht eines Entwicklers Spaß machen mag (das perfekte System mit der perfekten Sprache in einer isolierten Umgebung zu entwickeln), ist in Wirklichkeit nie das, was die Organisation will: Homogenisierung und Standardisierung. Dies bedeutet einen relativ standardisierten Satz von Sprachen, Bibliotheken und Tools, sodass andere Entwickler Ihren Haskell-Microservice auch in Zukunft weiterhin unterstützen können, wenn Sie sich in grünere Gefilde begeben. Ein Leitfaden für Java-Microservices.  Teil 1: Grundlagen und Architektur von Microservices – 8Die Geschichte zeigt, dass die Standardisierung meist zu tief verwurzelt ist. Beispielsweise durften Entwickler großer Fortune-500-Unternehmen Spring manchmal nicht einmal verwenden, weil es „nicht Teil der Technologie-Roadmap des Unternehmens“ war. Ein vollständiger Übergang zum polyglotten Ansatz ist jedoch fast dasselbe, die Kehrseite derselben Medaille. Empfehlung: Wenn Sie mehrsprachige Programmierung verwenden möchten, versuchen Sie es mit weniger Abwechslung innerhalb desselben Programmiersprachen-Ökosystems. Daher ist es besser, Kotlin und Java zusammen zu verwenden (beide Sprachen basieren auf der JVM und sind zu 100 % miteinander kompatibel) als Java und beispielsweise Haskell. Im nächsten Teil erfahren Sie mehr über die Bereitstellung und das Testen von Java-Microservices.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION