Vor zwei Jahren haben wir mit der Dezentralisierung eines E-Commerce-Projekts begonnen. Der Grund dafür ist, dass das Projekt in der Produktion mit einem Cluster von Hybris-Knoten in Europa eingesetzt wird und unser Kunde weltweite Interessen hat. Latenzen zwischen den Kontinenten sind größer als die Qualität, die wir bieten wollen. Außerdem haben wir die typischen Probleme eines riesigen Monolithen, wie lange Release-Zyklen, lange und erschöpfende Testprozesse. . . Mit einem ständig wachsenden Funktionsumfang nahm auch die Zeit, die benötigt wurde, um neue Funktionen auf den Markt zu bringen, stetig zu.

Forschung

Anwendung

Wie ich bereits sagte, haben wir hier bei communicode große Erfahrung in der Arbeit mit Java und Spring. Aus diesem Grund war es einfach, die grundlegende Technologie zu finden, die wir für die Entwicklung der Mikrodienste verwenden können.

Wir wussten von Anfang an, dass unsere Anwendungen auf Spring Boot basieren würden, da es eingebettete Servlet-Container liefert (kann Tomcat, Jetty usw. sein). Daher war dies einer der einfachsten Teile, die wir mit dem Team vereinbaren konnten.

„Spring Boot macht es einfach, eigenständige Spring-basierte Anwendungen in Produktionsqualität zu erstellen, die Sie ‚einfach ausführen‘ können. Wir betrachten die Spring-Plattform und die Bibliotheken von Drittanbietern mit einer gewissenhaften Meinung, damit Sie mit minimalem Aufwand loslegen können. Die meisten Spring Boot-Anwendungen benötigen nur sehr wenig Spring-Konfiguration.“

Die Entscheidung über das Framework und die Technologie, die zum Erstellen von Anwendungen verwendet werden, ist wichtig. Dies ist der Grundstein des Projekts und es ist wichtig, dass sich alle im Team mit den verwendeten Tools und Technologien wohlfühlen.

Kommunikation

Nach unserer ersten Entscheidung mussten wir uns Gedanken über die Kommunikation zwischen den Diensten machen. Die Kommunikation zwischen Microservices kann entweder synchron oder asynchron erfolgen. Die für unseren Anwendungsfall erforderliche Hauptkommunikation bestand darin, andere Dienste darüber zu informieren, dass einige Aktionen vom Benutzer ausgeführt wurden. Bei diesem Publish/Subscriber-Muster konnte die Kommunikation asynchron erfolgen, sodass wir dem Benutzer schnell antworten konnten, während alle Prozesse, die durch die Interaktion des Benutzers ausgelöst wurden, abgeschlossen waren.

Um dies zu erreichen, war die Idee, eine Nachrichtenwarteschlange zu verwenden. Wir haben zwei Lösungen ausprobiert: Redis (als Nachrichtenwarteschlange) und RabbitMQ. Eine der Anforderungen an das neue System war, dass wir sicherstellen mussten, dass auf keinen Fall Daten verloren gehen, daher fiel die Entscheidung auf RabbitMQ, da Redis bisher keine zuverlässige Lieferung unterstützt.

RabbitMQ ist ein Nachrichtenbroker, der das AMQP (Advanced Message Queuing Protocol) implementiert. Es ist robust, einfach zu bedienen, Open Source und wird in mehreren Sprachen wie Java, .NET, Python und Ruby unterstützt.

Für die Arbeit mit RabbitMQ ist es wichtig zu verstehen, was ein Message Broker ist. Um eine Analogie zur realen Welt herzustellen, ist ein Message Broker wie die Post. Wenn Sie ein Paket versenden müssen, benötigen Sie den Inhalt (das Papier, das Sie darin einführen), die Zieladresse und die Quelladresse. Sobald Sie zur Post gehen, übernehmen sie die Verantwortung für Ihr Paket und versichern, dass es zugestellt wird, oder sie können Ihnen im Gegenteil eine Benachrichtigung über das Scheitern der Zustellung senden. Ein Message Broker funktioniert genauso. Es stellt sicher, dass Ihre Nachricht in einem asynchronen Modus zugestellt wird, in dem Sie nicht auf die Bestätigung des Empfängers warten müssen.

An diesem Punkt haben wir Anwendungen, die miteinander kommunizieren können. Jetzt müssen wir noch einige weitere Probleme lösen: Deployment, Testing und Monitoring. ## Bereitstellung und Verteilung

Es gibt verschiedene Ansätze zum Bereitstellen und Verteilen einer Anwendung. In unserem Fall wollten wir unabhängig von der Umgebung sein, in der die Anwendung ausgeführt wird. Wir wollten bei diesem Prozess agil sein und er musste einfach zu warten, für neue Releases einfach neu bereitzustellen und einfach zu konfigurieren sein. Daher lag unser Fokus auf Docker. Warum Docker? Zitat der offiziellen Website von Docker: „Docker ist die weltweit führende Software-Containerisierungsplattform. Mit Docker können Sie Ihre Anwendung aus Microservices zusammenstellen, ohne sich Gedanken über Inkonsistenzen zwischen Entwicklungs- und Produktionsumgebungen machen zu müssen und ohne sich auf eine Plattform oder Sprache festzulegen. Mit Docker können Sie den gesamten Zyklus der Anwendungsentwicklung, des Testens und der Verteilung entwerfen und mit einer konsistenten Benutzeroberfläche verwalten. Docker bietet Ihnen die Möglichkeit, skalierbare Dienste sicher und zuverlässig auf einer Vielzahl von Plattformen bereitzustellen.“

Docker ermöglicht es uns also tatsächlich, unsere Anwendung immer in derselben Umgebung auszuführen. Das Einzige, was wir tun müssen, ist, diese Umgebung zu konfigurieren, und dann ist das Deployable immer genau dasselbe. Solange wir mit Docker ein Image der Anwendung generieren können, können wir Container basierend auf diesem Image erstellen und sie schnell in jeder Umgebung bereitstellen, in der Docker ausgeführt werden kann. Docker hat Daemons für Windows, Mac und läuft nativ in GNU-Linux (und seit der neuesten Version können sie jetzt auch nativ in Mac laufen). Das bedeutet, dass die Anwendung in der Entwicklung und in der Produktion immer gleich ausgeführt wird, egal in welcher Umgebung Sie entwickeln.

Testen

Testen war das interessanteste Thema in diesem Projekt. Im Allgemeinen schreiben wir Unit-Tests, API-Tests und Integrationstests (innerhalb der Anwendung), aber dieses Projekt besteht aus mehreren Anwendungen, die miteinander kommunizieren, daher mussten wir die Anwendungsfälle vom Einstiegspunkt (Nutzung durch einen Benutzer) richtig testen. zum Ausstiegspunkt.

Das Diagramm zeigt alle Verbindungen, die zwischen den Diensten bestehen. Wie Sie sich vielleicht schon gedacht haben, mussten wir zum Testen aller Dienste zunächst eine Orchestrierung vornehmen, sie konfigurieren und mit den richtigen Verbindungen zum Laufen bringen. Dazu haben wir das „docker-maven-plugin“ von „fabric8io“ verwendet. Das Plugin ermöglicht die Verwaltung der Docker-Images und -Container. Es kann Images erstellen, Container ausführen, Container miteinander verknüpfen und viele andere Docker-Funktionalitäten wie Push, Stop, Remove usw. . .

Was wir getan haben, war, das Setup genau so zu konfigurieren, wie es in der Produktion verwendet wird, um sicherzustellen, dass die Bereitstellung der bereitgestellten Dienste immer funktioniert und die Konnektivität vom Einstiegspunkt zum Ausgangspunkt erhalten bleibt und die Anwendungen senden und empfangen können Mitteilungen.

In der Grafik können wir sehen, dass es neben RabbitMQ und den Einstiegspunkten eine zusätzliche Box namens „JLament“ gibt. Dies ist das „Testtool/Framework“, das wir zum Testen erstellt haben. In JLament haben wir die zuvor besprochene Orchestrierung, die Testsuite, Mocks von externen Diensten, die nicht vom Team entwickelt wurden (Mocks von Backends) und einige Dienste, die es den Tests ermöglichen, mit den Diensten zu kommunizieren, Nachrichten aus der Nachrichtenwarteschlange zu lesen, Nachrichten zu veröffentlichen, Verfolgung der Kommunikation und alle möglichen verschiedenen Funktionalitäten, die zum Testen aller Anwendungsfälle erforderlich sind. ## Lager

Dieses Thema war eines der umstrittensten, das wir hatten. Die Wahl des richtigen Speichers für Ihre Anwendung ist entscheidend, daher haben wir viel Zeit in die Recherche investiert, um die beste Lösung für uns zu finden. Der erste Ansatz, den wir hatten, war die Verwendung von Redis. Jedes Mal, wenn eine neue Technologie vorgeschlagen und diskutiert wird, führen wir vor der Anpassung immer eine Recherche über die Technologie durch, bei der wir testen, was diese Technologie für uns tun kann. Im Fall von Redis, einer nicht relationalen Datenbank, haben wir mehrere Vorschläge gemacht, wie die Daten gespeichert werden können, Vor- und Nachteile von jeder Struktur und dann Messungen der Leistung jedes Ansatzes. Zum Beispiel haben wir einen Vergleich über die Leistung, die Redis uns geben würde, wenn wir die Daten in Strings oder in Redis-Hashes (in Form einer Karte gespeichert) speichern würden, und eine Pro-Contra-Liste angestellt, bevor wir unsere Entscheidung getroffen haben. In diesem Fall lautete die Pro-Contra-Liste:

Und die Leistungsergebnisse waren:

Durch das Sammeln all dieser Informationen konnten wir Entscheidungen treffen, die ausgereift, gut durchdacht und für das Projekt angemessen waren. Das Ergebnis nach dem Testen von Redis als Datenbanktechnologie war für uns nicht erfolgreich. Die Leistung, die wir erzielten, als die Menge der gespeicherten Daten zunahm, lag außerhalb unserer Grenzen (wir mussten zeitliche Einschränkungen erfüllen).

Ansatz 1 ist der String-Ansatz und Ansatz 2 ist der Hash-Ansatz.

Ansatz mit blauer Linie ist der String-Ansatz und Ansatz mit roter Linie ist der Hash-Ansatz.

Dann entschied das Infrastrukturteam, dass Amazon AWS das Angebot für das Hosting der Dienste in der Cloud sein würde. Daher haben wir uns entschlossen, einen Blick auf die Lösungen von Amazon AWS zu werfen. Die Verwendung von Amazon RDS schien eine gute Idee zu sein. Es bietet die Möglichkeit, den Entwicklern des Teams bekannte Technologien wie MySQL, Oracle DB oder PostreSQL zu verwenden. Also haben wir diese Lösung ausprobiert, nur um herauszufinden, dass das Datenmodell, wie wir es speichern wollten, komplexe Abfragen für einfache Anwendungsfälle erfordert hätte, was die Verwendung einer relationalen Datenbank in der Cloud ziemlich kostspielig und komplexer als nötig gemacht hätte. Also haben wir uns den anderen Dienst angesehen, den Amazon AWS für nicht relationale Datenbanken bereitstellt: DynamoDB. „Amazon DynamoDB ist ein schneller und flexibler NoSQL-Datenbankdienst für alle Anwendungen, die eine konsistente Latenz im einstelligen Millisekundenbereich in jeder Größenordnung benötigen. Es ist eine vollständig verwaltete Cloud-Datenbank und unterstützt sowohl Dokument- als auch Schlüsselwertspeichermodelle. Das flexible Datenmodell und die zuverlässige Leistung machen es zu einer großartigen Lösung für Mobil-, Web-, Gaming-, Ad-Tech-, IoT- und viele andere Anwendungen. aws.amazon.com/documentation/dynamodb

Die Einrichtung war einfach und die von DynamoDB bereitgestellte Leistung ist unglaublich hoch. Amazon stellt auch ein Java-SDK für die Verbindung mit DynamoDB zur Verfügung, was die Entwicklung erheblich vereinfacht.

Überwachung

Der letzte Schritt in allen Projekten besteht darin, über die richtigen Tools zur Überwachung der Dienste zu verfügen. Bei einem großen Projekt wie diesem, bei dem verschiedene Unternehmen unterschiedliche Komponenten produzieren, besteht die Notwendigkeit, zentralisierte Tools zu haben, die von allen Projektbeteiligten zur Überwachung aller Komponenten verwendet werden können. Stellen Sie sich vor, jede Komponente (wir sprechen derzeit von mehr als 15) hätte ihre eigenen Überwachungstools. Es gäbe keine Möglichkeit, die Daten zu Debugging-Zwecken zu überqueren, ohne einen enormen Overhead zu haben. Daher haben wir uns alle darauf geeinigt, ELK (ELK steht für Elasticsearch) für das Protokollierungssystem und InfluxDB + Grafana für die Metriken zu verwenden. Der ELK-Stack ermöglicht es uns, alle Protokolle von allen Komponenten zu zentralisieren, indem wir Logstash verwenden, um Daten an Elasticsearch zu übertragen, die von Kibana gelesen und gefiltert werden können. Dies ist ein leistungsstarker Stapel für Protokollierungssysteme. Kibana bietet verschiedene Visualisierungen, Erstellung von Dashboards und sogar Grafiken zur Überwachung, beispielsweise wie oft eine Protokollaussage auftritt. InfluxDB + Grafana ist der zweite Überwachungsstack. In InfluxDB speichern wir alle Metriken, die von einer beliebigen Komponente erzeugt werden, und Grafana ermöglicht es uns, Diagramme zur Visualisierung der Daten zu erstellen. Mit diesem Stapel können wir eine grundlegende Überwachung (dh: CPU, Speicherauslastung, verfügbare / verwendete Threads), eine benutzerdefinierte Überwachung (dh: Anzahl der Ereignisse eines Typs, die in der Anwendung ausgelöst werden), eine Infrastrukturüberwachung (dh: bereitgestellte Lese- / Schreibvorgänge) haben dynamoDB und Nutzung dieser bereitgestellten Daten).

Diese Kombination beider Stacks versorgt sowohl das Infrastrukturteam als auch das Komponententeam mit allen erforderlichen Informationen für die Überwachung der Systeme und das Debuggen, Verfolgen oder Verfolgen jeglicher Art von Fehlverhalten, das durch die normale Nutzung der Plattform entstehen kann.