Introduzione a Spring Cloud Netflix - Eureka

1. Panoramica

In questo tutorial, introdurremo la scoperta del servizio lato client tramite " Spring Cloud Netflix Eureka ".

Il rilevamento dei servizi lato client consente ai servizi di trovarsi e comunicare tra loro senza il nome host e la porta hardcoded. L'unico "punto fermo" in una tale architettura è costituito da un registro dei servizi con cui ogni servizio deve registrarsi.

Uno svantaggio è che tutti i client devono implementare una certa logica per interagire con questo punto fisso. Ciò presuppone un round trip di rete aggiuntivo prima della richiesta effettiva.

Con Netflix Eureka ogni client può agire simultaneamente come un server, per replicare il proprio stato su un peer connesso. In altre parole, un client recupera un elenco di tutti i peer connessi di un registro di servizio ed effettua tutte le ulteriori richieste a qualsiasi altro servizio tramite un algoritmo di bilanciamento del carico.

Per essere informati sulla presenza di un client, devono inviare un segnale di heartbeat al registro.

Per raggiungere l'obiettivo di questo articolo, implementeremo tre microservizi :

  • un registro dei servizi ( Eureka Server ),
  • un servizio REST che si registra nel registro ( Eureka Client ) e
  • un'applicazione web, che utilizza il servizio REST come client che riconosce il registro ( Spring Cloud Netflix Feign Client ).

2. Eureka Server

L'implementazione di un server Eureka per il registro dei servizi è facile come:

  1. aggiungendo spring-cloud-starter-netflix-eureka-server alle dipendenze
  2. abilitare il server Eureka in una @SpringBootApplication annotandolo con @EnableEurekaServer
  3. configurare alcune proprietà

Ma lo faremo passo dopo passo.

Per prima cosa creeremo un nuovo progetto Maven e inseriremo le dipendenze in esso. Devi notare che stiamo importando lo spring-cloud-starter-parent in tutti i progetti descritti in questo tutorial:

 org.springframework.cloud spring-cloud-starter-netflix-eureka-server     org.springframework.cloud spring-cloud-starter-parent Greenwich.RELEASE pom import   

Nota: possiamo controllare le ultime versioni di Spring Cloud nella documentazione dei progetti di Spring.

Successivamente, stiamo creando la classe dell'applicazione principale:

@SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }

Infine, stiamo configurando le proprietà in formato YAML ; quindi un application.yml sarà il nostro file di configurazione:

server: port: 8761 eureka: client: registerWithEureka: false fetchRegistry: false

Qui stiamo configurando una porta dell'applicazione: 8761 è quella predefinita per i server Eureka . Stiamo dicendo al client Eureka integrato di non registrarsi con "se stesso" perché la nostra applicazione dovrebbe agire come un server.

Ora indirizzeremo il nostro browser a // localhost: 8761 per visualizzare il dashboard di Eureka , dove in seguito ispezioneremo le istanze registrate.

Al momento, vediamo indicatori di base come gli indicatori di stato e di salute.

3. Eureka Client

Affinché una @SpringBootApplication sia consapevole della scoperta, dobbiamo includere alcuni Spring Discovery Client (ad esempio spring-cloud-starter-netflix-eureka-client ) nel nostro classpath.

Quindi dobbiamo annotare una @Configuration con @EnableDiscoveryClient o @EnableEurekaClient: nota che questa annotazione è facoltativa se abbiamo la dipendenza spring-cloud-starter-netflix-eureka-client sul classpath.

Quest'ultimo dice a Spring Boot di utilizzare Spring Netflix Eureka per la scoperta del servizio in modo esplicito. Per riempire la nostra applicazione client con alcuni esempi di vita, includeremo anche il pacchetto spring-boot-starter-web nel pom.xml e implementeremo un controller REST .

Ma prima aggiungeremo le dipendenze. Di nuovo, possiamo lasciare che sia la dipendenza spring-cloud-starter-genitore a capire le versioni degli artefatti per noi:

 org.springframework.cloud spring-cloud-starter-netflix-eureka-starter   org.springframework.boot spring-boot-starter-web 

Qui implementeremo la classe dell'applicazione principale:

@SpringBootApplication @RestController public class EurekaClientApplication implements GreetingController { @Autowired @Lazy private EurekaClient eurekaClient; @Value("${spring.application.name}") private String appName; public static void main(String[] args) { SpringApplication.run(EurekaClientApplication.class, args); } @Override public String greeting() { return String.format( "Hello from '%s'!", eurekaClient.getApplication(appName).getName()); } }

E l' interfaccia di GreetingController :

public interface GreetingController { @RequestMapping("/greeting") String greeting(); }

Qui, invece dell'interfaccia, potremmo anche semplicemente dichiarare la mappatura all'interno della classe EurekaClientApplication . L'interfaccia può essere utile se vogliamo condividerla tra server e client.

Successivamente, dobbiamo impostare un application.yml con un nome di applicazione Spring configurato per identificare in modo univoco il nostro client nell'elenco delle applicazioni registrate.

Possiamo lasciare che Spring Boot scelga una porta casuale per noi perché in seguito accediamo a questo servizio con il suo nome.

Infine, dobbiamo dire al nostro cliente, dove deve trovare il registro:

spring: application: name: spring-cloud-eureka-client server: port: 0 eureka: client: serviceUrl: defaultZone: ${EUREKA_URI://localhost:8761/eureka} instance: preferIpAddress: true

Quando abbiamo deciso di impostare il nostro client Eureka in questo modo, avevamo in mente che questo tipo di servizio sarebbe stato successivamente facilmente scalabile.

Ora eseguiremo il client e indirizzeremo nuovamente il nostro browser a // localhost: 8761 , per vedere il suo stato di registrazione su Eureka Dashboard. Utilizzando il dashboard, possiamo eseguire ulteriori configurazioni, ad esempio collegare la home page di un cliente registrato con il dashboard per scopi amministrativi. Le opzioni di configurazione, tuttavia, esulano dallo scopo di questo articolo.

4. Finto cliente

Per finalizzare il nostro progetto con tre microservizi dipendenti, implementeremo ora un'applicazione web che consuma REST utilizzando Spring Netflix Feign Client .

Think of Feign as discovery-aware SpringRestTemplate using interfaces to communicate with endpoints. This interfaces will be automatically implemented at runtime and instead of service-urls, it is using service-names.

Without Feign we would have to autowire an instance of EurekaClient into our controller with which we could receive a service-information by service-name as an Application object.

We would use this Application to get a list of all instances of this service, pick a suitable one and use this InstanceInfo to get hostname and port. With this, we could do a standard request with any http client.

For example:

@Autowired private EurekaClient eurekaClient; public void doRequest() { Application application = eurekaClient.getApplication("spring-cloud-eureka-client"); InstanceInfo instanceInfo = application.getInstances().get(0); String hostname = instanceInfo.getHostName(); int port = instanceInfo.getPort(); // ... }

A RestTemplate can also be used to access Eureka client-services by name, but this topic is beyond this write-up.

To set up our Feign Client project, we're going to add the following four dependencies to its pom.xml:

 org.springframework.cloud spring-cloud-starter-feign   org.springframework.cloud spring-cloud-starter-netflix-eureka-client   org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-thymeleaf 

The Feign Client is located in the spring-cloud-starter-feign package. To enable it, we have to annotate a @Configuration with @EnableFeignClients. To use it, we simply annotate an interface with @FeignClient(“service-name”) and auto-wire it into a controller.

A good method to create such FeignClients is to create interfaces with @RequestMapping annotated methods and put them into a separate module. This way they can be shared between server and client. On server-side, you can implement them as @Controller, and on client-side, they can be extended and annotated as @FeignClient.

Furthermore, the spring-cloud-starter-eureka package needs to be included in the project and enabled by annotating the main application class with @EnableEurekaClient.

The spring-boot-starter-web and spring-boot-starter-thymeleaf dependencies are used to present a view, containing fetched data from our REST service.

This will be our Feign Client interface:

@FeignClient("spring-cloud-eureka-client") public interface GreetingClient { @RequestMapping("/greeting") String greeting(); }

Here we will implement the main application class which simultaneously acts as a controller:

@SpringBootApplication @EnableFeignClients @Controller public class FeignClientApplication { @Autowired private GreetingClient greetingClient; public static void main(String[] args) { SpringApplication.run(FeignClientApplication.class, args); } @RequestMapping("/get-greeting") public String greeting(Model model) { model.addAttribute("greeting", greetingClient.greeting()); return "greeting-view"; } } 

This will be the HTML template for our view:

   Greeting Page   

At least the application.yml configuration file is almost the same as in the previous step:

spring: application: name: spring-cloud-eureka-feign-client server: port: 8080 eureka: client: serviceUrl: defaultZone: ${EUREKA_URI://localhost:8761/eureka}

Now we can build and run this service. Finally, we'll point our browser to //localhost:8080/get-greeting and it should display something like the following:

Hello from SPRING-CLOUD-EUREKA-CLIENT!

5. ‘TransportException: Cannot Execute Request on Any Known Server'

While running Eureka server we often run into exceptions like:

com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server

Basically, this happens due to the wrong configuration in application.properties or application.yml. Eureka provides two properties for the client that can be configurable.

  • registerWithEureka: If we make this property as true then while the server starts the inbuilt client will try to register itself with the Eureka server.
  • fetchRegistry: The inbuilt client will try to fetch the Eureka registry if we configure this property as true.

Now when we start up the Eureka server, we don't want to register the inbuilt client to configure itself with the server.

If we mark the above properties as true (or don't configure them as they're true by default) while starting the server, the inbuilt client tries to register itself with the Eureka server and also tries to fetch registry which is not yet available. As a result, we get TransportException.

So we should never configure these properties as true in the Eureka server applications. The correct settings that should be put in application.yml are given below:

eureka: client: registerWithEureka: false fetchRegistry: false

6. Conclusion

As we've seen, we're now able to implement a service registry using Spring Netflix Eureka Server and register some Eureka Clients with it.

Because our Eureka Client from step 3 listens on a randomly chosen port, it doesn't know its location without the information from the registry. With a Feign Client and our registry, we can locate and consume the REST service, even when the location changes.

Infine, abbiamo un quadro generale dell'utilizzo del rilevamento dei servizi in un'architettura di microservizi.

Come al solito, troverai i sorgenti su GitHub, che include anche un set di file correlati a Docker da utilizzare con docker-compose per creare contenitori dal nostro progetto.