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?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:
- Bestätigt das Anmeldeformular.
- Überprüfte die Risiken der Adresse des Benutzers, um zu entscheiden, ob ihm ein Bankkonto zur Verfügung gestellt werden soll.
- Bankkonto eröffnet.
BankController
wird 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. Bild, 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...
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 nunriskCheck()
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?
- Nennen wir alle separat bereitgestellten Dienste Microservices, unabhängig von ihrer Größe oder Domänengrenzen.
- 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.
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. Das 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:- Meistens ist es schwierig, solche Projekte aufrechtzuerhalten/zu ändern/zu erweitern.
- Jeder, vom Entwickler bis zum Management, wünscht sich eine Vereinfachung.
- Sie haben (relativ) klare Domänengrenzen und wissen daher genau, was Ihre Software tun soll.
- 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. Um 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 BrownProjekt von Grund auf basierend auf Microservice-Architektur
Bei neuen Java-Projekten sehen die drei nummerierten Punkte aus dem vorherigen Teil etwas anders aus:- Sie beginnen mit einer sauberen Weste, es gibt also kein „Gepäck“, das Sie verwalten müssen.
- Die Entwickler möchten es künftig einfach halten.
- Problem: Sie haben ein viel unschärferes Bild von Domänengrenzen: Sie wissen nicht, was Ihre Software eigentlich tun soll (Hinweis: agil ;))
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):- Prüft, ob die richtige XML-Datei empfangen wurde.
- 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.
- Kombiniert XML mit einigen anderen bürokratischen Daten.
- Leitet die XML-Datei an die Versicherungsgesellschaft weiter, um Zahlungen zu veranlassen.
- 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.“
- 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.
- 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.
- 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.
GO TO FULL VERSION