Monolithischen Anwendungen in Microservices neugestalten
Die Migration zu Microservices erfordert eine sorgfältige Planung. Wir zeigen Ihnen bewährten Methoden, um monolithischen Anwendungen neu zu gestalten und geben Ihnen ein Beispiel für die Aufteilung einer Flask-Anwendung in zwei separate Microservice-Endpunkte.
Serverless-Architektur für flexible Backend-Webdienste
Serverless-Architektur ermöglicht es, Backend-Webdienste bei Bedarf zu implementieren. Anstatt Ihre eigene Serverkonfiguration zu pflegen, können Sie Ihre Software für serverlose Anbieter entwickeln, um den damit verbundenen Overhead zu minimieren. Serverlose Anwendungen werden in der Regel aus einem Git-Repository in eine Umgebung bereitgestellt, die je nach Bedarf skaliert werden kann.
Microservices: Strukturierte und skalierbare Architektur
Serverlose Bereitstellungen beinhalten in der Regel Microservices. Die Verwendung von Microservices ist ein Ansatz zur Softwarearchitektur, bei dem eine Anwendung als Sammlung von Diensten strukturiert wird, die lose gekoppelt, unabhängig bereitstellbar und unabhängig wartbar und testbar sind. Microservice-Architekturen existierten schon vor der weit verbreiteten Verwendung von serverlosen Bereitstellungen, aber sie passen natürlich gut zusammen. Microservices können in jedem Kontext verwendet werden, der es ermöglicht, sie unabhängig bereitzustellen und von einem zentralen Prozess oder Job-Server zu verwalten. Serverlose Implementierungen abstrahieren dieses zentrale Prozessmanagement, sodass Sie sich auf Ihre Anwendungslogik konzentrieren können.
Eine Übersicht über Microservices
Die Neugestaltung oder das Refactoring einer monolithischen Anwendung ist oft von Natur aus unsichtbar. Wenn Sie planen, Ihre Anwendungslogik erheblich umzuschreiben, ohne neue Funktionen einzuführen, sollte Ihr Ziel darin bestehen, Serviceunterbrechungen so weit wie möglich zu vermeiden. Dies kann die Verwendung einer Art von Blue-Green-Bereitstellung beinhalten. Bei der Implementierung von Microservices bedeutet dies in der Regel auch, die Funktionalität Ihrer Anwendung schrittweise zu ersetzen. Dies erfordert, dass Sie gründliche Unit-Tests durchführen, um sicherzustellen, dass Ihre Anwendung unerwartete Randfälle problemlos bewältigt. Es bietet auch viele Möglichkeiten zur Überprüfung Ihrer Anwendungslogik und zur Bewertung, wie bestehende Funktionen durch separate Microservices ersetzt werden können.
Frameworks und Zustandsmanagement
Einige Architekturen passen besser zu Microservices als andere. Wenn Ihre Anwendungslogik mehrere sequentielle Schritte enthält, die alle voneinander abhängen, ist es möglicherweise keine gute Idee, jeden von ihnen in separate Microservices zu abstrahieren. In diesem Fall benötigen Sie eine ausgefeilte Controller-Architektur, die Mid-Stage-Fehler behandeln und routen kann. Dies ist bei einer Microservice-Architektur möglich, die ein Framework wie Gearman verwendet, um Unterprozesse zu senden, kann jedoch bei der Arbeit mit serverlosen Bereitstellungen umständlicher sein und ohne notwendigerweise Probleme zu lösen, Komplexität hinzufügen.
Anstatt Microservices zwischen Stufen der gleichen Eingabe-Verarbeitungspipeline abzugrenzen, könnten Sie Microservices zwischen Anwendungsstatusänderungen oder immer dann abgrenzen, wenn eine Ausgabe an einen Benutzer zurückgegeben wird.
Auf diese Weise müssen Sie nicht dieselben Daten zwischen öffentlichen API-Aufrufen als Teil eines einzigen Prozesses übergeben. Das Verwalten des Anwendungsstatus kann bei einer Microservice-Architektur herausfordernd sein, da jeder Microservice nur Zugriff auf seine eigene Eingabe hat, nicht auf einen global definierten Scope. Wo immer möglich, sollten Sie ähnliche Datenstrukturen für jeden Ihrer Microservices erstellen und übergeben, damit Sie zuverlässige Annahmen über den verfügbaren Scope für jeden von ihnen treffen können.
Erwägen Sie das Erstellen und Pflegen Ihrer eigenen Anwendungsbibliotheken für Kernlogik und Funktionen, die wahrscheinlich an mehreren Stellen verwendet werden, und erstellen Sie dann Microservices, die eindeutige Kombinationen dieser Logik verbinden.
Denken Sie daran, dass Microservices auf Null skalieren können: Es gibt keine Strafe für die Aufrechterhaltung von ungenutzten Codepfaden. Auf diese Weise können Sie Microservices erstellen, die nicht direkt von anderen Microservices abhängen, da sie jeweils einen vollständigen linearen Satz von Anwendungslogik enthalten, der aus Funktionen besteht, die Sie in einem separaten Repository pflegen.
Bereitstellung aus Git
Bei der Arbeit mit Microservices sollten Sie die Prinzipien von GitOps so weit wie möglich anwenden. Behandeln Sie Git-Repositorys als einzige Wahrheitsquelle für Bereitstellungszwecke. Die meisten sprachspezifischen Paketmanager, wie pip für Python und npm für Node.js, bieten Syntax, um Pakete aus Ihren eigenen Git-Repositorys bereitzustellen. Dies kann zusätzlich zur Standardfunktionalität zur Installation von PyPI, npmjs.com oder anderen Upstream-Repositorys verwendet werden. Auf diese Weise können Sie Ihre eigenen in Entwicklung befindlichen Funktionen problemlos mit Third-Party-Bibliotheken kombinieren, ohne von bewährten Verfahren zur Wartbarkeit oder Reproduzierbarkeit abzuweichen.
API-Endpunkte
Jeder Ihrer Microservices kann seine eigene API implementieren, und je nach Komplexität Ihrer Anwendung können Sie eine weitere API-Ebene oben auf diese implementieren (und so weiter) und planen, nur die API der höchsten Ebene für Ihre Benutzer freizugeben.
Obwohl das Verwalten mehrerer verschiedener API-Routen Komplexität hinzufügen kann, kann diese Komplexität durch eine gute Dokumentation der einzelnen API-Endpunkte Ihrer Microservices gelöst werden. Die Kommunikation zwischen Prozessen mit gut definierten API-Aufrufen wie HTTP GET und POST verursacht praktisch keine zusätzliche Belastung und macht Ihre Microservices wesentlich wiederverwendbarer, als wenn sie eine eigenwilligere Interprozesskommunikation verwenden würden.
Die Einführung von Microservices kann Sie natürlich auch dazu bringen, mehr Software-as-a-Service (SaaS)-Tools als Ersatz für verschiedene Teile Ihres Anwendungsstapels zu übernehmen.
Dies ist fast immer eine gute Sache im Prinzip. Sie sind nicht verpflichtet, Ihre eigenen Funktionsaufrufe durch Dienste von Drittanbietern zu ersetzen, aber die Möglichkeit zur Beibehaltung dieser Option wird Ihre Anwendungslogik flexibler und zeitgemäßer machen.
Migration zu Microservices
Eine effektive Migration zu Microservices erfordert die Synthese bewährter Methoden im Bereich Softwareentwicklung und Bereitstellung.
Verwendung von CI/CD-Prinzipien
Bei der Neugestaltung einer Anwendung zur Verwendung von Microservices sollten Sie die bewährten Methoden für kontinuierliche Integration und kontinuierliche Bereitstellung befolgen, um schrittweise Funktionen Ihrer monolithischen Architektur zu ersetzen. Zum Beispiel können Sie das Abstraktionslayering verwenden – ein Abstraktionsschicht innerhalb einer vorhandenen Implementierung erstellen, sodass eine neue Implementierung parallel hinter der Abstraktion erstellt werden kann – um Produktionscode ohne Unterbrechung für Benutzer zu refaktorieren. Sie können auch Dekorateure verwenden, ein Sprachmerkmal von TypeScript und Python, um vorhandenen Funktionen weitere Codepfade hinzuzufügen. Auf diese Weise können Sie Funktionalitäten schrittweise ein- oder ausschalten.
Portabilität
Microservices sind aus gutem Grund zur gleichen Zeit wie Containerisierungsframeworks wie Docker beliebt geworden. Sie haben ähnliche Ziele und architektonische Annahmen:
- Container bieten Prozess- und Abhängigkeitsisolierung, sodass sie einzeln bereitgestellt werden können.
- Container ermöglichen es anderen Anwendungen, die gleichzeitig mit ihnen ausgeführt werden, als „Black Box“ zu funktionieren – sie müssen keinen Zustand oder Informationen außer Ein- und Ausgabe teilen.
- Containerregistrierungen wie Docker Hub ermöglichen die Veröffentlichung und Verwendung Ihrer eigenen Abhängigkeiten austauschbar mit Abhängigkeiten von Drittanbietern.
In der Theorie sollten Ihre Microservices gleichermaßen für die Ausführung in einem Docker-Container oder einem Kubernetes-Cluster geeignet sein wie in einer serverlosen Bereitstellung. In der Praxis kann es erhebliche Vorteile bei einer der Optionen geben. Sehr CPU-intensive Microservices wie die Videobearbeitung sind möglicherweise in serverlosen Umgebungen nicht wirtschaftlich, während die Pflege eines Kubernetes-Kontrollplans und von Konfigurationsdetails einen erheblichen Aufwand erfordert. Es ist jedoch immer eine lohnende Investition, mit Portabilität im Hinterkopf zu entwickeln. Je nach Komplexität Ihrer Architektur können Sie möglicherweise mehrere Umgebungen unterstützen, indem Sie einfach die relevanten .yml-Metadatenanweisungen und Dockerfiles erstellen. Die Prototypenerstellung für Kubernetes- und serverlose Umgebungen kann die Gesamtwiderstandsfähigkeit Ihrer Architektur verbessern.
Im Allgemeinen sollten Sie sich keine Gedanken über Datenbank-Gleichzeitigkeit oder andere Skalierungsprobleme in Microservices selbst machen müssen.
Ettwaige relevante Optimierungen sollten direkt von Ihrer Datenbank, Ihrer Datenbankabstraktionsschicht oder Ihrem Database-as-a-Service (DBaaS)-Anbieter angesprochen und implementiert werden, damit Ihre Microservices CRUD-Operationen ohne Schnörkel ausführen können. Microservices müssen in der Lage sein, gleichzeitig auf dieselben Datenquellen zuzugreifen und diese zu aktualisieren, und Ihre Datenbank-Backend sollte diese Annahmen unterstützen.
Versionierung
Wenn Sie in Ihren Microservices brechende, nicht rückwärtskompatible Aktualisierungen vornehmen, sollten Sie neue Endpunkte bereitstellen. Sie könnten beispielsweise einen /my/service/v2 zusätzlich zu einem bereits vorhandenen /my/service/v1 bereitstellen und planen, den /v1-Endpunkt nach und nach zu veralten. Dies ist wichtig, weil Produktions-Microservices wahrscheinlich außerhalb ihres ursprünglich beabsichtigten Kontexts nützlich und unterstützt werden. Aus diesem Grund werden viele serverlose Anbieter Ihre URL-Endpunkte beim Bereitstellen neuer Funktionen automatisch auf /v1-versionieren.
Microservice-Migrationsbeispiel
Das Implementieren von Microservices in Ihrer Anwendung kann verschachtelte Funktionsaufrufe oder private Methoden ersetzen, indem Sie sie zu eigenständigen Diensten machen. Nehmen Sie dieses Beispiel einer Flask-Anwendung, die basierend auf der Eingabe eines Benutzers in ein Webformular eine Google-Suche durchführt und das Ergebnis vor der Rückgabe an den Benutzer manipuliert:
# Flask-Anwendung def google_query(query, api_key, cse_id, **kwargs): # Führt eine Google-Suche aus pass def manipulate_result(input, cli=False): # Manipuliert das Suchergebnis pass @app.route('/', methods=["GET"]) def get_url(text): manipulated_text = manipulate_result(text) return render_template('index.html', prefill=text, value=Markup(manipulated_text)) if __name__ == "__main__": serve(app, host='0.0.0.0', port=5000)
Diese Anwendung bietet ihren eigenen Web-Endpunkt, der eine HTTP-GET-Methode enthält. Das Bereitstellen eines Textstrings an diesen Endpunkt ruft eine Funktion namens manipulate_result() auf, die zuerst den Text an eine andere Funktion google_query() sendet, dann den Text aus den Suchergebnissen manipuliert, bevor sie ihn dem Benutzer zurückgibt.
Diese Anwendung könnte in zwei separate Microservices umstrukturiert werden, die beide HTTP-GET-Parameter als Eingabeargumente verwenden. Der erste würde Google-Suchergebnisse basierend auf einer Eingabe zurückgeben, indem die googleapiclient Python-Bibliothek verwendet wird:
# Microservice 1 def main(input_text): # Führt eine Google-Suche aus pass
Ein zweiter Microservice würde dann die relevanten Daten manipulieren und extrahieren, die dem Benutzer aus diesen Suchergebnissen zurückgegeben werden sollen:
# Microservice 2 def main(search_string, standalone=True): if standalone == False: # Ruft Microservice 1 über HTTP auf pass else: search_results = search_string # Manipuliert das Suchergebnis pass
In diesem Beispiel führt microservice_2.py alle Eingabeverarbeitung durch und ruft microservice_1.py direkt über einen HTTP-Post auf, wenn ein zusätzliches Argument standalone=False übergeben wurde. Sie könnten optional eine separate dritte Funktion erstellen, um beide Microservices zusammenzuführen, wenn Sie sie lieber vollständig voneinander getrennt halten, aber trotzdem ihre volle Funktionalität mit einem einzelnen API-Aufruf bereitstellen möchten.
Dies ist ein einfaches Beispiel, und der ursprüngliche Flask-Code scheint keine erhebliche Wartungsbelastung darzustellen, aber es gibt immer noch Vorteile darin, Flask aus Ihrem Stack entfernen zu können. Wenn Sie Ihre eigene Webanforderungs-Handler nicht mehr ausführen müssen, können Sie diese Ergebnisse dann an eine statische Website zurückgeben, die eine Jamstack-Umgebung verwendet, anstatt eine Flask-Backend pflegen zu müssen. Jetzt mit Microservices und Best Practices neugestalten!