Nie wieder Monolithen! | jaxenter.de

Micro Services in der Praxis: Nie wieder Monolithen!

Timmo Freudl-Gierke

© Shutterstock/Levranii

Bei der Hypoport AG haben wir bereits drei verschiedene Modularisierungsinkarnationen erlebt. Jede Inkarnation brachte uns näher an das Ideal einer flexiblen, wartbaren Architektur. Und dennoch stellten wir nach wenigen Jahren der Produktweiterentwicklung wieder fest: Die Anwendung ist voll von unbeabsichtigter Komplexität, Innovationen sind nur schwer möglich und die Umsetzung von Funktionalität dauerte kontinuierlich länger. Der Micro-Service-Architekturstil verheißt durch die Zerlegung eines Systems in kleine, unabhängige Services nachhaltige Besserung. Wir haben’s ausprobiert und sind begeistert.

Im Jahr 2010 kannten wir noch keine Micro Services und wollten dennoch alles richtig machen: Nie wieder ein Monolith! Ein Greenfield-Projekt bot viele Möglichkeiten, die Architektur zu gestalten. Die angestrebte (eine) Webanwendung haben wir mit dem Product Owner gemeinsam fachlich in acht grobgranulare Module zerlegt. Jedes Modul wurde als Web-App mit eigenständiger NoSQL-Datenbank umgesetzt und alle Web-Apps wurden gemeinsam durch eine Deployment Pipeline kontinuierlich auf das Produktionssystem verteilt. Die Welt war in Ordnung. Zweieinhalb Jahre und 3 000 Klassen später war jede Web-App intern in zehn bis fünfzig Maven-Projekte unterteilt. Saubere Abhängigkeiten, klare Schnittstellen. Nur: Deployed wurden alle Web-Apps immer gemeinsam durch eine Pipeline. Die Pipelinelaufzeit und v. a. die Zeit bis zum relevanten Integrationstestergebnis lag im Durchschnitt bei ca. 40 Minuten. Auf dieses Feedback zu warten, ist eine gefühlte Ewigkeit. Das Milk-Maven-Plug-in schaffte uns etwas Luft: Der Maven Reactor wird durch das Plug-in so modifiziert, dass nur die tatsächlich geänderten Maven-Projekte sowie deren Upstream-Dependencies gebaut und getestet werden. Die durchschnittliche Laufzeit sank auf ca. sieben Minuten.

W-JAX
Mike Wiesner

Data- und Event-driven Microservices mit Apache Kafka

mit Mike Wiesner (MHP – A Porsche Company)

Niko Köbler

Digitization Solutions – a new Breed of Software

with Uwe Friedrichsen (codecentric AG)

Software Architecture Summit 2017
Dr. Carola Lilienthal

The Core of Domain-Driven Design

mit Dr. Carola Lilienthal (Workplace Solutions)

Sascha Möllering

Reaktive Architekturen mit Microservices

mit Sascha Möllering (Amazon Web Services Germany)

Die Entscheidung, weiterhin mit einem Sourcecode-Root in der Versionsverwaltung und einer Deployment Pipeline zu arbeiten, wurde bewusst getroffen: Zu groß erschienen uns immer noch die Vorteile z. B. bei übergreifenden Refactorings.
Nach einem weiteren Jahr und 2 000 weiteren Klassen war klar: Die Featureumsetzungsgeschwindigkeit nimmt kontinuierlich ab. Ein einfacher Test-driven-Development-Zyklus dauert 20 Sekunden – viel zu viel, wenn TDD die Standardentwicklungsmethode ist. Hinzu kommt, dass die Web-App lokal neu gestartet werden muss, um eine Erweiterung integrativ zu testen. Dabei sind zwei bis drei Minuten Wartezeit leider normal. Auch die Pipeline-Ausführungszeit stieg wieder deutlich an. Eine stark vermehrte Anzahl von Integrationstests sowie weitere Entwickler im Team verstopfen (durch fleißiges Kodieren) mehr und mehr die eine Pipeline. Die unterschiedlichen Web-Apps ermöglichen den Einsatz verschiedener Technologien innerhalb des Java-Universums. Wirklich genutzt wurde dies jedoch nicht. Im Gegenteil: Bewährtes wurde gerne wieder verwendet und übergreifende Konzepte in geteilten Bibliotheken hinterlegt. Eine zunehmend enge Verzahnung war das Ergebnis. Kleine Änderungen wurden aufwändig. Innovationen, wie z. B. eine Java-8-Einführung oder ein Spring-4-Update, erzeugen viel zu hohe Aufwände. Solche Themen dann zeitnah oder „nebenbei“ anzugehen, fällt nahezu aus. Unzufriedenheit über die veraltete Software und ein Verlust an „Spaß bei der Arbeit“ sind die subtilen wie ernstzunehmenden Konsequenzen.

Darüber hinaus traten weitere Symptome monolithischer Anwendungen auf: Eine technisch enge Kopplung von fachlich entkoppelten Themen resultiert in steigenden Lern- und Einarbeitungskurven bei den Entwicklern. Die Konsequenzen einer Änderung abzusehen wurde stetig schwieriger. Allen im Team wurde klar: Wir brauchen einen neuen, skalierbaren und innovationsfreundlichen Architekturansatz.

Micro Services

Die konzeptionellen Grundlagen zu Micro Services hat Eberhard Wolff in seinem Artikel (siehe Seite 29) ausführlich erläutert. Überführt in die Praxis bedeutet dies, alle Ebenen einer Softwarearchitektur neu zu durchdenken, um ein auf Micro Services abgestimmtes Paket zu schnüren. Dies gelingt nicht durch eine große Vorplanung. Daher setzen wir auf folgende Strategie: Bei jedem neuen Service wollen wir auf mindestens einer der Architekturebenen Verbesserungen vornehmen. So werden bestehende Konzepte hinterfragt und neue, leichtgewichtigere Lösungen erarbeitet. Durch den Ansatz „der zuletzt erstellte Service ist die Kopiervorlage für den nächsten“ lernen wir iterativ und inkrementell.

Im Ergebnis funktioniert dieser Weg sehr gut: Die erste Micro Services Deployment Pipeline ist schon deutlich einfacher gewesen als unsere allererste Pipeline von vor vier Jahren. Beim nächsten Service – dank eines Technologiewechsels hin zu Gradle und dem Gradle-ssh-Plug-in – eine richtige Wucht. Beim dritten Service hat das Team viel über REST und das für HATEOAS von uns verwendete Hypermedia-Format HAL gelernt. Der vierte Service brachte eine starke Vereinfachung der Spring-Konfiguration.

Die ideale Servicegröße

Den Umfang eines Service an der maximalen Anzahl von Lines of Code oder enthaltenen Klassen festzumachen, erschien uns nicht als geeignetes Mittel. Denn eine harte Grenze von z. B. 100 Zeilen Quelltext ist uns zu technisch fokussiert und operativ mit zu viel Overhead verbunden. Das Single Responsibility Principle ist die von uns bisher angewendete Richtschnur: „Für die Änderung einer Klasse sollte es nur genau einen Grund geben“. Dieses Prinzip kann auf Micro Services übertragen werden. Ein Service umfasst demnach eine fachliche Verantwortung. In unserem Kontext stellen z. B. Wiedervorlagen eines Geschäftsvorganges eine abgeschlossene, fachliche Funktionalität dar. Auch das Konvertieren von dynamischen HTML-Dokumenten in PDF-Dokumente ist ein – in diesem Fall eher technisch – klar abgegrenzter Verantwortungsbereich. Adapterservices, also Services, die zwei Systeme miteinander verbinden, sind schnell identifiziert und klar abgegrenzt.
Bis jetzt konnten wir fünf fachliche Bereiche in Micro Services implementieren. Diese Services sind zwischen 27 und 200 Klassen klein. 200 Klassen mögen auf den ersten Blick viel für einen Micro Service erscheinen. Hier werden allerdings 90 Prozent für die Kernfunktionalität der Transformation zwischen zwei Datenmodellen benötigt. James Lewis, Coding Architekt bei ThoughtWorks, formuliert die ideale Größe eines Service prägnant: „A Micro Service has to fit in my head.“

Services enthalten Funktionalität und Daten

Micro Services umfassen sowohl Funktionalität als auch Daten (Abb. 1). Jeder Service verwendet eine eigene, explizite Datenbank, auf die nur der Service Zugriff hat. Optimierungen der Datenbank oder Schemaänderungen müssen mit keinem weiteren Nutzer abgesprochen werden.

Abb. 1: Micro Services enthalten Funktionalität und Daten

Aus einer Betriebsperspektive kann das schnell Aufwand erzeugen: Mehrere Datenbanken müssen aufgesetzt, gepflegt und regelmäßig gesichert werden. Daher verwenden wir für die meisten Services ein geteiltes MongoDB-Replika-Set. Jeder Service erhält darin seine eigene Datenbank. Benötigen andere Services Daten, verwenden diese eine Schnittstelle auf einer höheren Abstraktionsebene. Meistens ist dies eine synchron aufgerufene REST-(Backend-)Schnittstelle.  Benötigt ein Service alle Daten eines anderen, nutzen wir Datenreplikation. Diese erfolgt nicht auf der Datenbankebene. Wir nutzen dazu wiederum abwärtskompatible REST-Schnittstellen. Die meisten unserer Implementierungen nutzen HTTP-/JSON-Push-Events, um eine Änderung im Quellsystem an alle interessierten Zielsysteme zu übertragen. Denkbar, aber von uns noch nicht implementiert, wäre das Veröffentlichen von z. B. Atom Feeds, die alle Änderungen enthalten und von interessierten Clients abgerufen werden können. Möglich ist auch eine Integration auf GUI-Ebene. Wir nutzen das z. B. für eine interaktive Kopfleiste, die in allen Anwendungsmodulen sichtbar ist.

Aufmacherbild: Crazy scientist. Young boy performing experiments von Shutterstock / Urheberrecht: Levranii

[ header = Seite 2: Makroarchitekturleitplanken ]

Makroarchitekturleitplanken

Wenn ein System aus vielen, miteinander interagierenden Services besteht, sind wenige übergreifende Spielregeln notwendig. Diese Regeln bezeichnen wir als Leitplanken der Makroarchitektur (Abb. 2). Die Betonung liegt hier ganz klar auf wenige, denn Technologiefreiheit und Innovationsmöglichkeiten sollen nicht durch ein zu enges Korsett an Vorschriften ausgebremst werden.

Abb. 2: Die Makroarchitektur regelt das Zusammenspiel aller Micro Services

Folgende Spielregeln haben sich in unserem Kontext bewährt (siehe auch Tabelle 1):

1. Abwärtskompatible Schnittstellen: Sollen Services unabhängig von ihren Konsumenten ausgerollt werden, sind rückwärtskompatible Schnittstellen ein Muss.
2. Darüberhinaus haben wir gute Erfahrungen mit vorwärtskompatiblen Schnittstellen gemacht. Bei einer Featureweiterentwicklung beobachten wir zwei Umsetzungsmuster: Zuerst das Backend fertigstellen und danach das GUI oder anders herum. Wird das GUI zuerst erweitert, ist es praktisch, wenn (GUI-)Konsumenten schon mit einem breiteren Schema Daten übermitteln können als der vorwärtskompatible Service aktuell verarbeiten kann. Wir erstellen für jeden Service Integrationstests, die sowohl die Abwärts- als auch die Vorwärtskompatibilität zeigen.
3. Als Kommunikationsstandard zwischen Services verwenden wir HTTP. Die meisten Services arbeiten auf Basis von REST mit HAL als JSON-basiertes Hypermediaformat. Jeder Service veröffentlicht seine HTTP-Ressourcen mittels eines HAL-Service-Dokuments.
4. Logausgaben aller Services sollten zentral aggregiert werden. Wir nutzen dazu Splunk. Ein einheitliches Pattern-Layout ist unabdingbar. Hilfreich ist es, den Namen des Service in jedes Logstatement mit aufzunehmen.
5. Zur Nachverfolgung von Requests durch das System hindurch verwenden wir eine Trace ID. Diese Zufallszahl wird möglichst früh generiert, bestenfalls ist sie bereits als HTTP-Header X-Trace-ID im Browser-Ajax-Call enthalten. Sie wird an nachfolgende Requests weitergereicht. Mithilfe des Log4J Diagnostic Context (MDC) ist die aktuelle Trace ID in jedem Logstatement sichtbar.
6. Ein einheitliches DNS-Namensschema ist empfehlenswert für manuelles, exploratives Testen eines Service.

Mikroarchitekturleitplanken

Auf der Mikroarchitekturebene existieren nur wenige Regeln (siehe auch Tabelle 1).

1. Die Wahl des Technologiestacks kann und sollte optimal auf die zu lösende Aufgabe abgestimmt sein. Wichtig ist, dass das gesamte Team bereit ist, die Technologieentscheidung dauerhaft mitzutragen.
2. Minimale Abhängigkeit: Die technische Unabhängigkeit von Services soll dauerhaft bestehen bleiben. Daher werden keine geteilten Bibliotheken, Infrastrukturkomponenten o. Ä. verwendet. Erlaubt sind nur Abhängigkeiten auf Third-Party-Open-Source-Bibliotheken.
3. Ergeben sich serviceübergreifende Gemeinsamkeiten, können diese über Open-Source-Projekte geteilt werden. Vorteil: Es entstehen nur wirklich wertbringende, allgemein verwendbare Bibliotheken.
4. Shared Nothing Model: Außer einem Security-Token teilen sich Serviceinstanzen keinen Zustand.

In der Praxis setzen die Teams auf ihnen vertraute und bewährte Technologien. Zwischen zwei Teams können diese Technolgiestacks allerdings deutlich unterschiedlich sein: Nutzt ein Team JavaScript und AngularJS als GUI-Technologie, setzen sie im Backend statt auf Java/Spring gerne auf Node.js, um eine einheitliche Programmiersprache verwenden zu können.

Makroarchitektur Mikroarchitektur
Continuous Deployment Freie Technologiewahl bei Team-Commitment
DNS-Namensschema Keine hausinternen, geteilten Bibliotheken
Logging (Pattern-Layout und Aggregation in Splunk) Bibliotheken als Open-Source-Projekte veröffentlichen
Metrikenanzeige (Graphite) Shared Nothing Model
Monitoring (Icinga/health)
Request-Verfolgung (HTTP-Header X-Trace-ID)
REST (Abwärtskompatibilität)
Security (Auth-Tokens, Rechte …)

Tabelle 1: Übersicht der Architekturplanken bei EUROSPACE 2

Großen Wert legen wir auf Know-how-Transfer. Von den Vorteilen neuer Technologien und innovativer Lösungen aus den Teams sollen alle Entwickler im Haus profitieren. Auf wöchentlichen Know-how-Sessions, in themenspezifischen Communities (z. B. DevOps) und Wissensnachmittagen im Abstand von sechs Wochen präsentieren die Teams ihre Lösungen und tauschen Erfahrungen aus.
Statt vorgeschriebenen Standardtechnologien kommt zum Einsatz, was sich bewährt hat und kosteneffektiv Business Value ermöglicht.

Unser Lieblings-Micro-Service-Technologiestack

Die Java-basierten Services sind mit folgenden Technologien erstellt:

• Java 8
Spring Boot
o Embedded Jetty Container
o Executable Jar
o Property-Management
o HTTP-Ressourcen
Spring HATEOAS
Jersey-HTTP-Client
Spring Data MongoDB
TestNG
Mockito
FEST Assertions
Gradle (Build and Deployment)
MongoDB

Insbesondere Spring Boot hat uns begeistert. Ein mitgeliefertes Maven oder Gradle-Plug-in erzeugt ein executable JAR, das per java -jar myservice.jar gestartet wird. Aus der IDE heraus starten wir direkt die main()-Methode der Application class. Ein Servlet-Container wird embedded hochgefahren. Von großem Vorteil ist die sehr kurze Start-up-Zeit: Schon nach sieben Sekunden beantwortet der Service HTTP-Requests.
Jeder Service wird bei uns typischerweise in drei verschiedenen Ablaufumgebungen (Stages) ausgeführt: Lokal auf einem Entwicklerrechner, auf einem Testsystem und auf dem Produktionssystem.
Stage-abhängige Konfigurationsparameter hinterlegen wir in Property-Dateien, die über Spring Profiles aktiviert werden. Zum Beispiel aktiviert das Kommandozeilenargument –spring.profiles.active=local das Profil local, sodass der Service auf Port 8080 lauscht.

[ header = Seite 3: Multi-Pipeline-Abhängigkeiten ]

Multi-Pipeline-Abhängigkeiten

Anders als bei monolithischen Anwendungen mit nur einer Deployment Pipeline existieren im Micro-Serviceumfeld viele Pipelines: eine pro Service. Jeder Service hat einen eigenen (Entwicklungs-)Lifecycle. Dank Continuous Deployment rollt jede Änderung durch die Pipeline vollautomatisch auf das Produktionssystem aus. Hier stellt sich die Frage der Synchronisierung von Deployments mehrerer Services. Es gibt vier mögliche Synchronisierungsoptionen:

• Gleichzeitiges, gemeinsames Deployment aller Services
• Serielles Deployment (ein Service nach dem anderen)
• Zeitpunktbasiertes Deployment (jedem Service seinen Timeslot)
• Unkoordiniertes, wahlfreies Deployment

Wir haben uns für den letzten Punkt – das nicht synchronisierte Deployment entschieden. Dies ermöglicht vor allem eine teamübergreifend blockadefreie Entwicklung. Anders als bei allen anderen Varianten muss kein Service (und damit kein Entwickler/Team) auf das Deployment eines anderen Service warten. Das Risiko eines Produktionsbugs durch zwei sich überschneidende Deployments mit inkompatiblen Softwareversionen wurde als sehr niedrig eingeschätzt. Nicht zuletzt wegen der sich aus Continuous Deployment ergebenden kleinen Änderungen.
Deployment Pipelines bilden wir mit TeamCity ab. TeamCity Build Goals und Build Steps koordinieren die einzelnen Schritte und delegieren die inhaltliche Ausführung an Gradle-Tasks oder Shell-Skripten.

Integrationstests

Continuous Deployment erfordert es, dass jede Änderung nur dann auf das Produktionssystem ausgerollt wird, wenn sie mit Tests abgesichert oder hinter einem „Feature-Switch“ versteckt ist. Wir unterscheiden folgende Integrationstestarten:

• Serviceintegrationstests
• Consumer-driven-Contract-Tests
• Systemintegrationstests

Serviceintegrationstests testen den für sich stehenden Service. Alle Downstream-Dependencies werden dazu über das Spring-Profil „standalone“ durch Mocks ersetzt. Ausgenommen ist dabei die Datenbank. DB-Interaktionen sind zentraler Bestandteil des Service und werden daher mit getestet. Serviceintegrationstests haben – dank der kurzen Start-up-Zeiten von Spring Boot – eine kurze Ausführungszeit von deutlich weniger als einer Minute und erzeugen praktisch keine Fehlalarme.
Wir möchten vor dem Produktions-Deployment auch das Zusammenspiel von zwei oder mehr Services testen. Dies wird insbesondere dann wichtig, wenn die Services von unterschiedlichen Teams entwickelt werden. Denn es kann schnell passieren, dass die Information über das kleine Schnittstellen-Refactoring nicht an alle Consumer(-Teams) verteilt wird. Zwei inkompatible Services könnten dadurch auf das Produktionssystem ausgerollt werden. Um die Gesamtausführungszeit einer Pipeline nicht durch viele Systemintegrationstests in die Länge zu ziehen, waren wir auf der Suche nach einer Integrationstestebene mit relativ kurzen Laufzeiten. Wir nennen sie Consumer-driven-Contract-Tests. Die Idee ist, dass nicht nur der Serviceanbieter (Provider) Tests erstellt, sondern vor allem die Benutzer des Diensts (Consumer) ihre Sicht und ihre benötigte Funktionalität gegenüber dem Provider testen (Abb. 3). Ein Schnittstellenvertrag muss von beiden Parteien erfüllt werden. Die Konsumenten eines Diensts erstellen dazu eine eigene Testsuite, welche in der Deployment Pipeline des Dienstanbieters ausgeführt wird. Schlägt ein Test fehl, bleibt die Pipeline des Dienstanbieters stehen.

Abb. 3: Schnittstellenvertrag

Im Diagramm sind drei Services abgebildet: A ist in der Rolle des Consumers gegenüber des Serviceproviders B. B wiederum ist selbst Consumer des Service C. Der Service B befindet sich gerade in der Pipeline. Ein Vertrag muss vom Serviceprovider wie vom Service-Consumer eingehalten werden. B hat bereits standalone getestet, dass alle seine Schnittstellen korrekt sind. Der Consumer (A) zeigt, dass der Vertrag (von B) erfüllt wird. Dies geschieht mithilfe einer Consumer-driven Testsuite, die als separates Artefakt durch A bereitgestellt wird. Da B ebenfalls in der Rolle des Consumers ist (gegenüber C), stellt B auch eine Testsuite bereit. Damit der Mechanismus funktioniert, müssen die Deployment Pipelines der beiden Services kreuzweise verknüpft werden: Rollt der Service B aus, müssen die Tests von A ausgeführt werden, sowie die Tests von B; rollt der Service A aus, müssen nur die Tests von A ausgeführt werden. Entsprechendes gilt für C. Systemintegrationstests sind dennoch notwendig, um die Korrektheit von geschäftskritischen, stark verteilten Prozessen zu zeigen. Diese können durch Contract-Tests auf ein Minimum in Anzahl und Variabilität reduziert werden.

Deployment

Das Deployment erfolgreich getesteter Artefakte erfolgt vollautomatisch am Ende der Deployment Pipeline. Da bisher alle Services auf der JVM basieren, haben wir uns entschlossen, einen leichtgewichtigen, im Sourcecode-Projekt des Service enthaltenen Deployment-Mechanismus zu implementieren. Dies ist eine sehr einfache aber vollkommen ausreichende Alternative zu RPM oder Docker. Die JVM-basierten Micro Services erwarten von ihrer Umgebung Oracle Enterprise Linux, eine installierte JVM sowie einen Betriebssystemnutzer.

Zur Realisierung eines unterbrechungsfreien Deployments nutzen wir das „Rolling Update“-Konzept. Dabei wird jeder Service des Verbunds einzeln aus dem Load Balancer herausgenommen, heruntergefahren, mit neuer Software betankt, wieder hochgefahren und bei Erfolg in den Load Balancer aufgenommen. Die Steuerung der einzelnen Schritte erfolgt in der pipeline.gradle-Datei. Das Gradle ssh plugin kopiert das zu deployende Artefakt auf die entfernten Rechner und stoppt bzw. startet danach den Service. Jede nicht lokale Ablaufumgebung besteht bei uns aus mindestens zwei Rechnern. Für das Deployment ist es wünschenswert, ein Artefakt mit nur einem Gradle-Aufruf auf alle Rechner einer Ablaufumgebung zu kopieren. Dies wird durch das gradle ssh plugin unterstützt. Es erlaubt die Angabe einer Rolle (role) für jeden hinterlegten Rechner (remote). Diese Rollen bilden wir auf unsere Ablaufumgebung ab. Durch das Kommando gradle deploy-ps werden alle Produktionsrechner nacheinander betankt. Als vertraute Schnittstelle zwischen Operationsmitarbeiter und Service liefern wir ein service.sh-Shell-Skript mit aus. Dies enthält die typischen Argumente

start|stop|restart|status|online|offline

Die Restart-Spezifika eines Service bleiben durch diese einfache Fassade verborgen. Im Störfall kann jeder Operator ohne große Vorkenntnisse den Service von außen bedienen.

Fazit

Einen Micro Service zu entwickeln, ist wie ein green-field-Projekt in den ersten drei Monaten. Das macht richtig Spaß. Und zwar nachhaltig. Zufriedene und hoch motivierte Mitarbeiter sind die Konsequenz. Fachlich bilden unsere Services eine geschlossene Funktionalität ab. Neue Anforderungen werden in neuen Services implementiert (Open Closed Principle). Einem grenzenlosen Wachstum der Code Base bis hin zu einem Monolithen ist damit ein für alle mal ein Riegel vorgeschoben. Jeder Service bleibt in Gänze verständlich und wartbar. Durch die technologische Unabhängigkeit jedes Micro Service, sind Updates, neue Frameworks oder andere Programmiersprachen problemlos möglich. Entwickler profitieren von den Vorteilen aktueller Frameworks und kurzer Turnaround-Zeiten. Damit können wir Anforderungen schneller umsetzen. Der Micro-Service-Architekturstiel – die vierte Modularisierungsinkarnation bei Hypoport – ist für uns ein echter Erfolg.

Geschrieben von
Timmo Freudl-Gierke
Timmo Freudl-Gierke
Timmo Freudl-Gierke arbeitet als Head Architekt bei der Hypoport AG. Er verantwortet die Finanztransaktionsplattform EUROPACE 2. Sein Wissen teilt er regelmäßig auf Konferenzen und Workshops. Er ist Organisator der MongoDB User Group Berlin und großer Fan von leichtgewichtigen Webarchitekturen.
Kommentare
  1. Sven Bunge2015-07-20 18:55:23

    Moin,
    in einem Kundenprojekt haben wir vor längerem auch alle Services schön schlank geschnitten. Die Vorteile haben weit überwogen, aber das Verfolgen von Nutzer- oder Service-Interaktionen in den Logs wurde über die Anzahl der Server schwieriger. Auch wir haben dann eine Trace-ID geschaffen, aber das Framework OpenSource allen zur Verfügung gestellt. Mitlererweile liefern auch externe Services die Trace-Informationen mit und wir können uns auch in Jira-Tickets über Invocations und weitere Nutzdaten unterhalten.
    Das Framework heißt TracEE und es ermöglicht via Interceptoren/Filter die Nutzdaten zu übertragen. Nicht nur im Request, auch im Response. Einfach unter http://tracee.io mal vorbei schauen. Feedback ist ausdrücklich gewünscht, ebenso PRs :-)

    Beste Grüße und vielen Dank für den schönen Artikel,
    Sven

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.