Introducción a Spring Cloud Netflix - Eureka

1. Información general

En este tutorial, presentaremos el descubrimiento de servicios del lado del cliente a través de " Spring Cloud Netflix Eureka ".

El descubrimiento de servicios del lado del cliente permite que los servicios se encuentren y se comuniquen entre sí sin codificar el nombre de host y el puerto. El único "punto fijo" en dicha arquitectura consiste en un registro de servicios en el que cada servicio debe registrarse.

Un inconveniente es que todos los clientes deben implementar una cierta lógica para interactuar con este punto fijo. Esto supone un viaje de ida y vuelta de red adicional antes de la solicitud real.

Con Netflix Eureka, cada cliente puede actuar simultáneamente como servidor, para replicar su estado a un par conectado. En otras palabras, un cliente recupera una lista de todos los pares conectados de un registro de servicios y realiza todas las solicitudes posteriores a cualquier otro servicio mediante un algoritmo de equilibrio de carga.

Para estar informado sobre la presencia de un cliente, debe enviar una señal de latido al registro.

Para lograr el objetivo de este artículo, implementaremos tres microservicios :

  • un registro de servicios ( Eureka Server ),
  • un servicio REST que se da de alta en el registro ( Cliente Eureka ) y
  • una aplicación web, que consume el servicio REST como cliente de registro ( Spring Cloud Netflix Feign Client ).

2. Servidor Eureka

Implementar un servidor Eureka para el registro de servicios es tan fácil como:

  1. agregando spring-cloud-starter-netflix-eureka-server a las dependencias
  2. habilite el servidor Eureka en una @SpringBootApplication anotándolo con @EnableEurekaServer
  3. configurar algunas propiedades

Pero lo haremos paso a paso.

En primer lugar, crearemos un nuevo proyecto de Maven y pondremos las dependencias en él. Debes notar que estamos importando spring-cloud-starter-parent a todos los proyectos descritos en este tutorial:

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

Nota: podemos consultar las últimas versiones de Spring Cloud en la documentación de Proyectos de Spring.

A continuación, estamos creando la clase de aplicación principal:

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

Finalmente, estamos configurando las propiedades en formato YAML ; por lo que un application.yml será nuestro archivo de configuración:

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

Aquí estamos configurando un puerto de aplicación: 8761 es el predeterminado para los servidores Eureka . Le estamos diciendo al Cliente Eureka integrado que no se registre con "sí mismo" porque nuestra aplicación debería actuar como un servidor.

Ahora apuntaremos nuestro navegador a // localhost: 8761 para ver el panel de Eureka , donde luego inspeccionaremos las instancias registradas.

Por el momento, vemos indicadores básicos como el estado y los indicadores de salud.

3. Cliente de Eureka

Para que una @SpringBootApplication sea ​​consciente de los descubrimientos, debemos incluir algo de Spring Discovery Client (por ejemplo, spring-cloud-starter-netflix-eureka-client ) en nuestra ruta de clases.

Luego, necesitamos anotar una @Configuration con @EnableDiscoveryClient o @EnableEurekaClient; tenga en cuenta que esta anotación es opcional si tenemos la dependencia spring-cloud-starter-netflix-eureka-client en el classpath.

Este último le dice a Spring Boot que use Spring Netflix Eureka para el descubrimiento de servicios explícitamente. Para llenar nuestra aplicación cliente con algo de vida de muestra, también incluiremos el paquete spring-boot-starter-web en el pom.xml e implementaremos un controlador REST .

Pero primero, agregaremos las dependencias. Nuevamente, podemos dejar que la dependencia spring-cloud-starter-parent descubra las versiones del artefacto por nosotros:

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

Aquí implementaremos la clase de aplicación principal:

@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()); } }

Y la interfaz GreetingController :

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

Aquí, en lugar de la interfaz, también podríamos simplemente declarar el mapeo dentro de la clase EurekaClientApplication . La interfaz puede ser útil si queremos compartirla entre servidor y cliente.

A continuación, tenemos que configurar un application.yml con un nombre de aplicación Spring configurado para identificar de forma única a nuestro cliente en la lista de aplicaciones registradas.

Podemos dejar que Spring Boot elija un puerto aleatorio por nosotros porque luego estaremos accediendo a este servicio con su nombre.

Finalmente, tenemos que decirle a nuestro cliente, donde tiene que ubicar el registro:

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

Cuando decidimos configurar nuestro Cliente Eureka de esta manera, teníamos en mente que este tipo de servicio debería ser fácilmente escalable más adelante.

Ahora ejecutaremos el cliente y apuntaremos nuestro navegador a // localhost: 8761 nuevamente, para ver su estado de registro en el Panel de Eureka. Al usar el Tablero, podemos realizar más configuraciones, por ejemplo, vincular la página de inicio de un cliente registrado con el Tablero para fines administrativos. Sin embargo, las opciones de configuración están fuera del alcance de este artículo.

4. Cliente fingido

Para finalizar nuestro proyecto con tres microservicios dependientes, ahora implementaremos una aplicación web que consuma REST usando 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.

Finalmente, tenemos un panorama general del uso del descubrimiento de servicios en una arquitectura de microservicios.

Como de costumbre, encontrará las fuentes en GitHub, que también incluye un conjunto de archivos relacionados con Docker para usar con docker-compose para crear contenedores a partir de nuestro proyecto.