Spring Boot - Microservicios de Descubrimiento, proxy y tolerancia a fallos (Eureka, Zuul, Ribbon y Histrix de Netflix)

- Agregamos un servidor de configuración totalmente independiente de todos los servicios a construir.

- Sabemos que los microservicios / microservices deben estar disponibles para comunicarse entre ellos y que debe ser por medio de algún servicio de descubrimiento como Eureka (de Netflix), ZooKeeper, etc. así que agreamos un servicio de descubrimiento.

- Trabajando con microservicios / microservices, tenemos tantas entradas (urls) como servicios tengamos programados. Si tenemos un módulo que entrega información de productos, usuarios, consumo, descripciones, etc. tendríamos al rededor de unos 60 microservicios (por poner un ejemplo) por lo tanto es necesario agregar un patrón conocido como Servicio Edge (que se subdivide en Micro-Proxy y API Gateway); para nuestra cápsula un micro-proxy bastará para tener un solo punto de entrada (single entry point) hacia los servicios que entregan la información mencionada.

Un MicroProxy concentra las llamadas, en un solo punto de entrada, y las distribuye de forma inteligente hacia los servicios en la parte de atrás; Por qué inteligente? porque agrega por defecto un balanceador de cargas que garantiza la disponibilidad balanceada entre los distintos servicios o entre la redundancia que de forma natural permite la arquitectura de microservicios.

Para nuestro ejemplo utilizaremos Zuul (de Netflix).

- Además para asegurar flujos de trabajo constantes y fluidos, tenemos que hacer nuestro sistema altamente tolerante a fallos. Para lo anterior agregamos Hystrix (también de Netflix).

 

 

- Prerequisitos

* Servidor de configuración, servidor de descubrimiento Eureka y Servicio que consulte productos.

* Servidor MicroProxy Zuul (a construir)

* Tolerancia a fallos con Hystrix (a construir)

 

Bonus, que es una librería para interpretar los resultados del servicio de consulta. HATEOAS.

 

ZUUL

1. Configuración MAVEN

<name>ledzedev-producto-cliente</name>
<description>Demo de Spring Boot de un Microproxy con Zuul y Tolerancia a fallos con Hystrix. Además del uso de la librería HATEOAS.</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.2.RELEASE</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

2. Configuración properties (local al servicio y el archivo por convención se debe llamar bootstrap.properties)

spring.application.name=producto-cliente
spring.cloud.config.uri=http://localhost:8888 # aquí es donde tenemos nuestro servidor de configuración

 

3. Configuración remota (producto-cliente.yml); en mi caso prefiero el formato YAML pero también puede ser properties.

Nota: El servidor Eureka sube por defecto en el puerto: 8761

server:
   port: 9999

eureka:
   client:
      serviceUrl:
         defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
   instance:
      preferIpAddress: true

 

3. Código Java

/**
* Código generado por Gerado Pucheta Figueroa
* Twitter: @ledzedev
* http://ledze.mx
* 01/Dic/2016.
*/
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class LedzedevProductoClienteApplication {

public static void main(String[] args) {
SpringApplication.run(LedzedevProductoClienteApplication.class, args);
}
}

 

4. Arrancamos nuestra aplicación y consultamos tal como lo definimos en el yaml en el navegador: http://localhost:9999/producto-service/productoes

y debemos ver el servicio pero por medio de nuestro single entry point y no directo a nuestro servidor:

 

 

 

Solo para comparar, este es el acceso del servidor que consulta los datos de los productos:

 

EUREKA

Es importante que la pantalla de monitoreo de Eureka se mantenga limpia, sin mensajes de error y debe mostrar los dos servicios registrados.

 

HYSTRIX

- Tolerancia a Fallos.

- Agregamos nuestro servicio RESTful que consulte únicamente los nombres de los productos, no necesitamos todo, así que lo agregamos e implementamos la tolerancia a fallos así como el método que nos permitirá consultar información de los productos con balanceo de cargas.

 

1. Configuración MAVEN (Solo hay que agregar la dependencia en nuestro mismo proyecto)

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

 

2. Código Java

Simplemente agregamos la anotación @EnableCircuitBreaker

@EnableCircuitBreaker
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class LedzedevProductoClienteApplication {
public static void main(String[] args) {
SpringApplication.run(LedzedevProductoClienteApplication.class, args);
}
}


Clase con el servicio RESTful que consulta información de los productos. Al método que consulta se le agrega la anotación @HystrixCommand y se le da la referencia del método que se ejecutará en caso de fallo.
/**
* Código generado por Gerado Pucheta Figueroa
* Twitter: @ledzedev
* http://ledze.mx
* 01/Dic/2016.
*/
@RestController
class ProductoApiGatewayRestController {

private static final Logger LOG = LoggerFactory.getLogger(ProductoApiGatewayRestController.class);

@Bean
@LoadBalanced
public RestTemplate restTemplate(){

return new RestTemplate();
};

@Autowired
RestTemplate restTemplate;


public Collection<String> getNombreProductosFallback(){
return Arrays.asList("Error al obtener los nombres de los productos en la tienda de la esquina.");
}

@HystrixCommand(fallbackMethod = "getNombreProductosFallback")
@RequestMapping(method = RequestMethod.GET, value = "/nomprod")
public Collection<String> getNombreProductos(){
LOG.info("Obteniendo nombre de productos");

ParameterizedTypeReference<Resources<Producto>> ptr =
new ParameterizedTypeReference<Resources<Producto>>() { };

ResponseEntity<Resources<Producto>> entity = this.restTemplate.exchange("http://producto-service/productoes", HttpMethod.GET, null, ptr);

return entity.getBody()
.getContent()
.stream()
.map(Producto::getNombreProducto)
.collect(Collectors.toList());
}
}

IMPORTANTE: Nótese que en el exchange, agregamos el id del servicio, no es un host.


Esta es solo una representación del objeto Producto original, podríamos agregar el jar que contiene el objeto original, pero eso sería ir en contra de nuestra arquitectura de microservicios.

/**
* Código generado por Gerado Pucheta Figueroa
* Twitter: @ledzedev
* http://ledze.mx
* 01/Dic/2016.
*/
class Producto {

private String nombreProducto;

public String getNombreProducto() {
return nombreProducto;
}
}

3. Consultamos en el navegador la dirección de nuestro cliente http://localhost:9999/nomprod.

 

 

4. Deliveradamente tiramos el servicio que consulta los productos y nuevamente consultamos nuestro cliente http://localhost:9999/nomprod.

 

 

CONCLUYENDO

Tenemos 2 servicios (que son totalmente independientes y que se comunican entre si) montados en una arquitectura de microservicios dividida de la siguiente manera:

1) Servidor de configuración

2) Servidor de descubrimiento (Eureka)

3) Servidor de consulta de productos.

4) Servidor Zuul (patrón Micro-Proxy) para enmascarar las llamadas en un solo punto de entrada.

5) Servicio REST que funciona como cliente y que consulta únicamente los nombres de los productos.

 

BONUS

6) Para poder obtener únicamente un aparte de la información se utiliza la librería de HATEOAS para poder utilizar una representación del objeto original.

 

 

Te puedes descargar mi código directamente del repositorio:

Ledzedev archivos de configuración -> github / formato zip

Ledzedev Servidor de configuración -> github / formato zip

Ledzedev Eureka Server -> github / formato zip

Ledzedev servicio RESTful de consulta de productos -> github / formato zip

Ledzedev Microservicios Zuul, Hystrix, RESTful cliente de consulta de nombres -> github / formato zip

 

 

Artículos Relacionados

- Spring Boot Microservicio de Mensajería en complemento con tolerancia a fallos.