Spring Cloud: descubrimiento de servicios con Eureka

En este artículo, conoceremos el descubrimiento de servicios del lado del cliente y el equilibrio de carga a través de Spring Cloud Netflix Eureka. En un típico arquitecto de microservicios...

Visión general

En este artículo, conoceremos el descubrimiento de servicios del lado del cliente y el equilibrio de carga a través de [Nube de primavera Netflix Eureka] (https://spring.io/projects/spring-cloud-netflix).

En una arquitectura típica de microservicios, tenemos muchas aplicaciones pequeñas implementadas por separado y, a menudo, necesitan comunicarse entre sí. Específicamente, cuando decimos servicio de cliente, nos referimos a un servicio que necesita realizar llamadas REST a algún otro servicio final.

El problema en este tipo de arquitectura es cómo el servicio de atención al cliente encuentra todos sus servicios finales. Podríamos codificar el nombre de host/puerto en algún archivo de propiedades, pero esto no siempre es práctico o factible en un entorno de nube. Puede haber cualquier cantidad de microservicios, y codificar de forma rígida requiere mucho tiempo y recursos cuando hay una cantidad incierta de ellos y cuando sus ubicaciones pueden cambiar.

Para aumentar aún más la complejidad, los servicios podrían tener varias instancias de sí mismos (según la carga). Qué instancia servirá realmente para la respuesta podría ser un desafío, ya que queremos tener una distribución de carga equitativa.

Netflix Eureka

Netflix Eureka es un servidor de búsqueda (también llamado registro). Todos los microservicios del clúster se registran en este servidor.

Al realizar una llamada REST a otro servicio, en lugar de proporcionar un nombre de host y un puerto, solo proporcionan el nombre del servicio.

El enrutamiento real se realiza en tiempo de ejecución junto con la distribución equitativa de la carga entre los servicios finales. Hay otros clientes de descubrimiento de servicios como Cónsul, cuidador del zoológico etc, pero usaremos Eureka en este artículo.

Para entender este concepto construiremos tres servicios en nuestro ejemplo:

  • Eureka Server: actúa como un registro de servicios.
  • Movie Service: un servicio REST simple que proporciona información sobre películas.
  • Servicio de recomendación: un servicio REST simple pero llama internamente al Servicio de películas para completar sus solicitudes.

Configuración del servidor Eureka

La mejor manera de comenzar con un proyecto básico es usar Spring Initializr. Seleccione su versión preferida de Spring Boot y agregue la dependencia "Eureka Server" y genere como un proyecto Maven:

spring-eureka-spring-initializer

Para hacer un servidor Eureka, todo lo que necesitamos hacer es agregar la anotación @EnableEurekaServer a nuestra clase principal:

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

Ejecutaremos el servidor en el puerto 8761, que es el puerto recomendado por el equipo de Spring. Así que en application.properties agregaremos:

1
server.port = 8761

Para probar este punto final, navegue su navegador a http://localhost:8761/:

spring-eureka-server-display

Configuración del servicio final (servicio de películas)

Nuevamente, estamos usando Spring Initializr para crear nuestro proyecto. Seleccione su versión preferida de Spring Boot y agregue las dependencias "Web" y "Eureka Discovery" y genere como un proyecto Maven:

spring-eureka-movie-service-init

Para convertir esto en un cliente, todo lo que tenemos que hacer es agregar la anotación @EnableEurekaClient en el nivel de clase:

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaClient
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Como alternativa, podríamos usar la anotación @EnableDiscoveryClient, que proviene de spring-cloud-commons. Selecciona la implementación (Consul, Zookeeper, Eureka) según el classpath. En nuestro caso, elegiría automáticamente Eureka.

Con eso fuera del camino, podemos definir nuestro modelo Película:

1
2
3
4
5
6
7
8
9
public class Movie {
    private Integer id;

    private String name;

    private String synopsis;

    // getters and setters
}

Y finalmente, podemos definir un controlador:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@RestController
public class MovieController {

    private static List<Movie> movieList = new ArrayList<>();
    static {
        movieList.add(new Movie(1, "movie-1", "summary-1"));
        movieList.add(new Movie(2, "movie-2", "summary-2"));
        movieList.add(new Movie(3, "movie-3", "summary-3"));
    }

    @GetMapping("/movies")
    public ResponseEntity<?> getMovies() {
        return ResponseEntity.ok(movieList);
    }
}

Arriba, creamos un punto final simple que devuelve una lista de objetos Película, simulando una llamada a una base de datos.

Tenemos el jar de cliente Eureka Discovery en este proyecto. Al ver esto en el classpath, Spring intentará encontrar un servidor Spring Eureka en ejecución. Necesitamos especificar esto en application.properties:

1
2
3
server.port = 8060
spring.application.name = movie-service
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/

Además de especificar la URL del servidor, también debemos especificar el nombre de la aplicación. Será este nombre el que utilicen otros servicios para realizar llamadas REST.

Con eso, hemos registrado nuestro servicio en el servidor, y cualquier otro servicio registrado puede llamarlo usando spring.application.name.

Actualicemos el punto final del servidor Eureka:

spring-eureka-movie-service-registers

Junto con el registro en el servidor de Eureka, el jar del cliente de Eureka también envía latidos periódicos al servidor de Eureka para informarle que aún está disponible.

Configuración del servicio de cliente (Servicio de recomendación)

Ahora construyamos nuestro servicio de cliente que se llamará movie-service. Una vez más, creemos un nuevo proyecto con Spring Initializr con las mismas dependencias que antes:

spring-eureka-recomendacion-servicio-init

En su application.properties nuevamente tenemos que especificar el nombre de la aplicación y los detalles de Eureka:

1
2
3
server.port = 8050
spring.application.name = recommendation-service
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/

Luego anotamos la clase principal con @EnableEurekaClient:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@SpringBootApplication
@EnableEurekaClient
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

También necesitamos crear un bean RestTemplate y marcarlo como @LoadBalanced. Esto le dice a Spring que queremos aprovechar el balanceo de carga del lado del cliente, que en este caso lo hace Cinta.

El equilibrio de carga del lado del cliente decide qué instancia (en caso de que se ejecuten múltiples servicios finales en el clúster que el cliente puede llamar) llamar.

Ribbon fue desarrollado por Netflix y luego de código abierto. Su dependencia viene automáticamente con la dependencia de Eureka Discovery. Se integra automáticamente con Spring y distribuye las cargas en función del estado del servidor, el rendimiento, la región, etc.

No se nos pedirá que usemos Ribbon directamente, ya que integra automáticamente RestTemplate, Zuul, [Fingir](https://github.com/ OpenFeign/feign), etc. Usando @LoadBalanced hicimos consciente la cinta RestTemplate.

Escribamos la clase RecommendationController que llama internamente a nuestro servicio de películas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@RestController
public class RecommendationController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping(value = "/recommendations", method = RequestMethod.GET)
    @ResponseBody
    public Movie[] recommendations() {
        Movie[] result = restTemplate.getForObject("http://movie-service/movies", Movie[].class);
        return result;
    }
}

Arriba, @Autowired el RestTemplate habilitado para Ribbon y lo usamos para llamar al movie-service. Tenga en cuenta que no tenemos que especificar el nombre de host o el puerto en ninguna parte.

Lo que hace Spring internamente aquí es porque está registrado con el servidor Eureka, almacena la lista de todos los servicios y sus instancias en ejecución localmente. Cuando realizamos una llamada REST al movie-service de esta manera (en lugar de proporcionar un nombre de host y un puerto), sustituye las URL de punto final reales de la lista previamente almacenada y luego realiza la llamada REST.

Por supuesto, la lista almacenada de servicios junto con sus instancias en ejecución se actualiza periódicamente. La mejor parte de todo esto es que no tenemos que ocuparnos de esto en absoluto y Spring maneja todas estas cosas internamente.

Probemos el punto final de recomendación, navegue con su navegador (o use curl, postman, etc.) hasta http://localhost:8050/recomendaciones, verá un respuesta que se ve algo como:

resultado-servicio-recomendacion-spring-eureka

Conclusión

En este artículo, hemos cubierto cómo usar Spring Cloud Eureka para el descubrimiento de servicios en el entorno de microservicio/nube. Creamos dos servicios REST simples que se comunican entre sí sin codificar ningún nombre de host/puerto mientras se realizan llamadas REST.

Como siempre, el código de los ejemplos utilizados en este artículo se puede encontrar en Github.