Microservices mit Spring Boot & Spring Cloud

Von: Thomas Bayer
Datum: 18. November 2019

Mit über 42.000 Sternen auf der github Seite ist Spring Boot das mit Abstand populärste Framework für die Entwicklung von Microservices. Spring Boot ist opinionated oder „voreingenommen“, d.h. es macht Annahmen darüber, was ein Entwickler oder Betreiber einer Anwendung haben möchte. Das ermöglicht die produktive Entwicklung eines Anwendungsgerüstes welches u.a. Aspekte wie Konfiguration, Logging und Monitoring abdeckt.

Die Spring Cloud Projekte ergänzen Spring Boot um Funktionen für die Cloud wie z.B. Service Discovery, Routing oder Fault Tolerance.

Dieser Artikel beschreibt die Verwendung von Spring Boot und Spring Cloud für die Entwicklung sowie den Betrieb von Microservices.

1 Spring Framework

Basis für Spring Boot und Spring Cloud ist das bewährte Spring Framework. Spring bietet die Prinzipien Dependency Injection und Inversion of Control, welche eine lose Kopplung der Komponenten fördern. Das führt zu übersichtlichen Architekturen und fördert die Austauschbarkeit von Bausteinen. Zusätzlich erleichtert die lose Kopplung der Komponenten die Testbarkeit.

Eine Schwierigkeit bei der Arbeit mit dem Spring Framework ist das Bereitstellen von zahlreichen Konfigurationen und die Verwaltung der Abhängigkeiten, die Komponenten bereitstellen. Diese Probleme werden von Spring Boot adressiert.

2 Spring Boot

Spring Boot ist eine Ergänzung, die auf dem Spring Framework aufsetzt und die Erstellung von produktionstauglichen Anwendungen erleichtert.

Das Auto Configuration Feature erkennt, ob Komponenten im Classpath liegen und erstellt daraufhin Beans. Ein Beispiel wäre die Erzeugung einer DataSource-Bean mit Einstellungen für die Postgres Datenbank, wenn im Klassenpfad ein Treiber dieser Datenbank gefunden wird.

Das Spring Framework lässt dem Entwickler einen großen Spielraum bei der Auswahl von Abhängigkeiten. Die Auswahl passender Abhängigkeiten kostet Zeit und stellt eine Fehlerquelle dar. Die Spring Boot Starter fassen mehrere Abhängigkeiten in eine zusammen und ermöglichen mit dem Einbinden einer einzigen Abhängigkeit eine Technologie wie MVC oder JPA zu nutzen.

Spring Boot Starter Beschreibung
spring-boot-starter-web Web Anwendungen und REST Schnittstellen
spring-boot-starter-data-jpa Spring Data JPA mit Hibernate
spring-boot-starter-artemis JMS mit Apache Artmis
spring-boot-starter-security Spring Security
spring-boot-starter-hateoas Für RESTful APIs mit Hypermedia und HAL

Tabelle: Auszug aus der Liste der Spring Starter

3 Spring Cloud

Spring Cloud erweitert Spring Boot um Projekte für die Entwicklung von Cloud tauglichen Anwendungen und Services. Zu Spring Cloud zählen zahlreiche Projekte, mit denen Anwendungen bzw. Services um Funktionen für den Betrieb in der Cloud ergänzt werden können.

Der Name Spring Cloud bedeutet nicht, dass der Service zwingend in einer externen Cloud wie Amazon AWS oder Microsofts Azure betrieben werden muss. Cloud Features wie z.B. zentrale Konfiguration oder das Nachverfolgen von Aufrufen sind auch ohne Cloud nützlich. Spring Cloud stellt Funktionen zur Verfügung, die in der Cloud hilfreich oder notwendig sind. Ein Betrieb in der Cloud ist aber nicht die Voraussetzung um diese Features zu nutzen.

Ob ein Feature Bestandteil von Spring Boot oder Spring Cloud ist an der Maven GroupId des jeweiligen Projektes zu erkennen. Rein technisch gibt es keinen Unterschied zwischen einem Spring Boot- und einem Spring Cloud Starter. In der Tabelle unten sind einige Projekte mit ihren Maven Koordinaten aufgeführt.

Funktion GroupId ArtifactId Spring Boot oder Cloud?
Management Endpunkte (HTTP) org.springframework.boot spring-boot-starter-actuator Spring Boot
Deklarativer HTTP Client org.springframework.cloud spring-cloud-starter-openfeign Spring Cloud
Service Discovery org.springframework.cloud spring-cloud-starter-consul-discovery Spring Cloud
Configuration Client org.springframework.cloud spring-cloud-config-client Spring Cloud

4 Versionsdschungel

Eine Spring Boot Anwendung hat gewöhnlich zahlreiche direkte und indirekte Abhängigkeiten. Die Versionen der Abhängigkeiten müssen zueinander passen, um Probleme zu vermeiden. Zuerst müssen die verwendeten Spring Boot und Spring Cloud Versionen zueinander passen. Die zu verwendende Spring Cloud Version ergibt sich aus der Spring Boot Version. Die Tabelle unten zeigt passende Versionen ab der Spring Boot Version 2.0.

Spring Boot Version Spring Cloud Version
2.0 Finchley
2.1 Greenwich
2.2 Hoxton

Die Kompatibilität der weiteren Abhängigkeiten zueinander wird über eine Liste von Versionen gewährleistet, die Bill of Materials oder kurz BOM heißt. Die BOM ist ein Feature von Maven, die einem die Angabe der Versionsnummer bei Abhängigkeiten erspart. Der Code unten zeigt einen Auszug aus der build.gradle Datei. Das Gleiche gilt ebenso für eine Maven pom.xml Datei. Die Abhängigkeit bekommt ihre Versionsnummer aus dem BOM für Spring Cloud. Die Angabe einer Versionsnummer für die Abhängigkeit ist unnötig und würde bei einer Aktualisierung nur Inkonsistenzen verursachen.

Anstatt:

implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:2.1.3.RELEASE'

genügt:

implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:2.1.3.RELEASE'

5 Dependency Injection

Basis für Spring Boot ist das bewährte Spring Framework und das Konzept der Dependency Injection, welches eine lose Kopplung der Komponenten unterstützt. Die Dependency Injection führt zu übersichtlichen Architekturen und fördert die Austauschbarkeit von Bausteinen. Die lose Kopplung der Komponenten durch Dependency Injection und Inversion of Control erleichtert die Testbarkeit.

Sämtliche Spring Boot Module verwenden Dependency Injection. Mit Hilfe von Konfiguration und eigenen Beans lässt sich das Verhalten fast beliebig anpassen. Beispiele hierfür wären:

Bereich Beispiel für eine Modifikation über Beans
Loadbalancing Eigener Algorithmus für die Auswahl der Konten
Retries Festlegen unter welchen Bedingungen ein erneuter Verbindungsversuch unternommen wird.
Fault Tolerance Einklinken einer eigenen Aktion, die nach erfolglosen Übertragungsversuchen aufgerufen wird.

6 Funktionen

Es gibt zahlreiche Funktionen, die für die Entwicklung von Cloud-fähigen Services genutzt werden können. Unter anderem stehen die folgenden Features zur Verfügung:

  • Konfigurations-Management
  • Service Discovery
  • Circuit Breaker
  • Routing
  • Einmal Tokens
  • Globale Sperren
  • Leadership Election
  • Cluster State
  • ...

Jede Funktion wird von einer eigenen Abhängigkeit zur Verfügung gestellt. Für die Verwendung einer Funktion sind folgende Schritte notwendig:

  1. Hinzufügen einer Abhängigkeit ( Bibliothek) zum Maven- oder Gradle Build
  2. Eventuell Angabe von Konfigurationsoptionen
  3. Eventuell das Hinzufügen von Annotationen

In seltenen Fällen sind wenige Zeilen Code notwendig.

Die meisten der Funktionen können in eigene Anwendungen eingebunden werden. Beispielsweise muss kein eigener Server installiert werden, um Spring Cloud Gateway oder den Config Server zu nutzen. Man schreibt sich einfach selbst ein Gateway, indem man eine gewöhnliche Spring Boot Anwendung erstellt und eine Abhängigkeit zum Spring Cloud Gateway hinzufügt. Danach kann das Verhalten mit Konfiguration oder eigenen Beans beeinflusst werden. Der Vorteil gegenüber einer fertigen Server Software liegt darin, dass mit diesem Ansatz ein Server genauso flexibel erweitert und verändert werden kann, wie eine eigene Anwendung. Typisch für neue Infrastrukturkomponenten ist, dass sie nur das mitbringen, was unbedingt benötigt wird. Beispielsweise fehlt dem Router noch Security oder Logging. Dies ist aber kein Nachteil, vielmehr ein Vorteil. Um Security zu implementieren, nimmt man zum Projekt noch eine Abhängigkeit zu Spring Security hinzu und konfiguriert das Verfahren, das zu den anderen Services passt. Logging und Monitoring können ebenfalls bei Bedarf ergänzt werden.

In den folgenden Abschnitten werden einzelnen ausgewählte Features kurz beschrieben.

6.1 Spring Cloud Config

Mit Spring Cloud Config können Services über einen zentralen Server mit einer externen Konfiguration versorgt werden.Als Backend für die Ablage und Verwaltung von Konfigurationen werden git, Datenbanken über JDBC, Hashicorp Vault, Credhub und das Dateisystem unterstützt.

Alternativ zu einem Spring Cloud Config Server können verteilte Registries wie Zookeeper und Consul als Konfigurationsserver eingesetzt werden.

6.2 REST API

REST APIs sind die bei Microservices vorherrschende Technologie für die Kommunikation. Selbstverständlich wird REST von Spring Boot/Cloud unterstützt.

6.3 REST Client

Ein REST Client ermöglicht einem Service den Aufruf eines Endpunktes bei einem anderen Service.

Dem Entwickler steht gleich eine Auswahl von REST Clients zur Verfügung. Wichtig für den Einsatz in der Cloud ist, dass der Client mit den übrigen Cloud Features wie Loadbalancing oder Service Discovery zusammenarbeitet.

6.3.1 OpenFeign

Feign ist ein deklaratives API für REST Clients mit Einflüssen aus Retrofit, JAX-RS und WebSockets.

Die Schnittstelle des Servers wird bei Feign auf ein Java Interface abgebildet. Notwendige Angaben wie z.B. die Methode oder der Pfad werden über Annotationen ergänzt.

Der folgende Aufruf:

GET

http://api.predic8.de/shop/preis/{artikel}

wird bei Feign auf das Interface im Codebeispiel unten abgebildet:

@FeignClient(url="http://api.predic8.de/shop", name = "preis")
interface PreisClient {

    @RequestMapping(method = GET, value = "/preis/{artikel}")
    Preis getPreis(@PathVariable("artikel") String artikel);

}

Feign verwendet selbst austauschbare Client Bibliotheken um die Aufrufe abzusetzen. Die Tabelle unten listet REST Bibliotheken, die Feign verwenden kann und deren Besonderheiten.

Bibliothek Besonderheit
JAX-RS JAX-RS ist ein Java Standard
Ribbon Unterstützt Loadbalancing und Resiliency
OkHttp Unterstützt SPDY
Java 11 Http2 Unterstützt HTTP/2

Feign kann mit den Circuit Breaker Hystrix (veraltet) zusammenarbeiten.

Momentan verwendet Feign noch Ribbon von Netflix. In Zukunft wird Ribbon durch den Spring Cloud Loadbalancer erstetzt.

6.4 Service Discovery

In der Cloud können Services nicht über IP Adressen und Portnummern hart verdrahtet werden. Wo ein Zielservice später läuft steht nicht zur Entwicklungszeit fest. Wird ein Knoten in der Cloud außer Betrieb gesetzt, werden die dort installierten Services auf andere Knoten verlagert und die Adressen der Services ändern sich entsprechend. Über Service Discovery können Services zeitnah über die aktuelle Adresse von Zielservices informiert werden.

Für Service Discovery verwendet Spring Cloud einen zentralen Verzeichnisdienst, die Registry, bei der sich Services beim Startup anmelden. Über den Health-Endpunkt des Management Interface ( Actuator) wird die Betriebsbereitschaft der Services ständig überwacht. Ist eine Instanz eines Service nicht erreichbar, so wird diese aus der Liste entfernt. Anstatt einer tatsächlichen Adresse mit Hostname und Port können die Clients jetzt einfach den Service Namen verwenden, der über die Registry in die tatsächliche Adresse aufgelöst wird.

Unter anderem arbeiten der REST Client Feign und das Spring Cloud Gateway nahtlos mit Service Discovery zusammen. Zur Umstellung auf Service Discovery genügt meist die Einbindung einer Abhängigkeit.

Die Tabelle unten führt verschiedene Registries auf, die mit Spring Cloud verwendet werden können.

Consul Eureka etcd Zookeeper
Hersteller Hashicorp Netflix Cloud Native Computing Foundation Apache
Lizenz MPL 2.0 & kommerziell Apache ASF 2 Apache ASF 2 Apache ASF 2
Plattform/Sprache Go Java Go Java
Schnittstelle REST API REST API REST API Java RMI

6.5 Spring Cloud Security

Mit Spring Cloud Security können einzelne Services in der Cloud mit Passwort oder über ein OAuth2 Token abgesichert werden. Der Setup ist wieder deklarativ und leicht aufzusetzen.

Entgegengenommene Tokens können an nachgelagerte Services automatisch weitergegeben werden.

[Abbildung Delegation Token]

Selbstverständlich können der Feign Client und das Zuul API Gateway in Verbindung mit Spring Cloud Security ebenfalls Tokens an nachgelagerte Services weitergeben.

6.6 Ciruit Breaker

Eine Überlastsicherung unterbricht den Stromkreis um Schaden abzuwenden. Eine Überlastung liegt z.B. vor, wenn die Häufigkeit von Aufrufen oder die Fehlerrate einen bestimmten Schwellwert übersteigt.

Spring Cloud Netflix beinhaltet den Hystrix Circuit Breaker von Netflix. Über ein Dashboard, kann der Zustand von mehreren Circuit Breakern überwacht und Statistiken eingesehen werden. Hystrix bietet auch Fallback-Funktionalität, um bei Fehlern auf alternative Funktionen und Defaultwerte auszuweichen.

Der populäre Hystrix von Netflix ist mittlerweile im Maintenance Mode und wird nicht mehr weiter entwickelt. Als Ersatz wir Resilience4j empfohlen. Einen aktuellen in Spring Cloud integrierten Circuit Breaker gibt es momentan nicht (Stand November 2019).

6.7 Routing

Der Zuul Router wurde von Netflix speziell für Spring Boot entwickelt. Mittlerweile wurde Zuul von Spring Cloud Gateway abgelöst und befindet sich im Maintenance Mode.

6.7.2 Spring Cloud Gateway

Das Spring Cloud Gateway bietet in Verbindung mit den anderen Spring Projekten wie Security, Service Discovery, Logging und Monitoring eine leistungsfähige Lösung und steht in Konkurrenz zu anderen API Gateway Lösungen. Spring Cloud Gateway bietet den Vorteil, dass es nahtlos integriert werden kann.

Das Routing ist über Regeln frei konfigurierbar. Die Entscheidung für eine Route kann u.a. von folgenden Größen beeinflusst werden:

  1. Pfad
  2. Methode
  3. HTTP Header

Neben dem Routing können Anfragen manipuliert und Pfade umgeschrieben werden. Weitere Features sind Loadbalancing, Retries und Service Discovery.

6.8 Polyglotte Microservices mit Spring Cloud und Side Cars

Microservices unterstützen die polyglotte Entwicklung und sind nicht auf eine Programmiersprache limitiert. Alle Komponenten von Spring Boot bzw. Cloud sind mit Java realisiert. Wie lassen sich in diese Infrastruktur Services, die mit anderen Sprachen realisiert wurden nahtlos integrieren? Das Side Car Projekt ermöglicht nicht-Java Microservices einen Java basierten Sidecar „an die Seite“ zu stellen, der die Kommunikation mit Spring Cloud Komponenten wie Discovery, Health, Circuit Breaker oder Zuul Routing ermöglicht.

6.10 Spring Cloud Bus

Spring Cloud Bus verbindet die Services mit einem Message Broker. Zur Auswahl stehen. Apache Kafka oder ein beliebiger Broker, der das AMQP Protokoll unterstützt wie beispielsweise der RabbitMQ.

6.11 Spring Cloud Sleuth

Wenn Services andere Services aufrufen, geht der Überblick leicht verloren. Mit Distributed Tracing kann der Weg von Aufrufen nachverfolgt werden. Daten über alle Aufrufe oder um die Performanz zu schonen nur eine Stichprobe der Daten können mit Spring Cloud Sleuth automatisch im Hintergrund an eine Tracing Lösung z.B. Zipkin oder Jaeger übergeben werden.

Spring Cloud Sleuth ist kompatibel zur OpenTracing Spezifikation.

7 Deployment

Mit Spring Boot realisierte Services können auf unterschiedliche Arten betrieben werden. Die folgenden Deployments sind besonders für Microservices interessant:

  • FAT-JAR
  • Docker Container
  • Kubernetes
  • Cloud Foundry

7.1 FAT JAR

Eine Spring Boot Anwendung kann mitsamt Web Container z.B. dem Tomcat oder Jetty in eine einzige Datei gepackt werden. Ausgeführt werden kann diese Datei mit einer virtuellen Java Maschine, ein Application Server ist nicht notwendig. Der Vorteil dieses Deployments liegt in der Vereinfachung. Ein Nachteil ist die Dateigröße eines „FAT“ JARS, die bei mindestens ca. 30 Mbyte liegt.

7.2Docker Container

Eine Spring Boot Anwendung kann auch in einen Docker Container ausgeführt werden.

7.3 Kubernetes

Container Orchestratoren wie Kubernetes können Service Instanzen überwachen und kontrollieren, ob die Instanz noch lebt. Die dafür notwendigen Health-Endpunkte können über die Spring Boot Actuatoren bereitgestellt werden. Für Kubernetes gibt es noch eine weitergehende Unterstützung, die weiter unten beschrieben wird.

7.4 Cloud Foundry

Hinter dem Spring Universum steht die Firma Pivotal, die auch Cloud-Anbieter ist. Daher verwundert es nicht, dass Spring Boot Deployments in der Pivotal Web Services Cloud unterstützt werden.

8 Unterstützung für die Cloud

Wie im vorherigen Kapitel beschrieben können Spring Boot Anwendungen im Docker Container, unter Kubernetes und in Cloud Foundry basierten Cloud Systemen betrieben werden.

Ein Betrieb in der Amazon Web Services Cloud oder unter Microsofts Azure ist auf verschiedene Weise möglich. Services können dort in virtuellen Maschinen (EC2), auf Containerdiensten oder unter Kubernetes eingesetzt werden.

Für den Container Orchestrator Kubernetes gibt es eine erweiterte Integration.

8.1 Spring Cloud Kubernetes

Spring Cloud Kubernetes erleichtert die Integration von Spring Boot Anwendungen in eine Kubernetes Laufzeitumgebung. Um die hier aufgezählten Features von Kubernetes in einer Spring Boot Anwendung zu nutzen, muss Dank der allgegenwärtigen Abstraktionen der Quellcode nicht angepasst werden. Es genügt die richtige Abhängigkeit zu verwenden und die Konfiguration anzupassen. Werden für ein Projekt mehrere Profile angelegt, so kann das gleiche Projekt nicht nur unter Kubernetes sondern auch in weiteren Umgebungen betrieben werden.

Für Kubernetes gibt es einen speziellen Health-Indicator, der Kubernetes spezifische Information wie Name des Pods, Namespace und den Nodenamen ausgibt.

In Kubernetes können Pods über Services angesprochen werden. Diese Services sind mit dem Spring Discovery Client über den Servicenamen auffindbar.

Die in Kubernetes über ConfigMaps verwaltete Konfiguration kann in Spring Boot Anwendungen eingebunden werden.

Security Credentials wie Zertifikate, OAuth 2 Tokens oder Passwörter können in Kubernetes mit der Secrets Funktion verwaltet werden. Mit diesen Secrets können auch Spring Boot Anwendungen transparent versorgt werden.

Der Loadbalancer Ribbon kann aus Kubernetes mit den Adressen von Endpunkten versorgt werden.

9 Spring Cloud Netflix

Die Open Source Projekte von Netflix können mit diesem Projekt auf die Spring Boot typische Weise über Starter und Autoconfiguration verwendet werden. Zur Verfügung gestellt werden folgende Muster:

Microservices Pattern Projektname Bemerkung Ersatz
Service Discovery Eureka
Circuit Breaker Hystrix Maintenance Mode Resilience4J
Client Side Load Balancer Ribbon Maintenance Mode
Zentrale Konfiguration Archaius Maintenance Mode Spring Cloud Config
Router Zuul Maintenance Mode Spring Cloud Gateway

Leider sind die meisten Projekte in Maintenance Mode, d.h. sie werden noch gepflegt, aber nicht mehr weiterentwickelt.

10 Vorteile und Nachteile

Einige Vorteile von Spring Boot sind zugleich auch Nachteile.

10.1 Vorteile

  • Spring Boot hat mit Abstand die größte Community der Microservices Frameworks.
  • Das Angebot an Funktionen und Muster ist riesig.
  • Die Infrastruktur wird immer abstrahiert und ist daher austauschbar.
  • Es ist umfangreiche Dokumentation, viele Bücher und Tutorials verfügbar.
  • Viele Entwickler sind bereits mit dem Spring Framework oder mit Spring Boot vertraut.

10.2 Nachteile

  • Es gibt für viele Funktionen mehrere Alternativen im Spring Stack, so dass eine Auswahl schwerfällt.
  • Es sind relativ viele Abhängigkeiten notwendig. Dazu kommen die transitiven Abhängigkeiten.
  • Es ist eine zeitintensive Einarbeitung in die Konzepte von Spring und Spring Boot notwendig.
  • Die „Magie“ d.h. die Mechanismen, die im Hintergrund ablaufen sind nicht leicht nachzuvollziehen.

11 Fazit

Laut der Statistik von Google (1) ist Java die populärste Programmiersprache und Spring Boot das populärste Framework für Microservices.

Spring Boot und Spring Cloud erleichtern erheblich die Erstellung von Anwendungen bzw. Services für die Cloud. Notwenige Features wie Health-Checks, Konfiguration oder Monitoring können in kurzer Zeit realisiert werden. Der Einarbeitungsaufwand lohnt sich nach kurzer Zeit, besonders wenn mehrere Services zu realisieren sind, die alle die gleichen Features benötigen.

Im Spring Universum findet sich für jede Anforderung ein Projekt oder ein Spring Boot Starter. Fehlendes oder abweichendes Verhalten kann mit wenig Aufwand mit einer Bean, einem Listener oder einer Fabrik eingeklinkt werden. Wer keine Probleme damit hat, sich das Passende aus der großen Auswahl zusammenzustellen und anzupassen wird mit Spring Boot glücklich.

Wer es bevorzugt, weniger oder keine Alternativen zu haben, der wird sich mit Spring Boot und Spring Cloud schwertun. Microservices Teams, die ein kompakteres, überschaubares und eingeschränkteres Toolkit vorziehen und auch sonst die Java Enterprise Edition anstatt das Spring Framework einsetzen, könnten die Fülle an Möglichkeiten und die Komplexität des Spring Universums kritisch betrachten. In diesem Falle empfehle ich vor der Entscheidung für den Favoriten Spring Boot einen Blick auf die Alternativen Eclipse MicroProfile oder Micronaut zu werfen.

Prinzipiell macht man mit der Entscheidung Java Microservices mit Spring Boot und Cloud zu realisieren keinen Fehler. Vielleicht sind aber auch das Eclipse MicroProfile z.B. Aufgrund der schnellen Startzeiten oder Micronaut wegen der Kotlin Unterstützung interessant.

12 Quellen

(1) Google Trend: Microservices ( 26.11.2019) https://trends.google.de/trends/explore?q=%2Fm%2F011spz0k