07.06.2016
Spring Boot - Eureka Server
In diesem Artikel wird beschrieben, wie ein Eureka-Server mit Spring Boot aufgesetzt wird und wie sich Spring Boot Client-Services bei dem Server mit @EnableEurekaClient
registrieren. Anschliessend wird beschrieben, wie sich die registrierten Services programmatisch finden und aufrufen lassen, dabei wird auch gezeigt, wie die Services über ihren Namen (spring.application.name) mittels eines Ribbon-RestTemplate
aufgerufen werden und wie eine clientseitige Lastverteilung (LoadBalancing) zwischen mehreren Instanzen eines Services realisiert wird indem man die Annotation @LoadBalanced
verwendet.
Übersicht
In diesem Artikel wird ein Eureka Server erstellt und ein Spring Boot DateService, der über eine REST-Schnittstelle einfach nur das aktuelle Datum zurückliefert.
Der DateService wird in dem Szenario zweimal gestartet, allerdings mit unterschiedlichen Ports.
Außerdem wird eine Spring Boot Applikation (EurekaDiscoveryClient
) erstellt, die mittels eines RestTemplate den date-Endpoint vom DateService aufruft. Die beiden Instanzen des DateService haben sich beim Eureka Server mit der Annotation @EnableEurekaClient
registriert. Das verwendete RestTesmplate wird durch die Annotation @LoadBalanced
dazu gebracht ein clientseitiges Loadbalancing mit Ribbon bei jedem Aufruf zu machen.
Eureka Server erstellen
Los geht es auf der Seite start.spring.io. Wo der Eureka Server ausgewählt wird, um dann das Projekt-Template zu erstellen.
Das Template wird anschliessend in einer IDE geöffnet. In der Klasse EurekaServerApplication muss die Annotation @EnableEurekaServer
ergänzt werden, damit die Anwendung als Eureka Server startet.
package org.hameister; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
In der Datei application.properties müssen dem Eureka Server mitgeteilt werden auf welchem Port er starten soll. Der Port 8761 ist der Standard-Port unter dem die Clients den Eureka Server suchen, wenn kein anderer Port für den Eureka Server im Client angegeben ist. Da Eureka redundant und ausfallsicher entworfen wurde, wird davon ausgegangen, dass immer zwei Instanzen des Eureka Server laufen. In dem Beispiel benutzen wir nur einen Server und schalten deshalb die gegenseitige Registrierung der Server über die Properties aus, damit der Server ohne Fehlermeldung startet.
spring.application=eureka-service server.port: 8761 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false
Spring Boot DateService erstellen
Im nächsten Stritt erstellen wir den DateService. Gestartet wird wieder bei start.spring.io. Dort wählt man Web, Actuator und Eureka Discovery aus und erstellt das Projekt-Template und öffnet es in einer IDE.
Anschließend wird in der Klasse DateServiceApplication
die Annotation EnableEurekaClient
ergänzt, die dafür sorgt, dass sich die Spring Boot Application bei dem Eureka Server beim Start registriert.
package org.hameister; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class DateServiceApplication { public static void main(String[] args) { SpringApplication.run(DateServiceApplication.class, args); } }
In der Datei application.properties
muss noch der Name des Service angegeben werden. Den Port übergeben wir beim Starten des Service.
spring.application.name=date-service
Der RestController mit der Rest-Schinttstelle sieht folgendermaßen aus:
package org.hameister; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.Date; /** * Created by hameister on 07.06.16. */ @RestController public class DateController { @Value("${instance.name}") private String instance; @RequestMapping(value = "/date", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity<String> getDate() { return new ResponseEntity<String>(instance+":"+new Date().toString(), HttpStatus.OK); } }
In der Response steht der Name der Instanz (instance.name
), der beim Starten übergeben wird. Außerdem wird das aktuelle Datum in die Response geschrieben.
Nachdem das mvn package -DskipTests
aufgerufen wurde, kann der Service auf der Kommandozeile gestartet werden.
Beim Start wird der Port und der instance.name
übergeben. So ist es möglich den gleichen Service unter verschiedenen Ports laufen zu lassen.
Start von Service 1
java -jar DateService-0.0.1-SNAPSHOT.jar --server.port=8001 --instance.name=Instanz1
Start von Service 2
java -jar DateService-0.0.1-SNAPSHOT.jar --server.port=8002 --instance.name=Instanz2
Wenn man vorher den Eureka Server gestartet hat, dann sollten unter der Url: localhost:8761 zwei Instanzen des DATE-SERVICE zu sehen sein:
DiscoveryClient und Lastverteilung
Im letzen Schritt wird ein Discovery-Client erstellt, der den DateService aufruft. Dazu wird einfach über start.spring.io ein Spring Boot Projekt erstellt. Es muss Eureka Discovery ausgewählt werden und abschließend mit Generate Project ein Maven-Projekt erstellt werden.
Das Projekt muss danach in einer IDE geöffnet und die Annotation @EnableEurekaClient
ergänzt werden.
package org.hameister; import org.apache.commons.lang.builder.ToStringBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableEurekaClient public class EurekaDiscoveryApplication { public static void main(String[] args) { SpringApplication.run(EurekaDiscoveryApplication.class, args); } } @Component class RestTemplateExample implements CommandLineRunner { @Autowired private RestTemplate restTemplate; @Override public void run(String... strings) throws Exception { for (int i=0; i<20;i++) { ResponseEntity<String> resp = this.restTemplate.getForEntity("http://date-service/date", String.class); System.out.println(resp.getBody()); } } }
Außerdem wird über eine @Component
und einen CommandLineRunner
über die run
-Methode eine Möglichkeit geschaffen das LoadBalancing auf der Kommandozeile auszuprobieren. Deshalb wird das restTemplate
hier beispielhaft 20 mal aufgerufen. Das besondere dabei ist, dass nur der logische Name date-service
und der Endpoint /date
verwendet wird und kein Hostname und Port benötigt wird.
Damit das funktioniert, muss noch dafür gesorgt werden, dass das @Autowired
mit dem Ribbon Template initialisiert wird. Dazu muss folgende Klasse angelegt werden:
package org.hameister; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * Created by hameister on 07.06.16. */ @Configuration public class LoadBalancedRestTemplate { @LoadBalanced @Bean RestTemplate restTemplate() { return new RestTemplate(); } }
Zu beachten ist dabei die Annotation @LoadBalanced
. Sie sorgt dafür, dass ein Ribbon-RestTemplate angelegt wird, das ein client-seitiges Loadbalancing ermöglicht.
Bis vor kurzem hat das @Autowired
per auto configuration funktioniert. Allerdings wurde das geändert:
Spring Doku zu der Änderung. Wenn man nach der Zeichenkette // use the "smart" Eureka-aware RestTemplate sucht, findet man noch eine Reihe von Beispielen, in denen die Annotation @LoadBalanced
noch nicht verwendet wurde.
Wenn man die Anwendung nun von der Kommandozeile aus startet sieht man sehr schön, dass beide Services aufgerufen werden.
Start der Applikation:
java -jar EurekaDiscovery-0.0.1-SNAPSHOT.jar
Konsolenausgabe:
... Instanz1:Tue Jun 07 20:21:44 CEST 2016 Instanz2:Tue Jun 07 20:21:44 CEST 2016 Instanz1:Tue Jun 07 20:21:44 CEST 2016 Instanz2:Tue Jun 07 20:21:44 CEST 2016 Instanz1:Tue Jun 07 20:21:44 CEST 2016 Instanz2:Tue Jun 07 20:21:44 CEST 2016 Instanz1:Tue Jun 07 20:21:44 CEST 2016 Instanz2:Tue Jun 07 20:21:45 CEST 2016 Instanz1:Tue Jun 07 20:21:45 CEST 2016 Instanz2:Tue Jun 07 20:21:45 CEST 2016 Instanz1:Tue Jun 07 20:21:45 CEST 2016 Instanz2:Tue Jun 07 20:21:45 CEST 2016 Instanz1:Tue Jun 07 20:21:45 CEST 2016 Instanz2:Tue Jun 07 20:21:45 CEST 2016 Instanz1:Tue Jun 07 20:21:45 CEST 2016 Instanz2:Tue Jun 07 20:21:45 CEST 2016 Instanz1:Tue Jun 07 20:21:45 CEST 2016 Instanz2:Tue Jun 07 20:21:45 CEST 2016 Instanz1:Tue Jun 07 20:21:45 CEST 2016 Instanz2:Tue Jun 07 20:21:45 CEST 2016 ....
In den folgenden Artikeln findet man noch eine genauere Beschreibung zum Eureka Server und Clients. Allerdings fehlt teilweise die automatische Konfiguration des RestTemplates.