Die Schmerzen und Leiden beim Debuggen von Microservices in der Webentwicklung

In der IT sehen Sie selten eine Person, die noch nichts von Microservices gehört hat. Im Internet und auf speziellen Websites zu diesem Thema gibt es viele Artikel, die die Unterschiede zwischen dem Monolithen und den Mikrodiensten im Allgemeinen gut erklären. Ein unerfahrener Java-Entwickler, der Artikel aus der Kategorie „Was sind Microservices für Webanwendungen und womit sie essen“ gelesen hat, ist voller Freude und Zuversicht, dass jetzt alles wunderbar wird. Schließlich besteht das Hauptziel darin, den monströsen Monolithen (das letzte Artefakt, bei dem es sich in der Regel um eine Kriegs- / Ohr-Datei handelt) zu „durchschauen“, der eine Reihe von Dingen für eine Reihe von separat lebenden Diensten ausführt, von denen jeder eine streng definierte Funktion erfüllt, die nur mit ihm zusammenhängt. und wird es gut machen. Hinzu kommt die horizontale Skalierbarkeit - skalieren Sie einfachentsprechende Knoten, und alles wird großartig sein. Es sind mehr Benutzer angekommen oder es sind mehr Kapazitäten erforderlich - es wurden nur 5-10 neue Dienstinstanzen hinzugefügt. Grob gesagt funktioniert das im Allgemeinen so, aber wie Sie wissen, steckt der Teufel im Detail, und was bei näherer Betrachtung zunächst recht einfach schien, kann zu Problemen werden, die anfangs niemand berücksichtigt hat.

In diesem Beitrag teilen Kollegen aus der Java-Praxis von Rexoft ihre Erfahrungen mit dem Debuggen von Microservices für das Web.



So erreichen Sie die Integrität von Transaktionsdaten


Beim Versuch, die Architektur von einem Monolithen auf Microservices zu übertragen, beginnen Teams, die zuvor keine solche Erfahrung hatten, häufig, Services in Objekte der obersten Ebene des Domänenmodells aufzuteilen, z. B.: Benutzer / Client / Mitarbeiter usw. In Zukunft wird mit einer detaillierteren Studie Verständnis angezeigt. Dies ist bequemer, um in größere Blöcke zu zerlegen, die mehrere Objekte der Domänendomäne in sich zusammenfassen. Auf diese Weise können Sie unnötige Anrufe bei Diensten von Drittanbietern vermeiden.

Der zweite wichtige Punkt ist die Unterstützung der Integrität von Transaktionsdaten. Im Monolithen wird dieses Problem durch den Anwendungsserver gelöst, auf dem sich Krieg / Ohr dreht, in dem der Container tatsächlich die Grenzen von Transaktionen umreißt. Im Fall von Microservices verschwimmen die Grenzen von Transaktionen, und es besteht neben dem Schreiben von Geschäftslogikcode die Notwendigkeit, die Datenintegrität zu verwalten und ihre Konsistenz zwischen verschiedenen Teilen des Systems aufrechtzuerhalten. Dies ist eine ziemlich nicht triviale Aufgabe. Empfehlungen zur Lösung dieser Art von Architekturproblemen finden Sie im Internet und in den relevanten Fachgemeinschaften.

In diesem Artikel werden wir versuchen, bestimmte technische Schwierigkeiten zu beschreiben, die auftreten, wenn Teams versuchen, mit Microservices zu arbeiten, und Möglichkeiten, diese zu lösen. Ich stelle sofort fest, dass die vorgeschlagenen Optionen nicht die einzig wahren sind. Vielleicht gibt es elegantere Dienstleistungen, aber die Empfehlungen, die ich geben werde, werden in der Praxis getestet und lösen die bestehenden Schwierigkeiten genau, und ob sie genutzt werden sollen oder nicht, ist eine persönliche Angelegenheit für alle.

Das Hauptproblem bei Microservices besteht darin, dass sie lokal sehr einfach ausgeführt werden können (mit spring.io und Intellij Idea kann dies beispielsweise in nur 5 Minuten oder sogar weniger erledigt werden). Wenn Sie jedoch versuchen, dasselbe in Kubernetes zu tunIm Cluster (wenn Sie zuvor wenig Erfahrung damit hatten) kann ein einfacher Start des Controllers, der beim Zugriff auf einen bestimmten Endpunkt „Hello World“ druckt, einen halben Tag dauern. Im Fall eines Monolithen ist die Situation einfacher. Jeder Entwickler hat einen lokalen Anwendungsserver. Der Bereitstellungsprozess ist ebenfalls recht einfach: Sie müssen das endgültige Kriegs- / Ohrartefakt manuell oder mithilfe der IDE an die richtige Stelle im Anwendungsserver kopieren . Normalerweise ist dies kein Problem.

Nuancen debuggen


Der zweite wichtige Punkt ist das Debuggen . In Situationen mit einem Monolithen wird davon ausgegangen, dass der Entwickler einen Anwendungsserver auf seinem Computer hat, auf dem sein Krieg / Ohr bereitgestellt wird. Sie können jederzeit debuggen, da alles, was Sie benötigen, zur Hand ist. Bei Microservices ist alles etwas komplizierter, ein Service ist normalerweise eine Sache für sich. In der Regel verfügt er über ein eigenes Datenbankschema, in dem sich seine Daten befinden, bestimmte für ihn spezifische Funktionen ausführen und die gesamte Kommunikation mit anderen Diensten über synchrone HTTP-Aufrufe (z. B. über RestTemplate oder Feign), asynchron (z. B. Kafka oder RabbitMQ) organisiert wird. Daher wird die im Wesentlichen einfache Aufgabe des Speicherns oder Validierens eines bestimmten Objekts, das zuvor an einem Ort innerhalb einer einzelnen Kriegs- / Ohr-Datei implementiert wurde, im allgemeinen Fall mit einem Microservice-Ansatz in der Form darstellbar: Gehen Sie zu einem oder N benachbarten Diensten, sei es Datenerfassungsoperationen B. einige Referenzwerte oder die Operation zum Speichern benachbarter Entitäten,deren Daten benötigt werden, um Geschäftslogik in unserem Service auszuführen. Das Schreiben von Geschäftslogik wird in diesem Fall viel schwieriger.

Dementsprechend sind die Lösungsoptionen wie folgt :

  1. Schreiben Sie Ihren Geschäftslogikcode. Gleichzeitig werden alle externen Anrufe verspottet - externe Verträge werden emuliert, Tests werden unter der Annahme geschrieben, dass externe Verträge einfach so sind, und anschließend wird sie zur Überprüfung in der Leitung bereitgestellt. Manchmal ist es ein Glücksfall und die Integration funktioniert sofort, manchmal ist es unglücklich - Sie müssen den Geschäftslogikcode ein n-mal mehrmals wiederholen, da während der Zeit, in der wir die Funktionalität implementiert haben, der Code im benachbarten Dienst aktualisiert wurde, die API-Signaturen geändert wurden und wir ihn wiederholen müssen Ein Teil der Aufgabe ist auf seiner Seite.
  2. . , , Kubernetes, . . , — , remote debug . , runtime , , . -, , 2–5 , . . , Kubernetes , . -, (Per thread), , .

Kubernetes


Eine Lösung für dieses Problem ist in der Tat die Telepräsenz . Es gibt wahrscheinlich andere Programme dieser Art, aber persönliche Erfahrung war nur mit ihm, und er etablierte sich positiv. Im Allgemeinen lautet das Funktionsprinzip wie folgt:

Auf dem lokalen Computer installiert der Entwickler telepresenc e, konfiguriert kubectl für den Zugriff auf den entsprechenden Kubernetes-Cluster (fügt die Schleifenkonfiguration zu ~ / .kube / config hinzu ). Danach beginnt die Telepräsenz , die tatsächlich als Proxy zwischen dem lokalen Entwicklercomputer und Kubernetes fungiert. Es gibt verschiedene Startoptionen. Es ist besser, im offiziellen Handbuch genauer nachzuschauen, aber im einfachsten Fall sind es zwei Schritte:

  1. Sudo telepresence (, Linux- , sudo . , root/). Kubernetes deployment telepresence . deployment Kubernetes.
  2. Das Starten Ihrer Dienstinstanz erfolgt wie gewohnt auf dem lokalen Computer des Entwicklers. In diesem Fall hat er jedoch Zugriff auf die gesamte Infrastruktur des Kubernetes-Clusters, sei es Service Discovery (Eureka, Consul), Api Gateway (Zuul), Kafka und gegebenenfalls die Warteschlangen usw. Das heißt, wir haben Zugriff auf alle benötigten Clusterumgebungen, jedoch lokal. Der Bonus ist die Möglichkeit des lokalen Debuggens, jedoch in einer Clusterumgebung, und es wird bereits viel schneller sein, da wir uns tatsächlich in Kubernetes (durch den Tunnel) befinden und nicht von außen über einen Port für das Remote-Debuggen darauf zugreifen.

Diese Lösung hat mehrere Nachteile:

  1. Telepresence Linux Mac, Windows VFS, , issue GitHub. . , - Linux/Mac, .
  2. , Service Discovery (Eureka, Consul)Round Robin , endpoint , , , :

  • kubernetes -> . telepresence deployment , «» Eureka ip-address:port/service-name dns-name:port/service-name , . . Kubernetes , timeout;
  • deployment - Kubernetes , ( ) (Round Robin), ;
  • endpoint, feature, HTTP 404 endpoint Gateway, Service Discovery , Round Robin . Service Discovery endpoint , HTTP 404.
  • , , .


Unter dynamischem Routing einer Anforderung verstehen wir, dass das API-Gateway (Zuul) zwischen mehreren Instanzen desselben Dienstes wählen kann, die wir benötigen. Im allgemeinen Fall kann dieses Problem gelöst werden, indem ein Prädikat hinzugefügt wird, mit dem Sie in der Anforderungsverarbeitungsphase den gewünschten Dienst aus dem gemeinsamen Dienstpool mit demselben Namen auswählen können. Natürlich muss jeder Dienst unter denen, mit denen wir dynamisch routen möchten, über eine Art Metainformation verfügen, die Daten enthält, anhand derer bestimmt wird, ob dieser Dienst benötigt wird oder nicht. Mit Spring Cloud (im Fall von Eureka) können Sie dies beispielsweise tun, indem Sie in application.yml in einem speziellen Metadatenblock Folgendes angeben :

eureka:
  instance:
    preferIpAddress: true
    metadata-map:
      service.label: develop

Nach der Registrierung einen solchen Dienst in Service Discovery in seiner com.netflix.appinfo.InstanceInfo # getMetadata es wird ein Etikett mit dem Schlüssel service.label und den Wert entwickeln , die in der Laufzeit erhalten werden kann. Ein wichtiger Punkt zu Beginn eines Dienstes ist die Überprüfung, ob in Service Discovery eine Dienstinstanz mit solchen Metainformationen vorhanden ist oder nicht, um mögliche Kollisionen zu vermeiden.

Routing-Optionen


Danach kann die Lösung des Problems auf zwei Optionen reduziert werden:

  1. API Gateway . , , , , Headers: DestionationService: feature/PRJ-001. , , Header . , — - API Gateway.
  2. API Gateway, , . ., , , Zuul 1 endpoint- /api/users/… user, feature/PRJ-001, Zuul 2 endpoint- /api/users/… user, feature/PRJ-002. , N API Gateway N , . . , . . feature — , , , , , , . API Gateway, , . ., , , — , .






Als Teil der Gateway-API lohnt es sich auch, einen Mechanismus bereitzustellen, mit dem Sie Routing-Regeln zur Laufzeit ändern können. Am besten platzieren Sie diese Einstellungen in der Konfigurationszuordnung . In diesem Fall reicht es aus, die neuen Routen neu zu schreiben und entweder die Gateway-API in Kubernetes neu zu starten, um das Routing zu aktualisieren, oder den Spring Boot Actuator zu verwenden (vorausgesetzt, die Gateway-API enthält eine entsprechende Abhängigkeit) - rufen Sie den Endpunkt / die Aktualisierung auf, der im Wesentlichen neu gelesen wird Daten von config-map und aktualisiert Routen.

Ein wichtiger Punkt ist auch, dass es relativ gesehen eine Referenzinstanz des Dienstes geben sollte (z. B. mit der Bezeichnung " Entwickeln")(die vom Hauptzweig der Serviceentwicklung gesammelt werden) und eine separate Haupt-Gateway-API, die immer in den Einstellungen angegeben wird, in denen auf diesen Service zugegriffen wird. Im Wesentlichen stellen wir uns eine unabhängige Staging- Umgebung zur Verfügung, die im Rahmen des dynamischen Routings immer betriebsbereit ist.

Ein Beispiel für einen Konfigurationszuordnungsblock für die Gateway-API, der Einstellungen für das Routing enthält (hier nur ein Beispiel für das Aussehen, für den ordnungsgemäßen Betrieb ist eine entsprechende Bindung in Form von Code auf der Backend-Seite des API-Gateway- Dienstes erforderlich ) :

{
  "kind": "ConfigMap",
  "apiVersion": "v1",
  "metadata": {
    ...
  },  
"data": {
    ...        
    "rules.meta.user": "develop",
    "rules.meta.client": "develop",
    "rules.meta.notification": "feature/PRJ-010",
    ...    
  }
}

rules.meta ist eine Karte mit Routing-Regeln für Dienste.
Benutzer / Kunde / Benachrichtigung - Der Name des Dienstes, unter dem er in Eureka registriert ist.

Develop / Feature / PRJ-010 - Service-Label aus application.yml des entsprechenden Service, auf dessen Grundlage der gewünschte Service unter allen verfügbaren Services mit demselben Namen aus Service Discovery ausgewählt wird , wenn mehr als eine Instanz eines solchen Service vorhanden ist.

Fazit


Wie alles auf dieser Welt sind die Tools und Lösungen in der IT nicht perfekt. Denken Sie nicht, dass alle Probleme sofort enden, wenn Sie die Architektur ändern. Nur ein detailliertes Eintauchen in die verwendeten Technologien und Ihre eigenen Erfahrungen geben Ihnen ein reales Bild davon, was passiert.

Ich hoffe, dieses Material hilft Ihnen bei der Lösung Ihres Problems. Interessante Aufgaben und verkaufen ohne Fehler!

All Articles