Wie wir eine Anwendung auf Basis einer Microservice-Architektur erstellen, auf welche Features wir stoßen und wie wir sie umgehen

Hallo! Mein Name ist Ivan, ich bin Lead Engineer bei Nexign. In diesem Artikel möchte ich ein wenig über die Lösung zur Unterstützung der Geschäftsprozesse und des Revenue Managements des Betreibers sprechen und die Erfahrung bei der Entwicklung einer seiner Komponenten mithilfe von Microservices teilen. Diese Informationen sind sowohl für Ingenieure nützlich, die die Microservice-Architektur in der Anwendungsentwicklung verwenden werden, als auch für Produktbesitzer und -manager, die ihre Grundlagen verstehen müssen, um die mit dem Projekt verbundenen Risiken einzuschätzen.

Was sind Lösungen zur Unterstützung der Geschäftsprozesse des Betreibers?

Lassen Sie uns zunächst definieren, welche Lösungen zur Unterstützung der Geschäftsprozesse und des Revenue Managements des Betreibers geeignet sind. Dies sind Produkte für die Arbeit mit den Finanzobjekten des Kunden, die die Automatisierung von Abrechnungsprozessen und die Abrechnung von Dienstleistungen für Kunden auf der Grundlage von Daten zu Zahlungen, Anpassungen, aggregierten Informationen über Verbrauchsereignisse und damit verbundene Gebühren umfassen. Revenue-Management-Lösungen umfassen auch die Forderungsautomatisierung. Wir haben architektonisch einen separaten Funktionsblock für diese Aufgabe ausgewählt und begonnen, ihn mithilfe von Microservices zu erstellen.

Unser Funktionsblock ist darauf ausgelegt, die Entscheidungsfindung über die Bildung von Kontrollmaßnahmen in externen Systemen auf der Grundlage der Analyse von Finanzdaten und Schlüsselmerkmalen des Kunden aus verschiedenen Quellen zu automatisieren. Um dieses Ziel zu erreichen, werden Geschäftsregeln (Maßnahmen) verwendet. Sie werden zu Arbeitsplänen zusammengefasst, die die Reihenfolge der Anwendung von Maßnahmen umsetzen, wenn die durch verschiedene Parameter festgelegten Bedingungen erfüllt sind.

Der Funktionsblock zur Automatisierung der Arbeit mit Forderungen umfasst folgende Funktionen:

  • Konfiguration und Speicherung von Maßnahmen und Arbeitsplänen;

  • Treffen von Entscheidungen über die Verwendung einer bestimmten Maßnahme mit anschließender Bildung von Kontrollmaßnahmen für externe Systeme;

  • Bereitstellung von Informationen an Drittsysteme über den Fortschritt der Arbeit mit Schulden.

Wie oben erwähnt, haben wir uns entschieden, unseren Block nach der Microservice-Architektur zu bauen. Die Hauptfaktoren bei der Wahl dieses Ansatzes waren die Flexibilität bei Entwicklung, Verwaltung und Skalierung. Dennoch ist jede Entwicklung mit einer Reihe von Schwierigkeiten verbunden, und ich werde Ihnen sagen, welche Strategien wir verfolgen, um einige davon zu lösen.

Microservices schneiden

Die erste Schwierigkeit, die mit einer Microservice-Architektur verbunden ist, ist das Slicing von Microservices.

Ein Microservice ist eine in sich geschlossene, unabhängig einsetzbare Softwarekomponente, die bestimmte nützliche Funktionen implementiert. Seine API kann Befehle, Anforderungen oder Ereignisse enthalten. Die richtige Zerlegung in Microservices und die Definition, wie sie interagieren, erspart Ihnen tonnenweise verbrannte Manntage, die mit der „Neuzusammenstellung“ von Services in der Weiterentwicklung der Lösung verbunden sind.

Beim Slicing von Microservices lohnt es sich, sich an eines der Hauptprinzipien des Ansatzes zu erinnern – die lose Kopplung von Services. Microservices sollten unabhängig voneinander sein. Idealerweise sollten sie keine gemeinsamen Strukturen und Datenspeicher haben und können auch unterschiedliche interne Architekturen und manchmal einen separaten Technologie-Stack haben. Mit anderen Worten, jeder Dienst sollte seine eigene API und seine eigene Datenbank oder ein separates Schema haben, wenn er eine Datenpersistenz benötigt, und sollte in der Lage sein, eine begrenzte und verwandte Menge von Aufgaben gut zu bewältigen. Das Slicing von Microservices hängt auch von Netzwerklatenzen ab, um die Datenkonsistenz zwischen den Services beim Schreiben sicherzustellen, da einige Vorgänge gezwungen sind, Daten in mehreren Services zu aktualisieren, und um eine konsistente Ansicht der Daten zu erhalten, da Informationen aus verschiedenen Datenbanken stammen können.

Bei der Entwicklung unseres Funktionsblocks haben wir den folgenden Algorithmus zum Slicing von Microservices gewählt:

  • Definition funktionaler Anforderungen: Erstellung eines verallgemeinerten Domänenmodells und Definition von Systemoperationen;

  • Verteilung auf Services durch Geschäftsfunktionen (dies ist aufgrund von Schnittmengen nicht immer möglich);

  • Dienst-API-Definition.

Basierend auf dem Zweck unseres Funktionsblocks prüfen wir die Möglichkeit, das Produkt in die folgenden Dienste zu zerlegen:

  • Dienstleistungen zum Konfigurieren und Speichern von Profilen;

  • Dienste zum Konfigurieren und Speichern von Maßnahmen;

  • Dienstleistungen zum Konfigurieren und Speichern von Arbeitsplänen;

  • Service für die Arbeit mit externen Ereignissen;

  • mehrere Dienste, die dazu dienen, Arbeitspläne auszuführen und NSI zu verwalten.

Ich werde das Slicing von Microservices auch anhand eines Bildes veranschaulichen:

Auswählen, wie Dienste interagieren

Die zweite Schwierigkeit im Zusammenhang mit der Microservice-Architektur ist die richtige Wahl der Art und Weise, wie Services interagieren. Ich stelle gleich fest, dass es hier keine Wunderwaffe gibt: Jeder wählt einen Ansatz, der auf den individuellen Bedürfnissen und Einschränkungen basiert.

Die Serviceinteraktion kann synchron oder asynchron sein, und jede dieser Methoden hat ihre Vor- und Nachteile.

Die synchrone Kommunikation basiert hauptsächlich auf RPC- oder REST-Technologien. REST ist am gebräuchlichsten, aber die Anfrage-Antwort-Kommunikation kann Dienste einschränken. RPC wiederum (wenn wir über die häufigste Implementierung von gRPC sprechen) ist etwas komplizierter, aber die Verwendung von HTTP / 2 und ProtocolBuffers erhöht seine Effektivität. Einer der Hauptnachteile synchroner Interaktionsoptionen ist die Notwendigkeit, den Standort aller Dienste anzugeben. Beim Zugriff auf einen anderen Dienst muss jede Instanz des Dienstes den bestimmten „Standort“ des Nachbarn kennen, aber dieses Problem lässt sich mit serviceDiscovery leicht lösen. Auch synchrone Interaktionsmöglichkeiten sind mit Schwierigkeiten bei Ausfällen oder Timeouts verbunden.

Asynchrone Kommunikationsverfahren können wiederum auf der Grundlage synchroner Protokolle unter Verwendung von Anforderungskennungen und Rückrufen aufgebaut werden, basieren jedoch in den meisten Fällen auf der Verwendung von Nachrichtenvermittlern. Ich möchte darauf hinweisen, dass das korrekte Funktionieren der asynchronen Kommunikation die Definition von Nachrichtentypen erfordert: Was werden Sie durch den Broker treiben – Entitäten, Befehle oder Ereignisse? Nachrichtentypen beeinflussen auch die Auswahl von Interaktionsmustern, wie z. B. unidirektionale Benachrichtigungen, Publisher/Subscriber und andere. Das Broker-basierte asynchrone Verfahren bietet eine flexiblere und zuverlässigere Kommunikation zwischen Diensten und erhöht die Systemverfügbarkeit, ist jedoch schwieriger zu implementieren. Der Message Broker kann auch zu einem Flaschenhals werden, da er das gesamte Interaktionssystem zwischen Diensten enthält.

Bei der Entwicklung unseres Funktionsblocks haben wir uns für eine kombinierte Austauschoption entschieden und versucht, eine asynchrone Interaktionsmethode dort einzusetzen, wo sie unverzichtbar ist. Wir erstellen eine synchrone Interaktion basierend auf RPC und eine asynchrone Interaktion basierend auf Apache Kafka.

Transaktionalität sicherstellen

Eine weitere Herausforderung im Zusammenhang mit Microservices ist die Bereitstellung von Transaktionalität. In einer Microservice-Architektur ist es schwierig, ACID-Transaktionen zu unterstützen, da einige Daten über mehrere Dienste hinweg aktualisiert werden können und Datenkonsistenz einer der wichtigsten Entwicklungspunkte ist.

Lassen Sie uns die Situation kurz darstellen. Stellen wir uns vor, wir haben eine Geschäftstransaktion „Some Action“, die kleinere Transaktionen in anderen Diensten durchführen muss. In diesem Fall ist die gesamte Aktion “Some Action” nur unter der Bedingung sinnvoll, dass alle untergeordneten Transaktionen erfolgreich abgeschlossen werden. Wenn mindestens eine von ihnen mit einem Fehler endete, müssen Sie alle zuvor abgeschlossenen untergeordneten Transaktionen rückgängig machen, da wir sonst einen inkonsistenten Zustand erhalten.

Es gibt mehrere Möglichkeiten, dieses Problem zu lösen, einschließlich verteilter Transaktionen und Sagas (Sätze lokaler Transaktionen), auch bekannt als Erzählungen. Wir haben uns für die Variante mit Sagen und Orchestrator entschieden. Dieses Verfahren erschien uns aufgrund seiner Einfachheit und relativ geringen Entwicklungskosten geeignet.

Die Essenz der Lösung ist ganz einfach. Eine Geschäftstransaktion wird in eine Folge kleinerer Transaktionen zerlegt, in denen Dienste aufgerufen werden, die für lokale Transaktionen verantwortlich sind. Danach wird ein bestimmtes Szenario für die Ausführung dieser Transaktion (Saga) zusammengestellt, das eine Fehlerbehandlung (Dienstausfälle) beinhaltet. Die Aufgabe, die Aufführung der Saga zu koordinieren, fällt dem Orchestrator zu.

Basierend auf den Anforderungen für verschiedene Fehler (Ausfälle) wird ein bestimmtes Verfahren oder eine Abfolge von Verarbeitungsverfahren festgelegt:

  • Wiederholung der Beschwerde;

  • Einleitung von Ausgleichsgeschäften;

  • Abschluss der Transaktion mit einem Fehler.

Auf Ausgleichsgeschäfte möchte ich kurz eingehen. Im Wesentlichen ist eine kompensierende Transaktion ein Rollback jeder bereits getätigten lokalen Transaktion. Der Nachteil dieser Verarbeitungsmethode ist die Notwendigkeit, zusätzliche Kompensationsmethoden zu implementieren, die jedoch auch aus natürlichen Gründen fehlen können.

Unten ist eine Illustration, die das Wesentliche am Beispiel der Buchung einer Tour gut einfängt.

Der letzte Schritt zur Lösung dieses Problems ist die Wahl einer Komponente für die Aufführung der Saga (Orchestrator). Hier können Sie das Rad selbst neu erfinden und etwas Eigenes implementieren, oder Sie ziehen fertige Lösungen wie Camunda, Apache AirFlow und andere in Betracht. Es ist wichtig, sich daran zu erinnern, dass der Orchestrator/Koordinator zuverlässig, skalierbar und in der Lage sein muss, die Saga vom Punkt des Scheiterns an wieder aufzunehmen.

Bei der Entwicklung unseres Funktionsblocks haben wir verschiedene vorgefertigte Tools ausprobiert, darunter Camunda, auf dem wir BPMN-Skripte als Sagen schreiben, und Temporal, auf dem die Saga direkt codiert ist. Wir haben uns schließlich für Temporal entschieden, da wir der Meinung waren, dass es sich um eine produktionsreife Lösung handelt, die sich relativ einfach in ein Projekt integrieren lässt. Im Großen und Ganzen läuft die gesamte Temporal-Integration darauf hinaus, den Code mit Anmerkungen zu versehen und eine Bindung für seine Interaktion mit dem Server zu schreiben. Ich möchte darauf hinweisen, dass der Hauptvorteil dieser Lösung die einfache Bereitstellung dank DockerCompose für einen schnellen Start auf einem lokalen Computer oder in einer Testumgebung und Helm-Diagrammen für die Bereitstellung in Kubernetes ist. Bei der Bereitstellung auf k8s können Sie auch sofort die zeitliche Überwachung mit Metriken erhalten, die in Grafana angezeigt werden.

Fazit

In diesem Artikel habe ich nur die „Spitze des Eisbergs“ der Anwendungsentwicklung auf Basis einer Microservice-Architektur betrachtet. Ich möchte darauf hinweisen, dass der Einsatz von Microservices in allen Phasen des Softwarelebenszyklus, von der Konzeption einer Lösung bis zum Betrieb, eine ganze Reihe von Schwierigkeiten mit sich bringt. Sie können jedoch behandelt werden, und ich hoffe, dieser Artikel wird Ihnen dabei helfen.

Wenn Sie Fragen oder Kommentare haben, teilen Sie diese bitte in den Kommentaren mit.

Ich möchte auch noch einmal betonen, wie wichtig die bewusste Anwendung dieses Ansatzes ist. Es sei daran erinnert, dass viele Aufgaben, die häufig mithilfe von Microservices gelöst werden, problemlos ohne sie ausgeführt werden können oder nichts damit zu tun haben.

Selbst der hellste und teuerste Smaragd ersetzt keinen Hammer, wenn Sie einen Nagel einschlagen müssen©.

Similar Posts

Leave a Reply

Your email address will not be published.