Das Projekt Spring Cloud Netflix integriert nur Spring Boot mit den Netflix Open Source Bibliotheken und bindet diese in die bekannte Auto-Konfiguration und das Binding von Spring Boot mit ein. Mit Hilfe von Spring Cloud Netflix ist es möglich durch wenige, einfache Annotationen die entsprechenden Komponenten von Netflix zu integrieren, konfigurieren und zu nutzen. Im Rahmen des Artikels geben wir ihnen einen Überblick über die Anbindung von Eureka (Service Discovery), Zuul (Intelligentes Routing), Ribbon (Client-seitiges Load-Balancing) und Hystrix (Circuit Breaker).

Eureka

Eine Microservice-Architektur beruht unter anderem darauf, dass die Services technisch unabhängig lauffähig sind. Dies bezieht auch mit ein, dass es mehrere Instanzen einzelner Services verteilt geben kann. Um nun die technische Kommunikation zwischen den verschiedenen Diensten zu ermöglichen, müssen sie natürlich wissen, unter welcher Netzwerk-Adresse die jeweilige Gegenseite zu erreichen ist. Dies könnte mit viel Aufwand und in Abhängigkeit der aktuellen Infrastruktur (manuell) konfiguriert werden. Die Alternative dazu ist eine sogenannte Service Discovery: ein eigener Dienst, dessen Aufgabe eine zentrale Adress-Verwaltung für Services darstellt. Im Netflix-Stack übernimmt Eureka diese Aufgabe, bestehend aus zwei Komponenten: dem Eureka Server und dem Eureka Client. Derzeit liegt Eureka in Version 1.0 vor, welche von Spring Cloud Netflix in den beiden Eureka-Starter-Projekten eingebunden werden.

Eureka Server

Der Eureka Server wird als eigenständiger Dienst innerhalb einer Microservice-Architektur betrieben und stellt die (de-)zentrale Anlaufstelle für die Eureka Clients dar. Im Gegensatz zu anderen Diensten empfiehlt sich hier eine feste Adresse pro Instanz. Dies vereinfacht die Konfiguration der registrierenden Dienste bzw. die Anfragen nach Service-Adressen. Mehrere Eureka-Server lassen sich so auch einfach zu einem Cluster zusammenschließen, da jeder Eureka Server selbst per Konvention auch Client ist und somit die gleichen Methoden (Registry-Fetch von den Peers) nutzen kann.

Als Basis für einen eigenen Eureka Server kann man sich an dem von Spring Cloud [1] bereitgestellten Projekt orientieren. Der Service kommt fertig konfiguriert und andere Dienste können sich einfach an der Adresse anmelden. Zum Umfang von Eureka gehört auch ein Dashboard (Abbildung 1), welches Auskunft über angemeldete Dienste und deren Status und Server-Details wie Replikationen, Speicherverbrauch und Uptime gibt.

Abb. 1: Eureka-Dashboard
Abb. 1: Eureka-Dashboard

Eureka Client

Die Aufgaben des Eureka Clients [2] lassen sich durch die typische Architektur ableiten: zum einen gibt es Application Clients, also Dienste, die andere Services kontaktieren möchten. Hier ist der Eureka Client dafür verantwortlich, Netzwerk-Adressen von den Eureka Servern zu erfragen und dem ausführenden Code zur Verfügung zu stellen. Da derzeit die komplette Registry bei Anfragen übertragen wird, werden die Antworten per Konvention client-seitig gecached. Ein Poll-Mechanismus erkundigt sich nach Aktualisierungen und fordert diese ggf. ein.

Auf der anderen Seite stehen die Application Services, welche selbst ihre Dienste anbieten. Der Eureka Client registriert den einzelnen Service am Eureka Server mittels einer REST-Anfrage (Operationen sind unter [3] beschrieben). Dieser gestartete Heartbeat-Prozess signalisiert in regelmäßigen Abständen dem oder den konfigurierten Eureka Server(n) den aktuellen Status. Bleiben die Heartbeats über längere Zeit aus, wird der Dienst als inaktiv markiert und nach einiger Zeit aus der Service Liste entfernt. Oft wird es hier zu Überschneidungen kommen, so dass ein Dienst sowohl als Client als auch als Service auftritt. Daher gibt es für den Eureka Client auch keine weitere Trennung, es kann aber festgelegt werden, ob sich ein Dienst am Eureka Server registrieren möchte oder nicht. Auch hier stellt Spring Cloud ein Demo-Projekt bereit, welches die Anmeldung am Eureka Server (s.o.) übernimmt. Es kann unter [4] abgerufen werden.

Ausblick Eureka 2.0

Als Motivation für die nächste Generation von Eureka werden von den Netflix-Entwicklern hauptsächlich [5] drei Gründe genannt:

  1. Anfragen des Eureka Clients werden derzeit mit der gesamten Registry beantwortet. Der Speicherbedarf könnte minimiert werden, wenn die Antwort nur nur das angefragte Subset enthielte.
  2. Derzeit werden Aktualisierungen vom Client angefragt (Polling). Zukünftig soll es möglich sein, sich für Änderungen einzuschreiben und die Updates zu empfangen (Push).
  3. Das aktuelle Replikations-Modell besteht aus der Client-Replikation der Daten innerhalb des Eureka Server Clusters (Peer-To-Peer) und dem Heartbeat, der an die konfigurierten Dienste sendet. Der aus dieser Replikation entstehende Traffic soll reduziert werden, was letztlich auch zu einer besseren Skalierbarkeit führt.

Derzeit wird bei Netflix an der Version 2.0 gearbeitet. Es gibt allerdings kein offizielles Release Date, so dass es aktuell nicht absehbar, ob (wobei wir fest davon ausgehen) und wann die aktualisierte Komponente über das Spring Cloud Projekt einsatzfähig ist.

Hystrix

Hat man mit Eureka einen Lookup auf Services umgesetzt so kommt Hystrix ins Spiel, die Aufrufe zu den externen Services abzusichern. Hystrix ist die Netflx Implementierung des Circuit Breaker Patterns. Da es in Microservice-Umgebungen Gang und Gäbe ist, mehrschichtige Service Calls zu haben kommt dem Library Hystrix eine zentrale Bedeutung zu. Es sorgt nämlich dafür, dass Fehler auf unteren Ebenen des Callstacks nicht nach oben kaskadieren. So schließt Hystrix beispielsweise bei zwanzig Fehlern in fünf Sekunden die Verbindung. Diesen Default kann man natürlich noch anpassen und zudem auf eine sogenannte Fallback-Implementierung umleiten. Zentrale Annotationen in Spring Cloud Netflix hierfür sind @EnableCircuitBreaker auf Ebene der Anwendungskonfiguration und @HystrixCommand auf Ebene von Methoden, deren Aufrufe man mit Hilfe des Circuit Breaker Patterns absichern will. Die Lösung von Spring sieht zudem noch eine Propagierung des Spring Security Contexts und die Nutzung von Spring Scopes vor. Mit Hilfe des @HystrixCommand Attributs commandProperties können sehr feingranulare Anpassungen an Hystrix konfiguriert werden.

Hystrix-Dashboard & Turbine

Wie beschrieben ist Hystrix die Implementierung des Circuit Breaker Patterns [6]. Das Monitoring der Funktionsfähigkeit von Diensten ist ein zentraler Erfolgsfaktor für Microservice-Architekturen. Aus der Hystrix-Bibliothek werden zu diesem Zweck Echtzeit-Metriken auf den Hystrix EventStream geschrieben, zum einen für die einzelnen HystrixCommands und zum anderen für die zugehörigen HystrixThreadPools. Die Daten beinhalten dabei den Status des Circuit Breakers, erfolgreiche und fehlgeschlagene Operationen, Durchsatz in Anfragen pro Sekunde, die durchschnittlichen Antwort-Zeiten samt Perzentilen sowie Daten zum Thread Pool wie Größe und der Anzahl aktuell in der Queue befindlichen Ausführungen.

Das Hystrix-Dashboard [7] ist eine von Netflix entwickelte Komponente zur grafischen Visualisierung der Daten aus dem EventStream. Abbildung 2 zeigt das Dashboard im Einsatz mit einem Mock-Stream aus den Beispielen von Spring Cloud [8]. Eine Einschränkung gilt für das Dashboard: es kann nur einen einzelnen Stream verarbeiten.

An dieser Stelle kommt Turbine [9] ins Spiel: dieser Teil des Netflix-Stacks aggregiert die Daten von multiplen, homogenen Streams zu einem Cluster und bietet diesen wiederum selbst als Event-Stream an. Das Hystrix-Dashboard ist darauf vorbereitet und zeigt unter anderem die Anzahl der aggregierten Hosts an.

Für beide Komponenten gibt es jeweils ein Spring-Cloud-Starter-Projekt [10] mit einer Beispiel-Implementierung, so dass die Integration ähnlich leichtfällt wie bei den übrigen Komponenten.

Abb. 2: Hystrix-Dashboard
Abb. 2: Hystrix-Dashboard

Clientseitiges Load Balancing: Ribbon

Ribbon ist ein clientseitiger Load Balancer, der es ermöglicht viel Kontrolle über das Verhalten von HTTP- und TCP-Clients auszuüben. Zentrales Konzept bei Ribbon ist der so genannte “Named Client”. In dessen Rahmen ist jeder Load-Balancer Teil einer Menge von Komponenten, die im Verbund einen entfernten Server kontaktieren. Dieser Verbund hat einen konkreten Namen und somit hat jeder Verbund seinen eigenen Application Context. Dies wird mit Hilfe der @RibbonClientConfiguration bereitgestellt.

Will man den RibbonClient über die defaults hinaus konfigurieren so stellt Spring Cloud Netflix folgende Beans bereit, die in der Konfiguration des RibbonClients angepasst werden können:

Tabelle 1: Konfigurations-Beans des RibbonClients und ihr Default
Funktion Bean/Interface Default–Wert
IClientConfig ribbonClientConfig DefaultClientConfigImpl
IRule ribbonRule ZoneAvoidanceRule
IPing ribbonPing NoOpPing
ServerList ribbonServerList ConfigurationBasedServerList
ServerListFilter ribbonServerListFilter ZonePreferenceServerListFilter
ILoadBalancer ribbonLoadBalancer ZoneAwareLoadBalancer

Deklarative REST Clients: Feign

Feign hilft uns Entwicklern dabei Clients für RESTful Web Services einfacher umzusetzen. Feign basiert hierbei auf annotierten Interfaces, wobei die Annotationen aus Feign-spezifischen und JAX-RS Annotationen basieren. Spring Cloud Netflix erweitert das Feature-Set von Feign um die bekannten Spring MVC Annotationen. Bei der Verwendung von Feign, welches mit Hilfe von @EnableFeignClients aktiviert wird, nutzt Spring Cloud Netflix automatisch Ribbon und Eureka um automatisch einen HTTP Client mit Load-Balancing bereitzustellen.

HTTP-Clients werden mit der Annotation @FeignClient versehen. Die Defaults von Feign können jederzeit mit Hilfe einer dedizierten Konfiguration, welche als Parameter and die @FeignClient Annotation übergeben werden kann, überschrieben werden. Dabei stellt Spring Cloud Netflix folgende Beans und Interfaces für Anpassungen bereit:

Tabelle 2: Beans ihre Interfaces für Feign
Funktion Bean/Interface Default
Decoder feignDecoder ResponseEntityDecoder
Ecoder feignEncoder Spring Encoder
Logger feignLogger Slf4jLogger
Contract feignContract SpringMvcContract
Feign.Builder feignBuilder HystrixFeign.Builder

Weiterhin werden folgende Beans nicht automatisch von Spring Cloud Netflix im ApplicationContext bereitgestellt. Allerdings werden Typen dieser Beans beim Hochfahren des FeignClients im ApplicationContext gesucht:

Gateway-Service: Zuul

Als Gateway- bzw. Edge-Service wird Zuul als Eintrittspunkt zu einer Microservice-Architektur genutzt, um den internen Aufbau (Service-Schnitt und Skalierung) zu verbergen und nach außen hin als geschlossenes System auftreten zu lassen. Diese Routing-Funktionalität von Zuul basiert auf einer Regelmaschine, die es erlaubt Regeln und Filter in jeder verfügbaren JVM-Sprache zu implementieren. Von Haus aus ist Unterstützung für Groovy und Java vorgesehen. Groovy-basierte Filter bieten die Besonderheit, dass sie zur Laufzeit hinzugefügt und geändert werden können, während in Java implementierte Filter leider ein Redeployment erfordern.

Durch intelligente Verteilung von eingehenden Requests, etwa nach geographischen Aspekten und im Round-Robin-Verfahren, und durch die Filter- und Routing-Regeln wird das server-seitige Load-Balancing erreicht und die Robustheit einzelner Services (siehe Hystrix) unterstützt. Aus architektonischer Sicht bietet es sich also an, diese Edge-Services ausreichend stark zu skalieren, was somit der Robustheit des Gesamtsystems zu Gute kommt.

Darüber hinaus werden dieser Software-Komponente weitere Aufgaben zuteil bzw. werden üblicherweise an dieser Stelle realisiert, u.a.:

Zuul Embedded Reverse Proxy

Spring Cloud liefert einen Embedded Reverse Proxy auf Basis von Zuul, der es ermöglicht, dass eine UI-Anwendungen Aufrufe zu mehreren nachgelagerten Diensten durch einen Proxy ausführt. Aktiviert wird dieser Proxy durch die Annotation @EnableZuulProxy in der Konfiguration der Anwendung. Dieser Proxy verwendet Ribbon um die entsprechende Instanz zu lokalisieren und führt sämtliche Ausrufe gleich via Hystrix aus. Verwendet man @EnableZuulProxy zusammen mit einem Spring Boot Actuator wird automatisch ein weiterer HTTP Endpoint namens /routes bereitgestellt. Mit Hilfe eines GET-Aufrufs kann man hierüber die abgebildeten Routen des Reverse Proxys aufrufen. Ein POST forciert hingegen einen Refresh der bestehenden Routen.

Eine weitere, populäre Herangehensweise in Microservice-Landschaften ist das Ausphasen alter Service-Endpoints, auch Service-Strangulation genannt. Hierbei wird eine neue Implementierung eines Dienstes parallel zur bestehenden, alten Implementierung produktiv gesetzt und Aufrufe zur neuen Implementierung werden erst peu á peu hochgefahren. So würden Fehler, die sich gegebenenfalls im neuen Deployment befinden nicht sofort global zuschlagen sondern können frühzeitig bei wenigen Nutzern entdeckt werden. Der Zuul Proxy ist hierfür gut geeignet, da er in seiner Konfiguration mehrere Routen vorsieht.

Zuul für Spring Cloud wird mit einigen Zuul Filtern geliefert, die durch @EnableZuulProxy automatisch aktiviert werden. Diese Filter können in der Konfiguration jederzeit durch zuul.<EinfacherKlassenName>.<filterTyp>.disable=true deaktiviert werden.

Support für Polyglotte Umgebungen mit Sidecar

Arbeitet man in Umgebungen, welche neben Java oder Groovy auch nicht-JVM Sprachen verwenden so kann man mit Hilfe von Spring Cloud Netflix Sidecar dennoch Dienste wie Eureka, Ribbon oder den Config Server verwenden. Dies ist für allem dann interessant wenn man Legacy Dienste einbinden will, die nicht auf Spring Cloud Netflix basieren und für die man nicht selbst den Registriereungsprozess bzw. den Heartbeat implementieren möchte. Sidecar ist basierend auf den Ideen Netflix Bibliothek Prana entwickelt worden. Sidecar wir in der Konfiguration der Spring Anwendung mit @EnableSidecar aktiviert. Dadurch wird eine einfache HTTP API aktiviert mit deren Hilfe sämtliche Instanzen bestimmter Services zugänglich gemacht werden. Diese Annotation aktiviert zugleich Hystrix (@EnableCircuitBreaker), einen Zuul Proxy (@EnableZuulProxy) und den Discovery Client von Eureka (@EnableDiscoveryClient).

Links & Literatur

  1. https://github.com/spring-cloud-samples/eureka/  ↩

  2. https://github.com/Netflix/eureka/wiki/Configuring-Eureka/  ↩

  3. https://github.com/Netflix/eureka/wiki/Eureka-REST-operations/  ↩

  4. https://github.com/spring-cloud-samples/customers-stores/  ↩

  5. https://github.com/Netflix/eureka/wiki/Eureka-2.0-Motivations/  ↩

  6. https://martinfowler.com/bliki/CircuitBreaker.html  ↩

  7. https://github.com/Netflix/Hystrix/wiki/Dashboard/  ↩

  8. https://github.com/spring-cloud-samples/hystrix-dashboard/  ↩

  9. https://github.com/Netflix/Turbine/  ↩

  10. http://cloud.spring.io/spring-cloud-netflix/  ↩