Hoy vamos uno de los patrones para mejorar la resiliencia de los micro servicios. En este caso concreto, será el patrón Circuit Breaker. Y podremos verlo aplicado a un micro servicio con Apache Camel a través de Hystrix.
Como siempre empezaremos con un poco de teoría. Vamos a explicar que es el patrón Circuit Breaker y cual es su funcionalidad. Este patrón se basa en la idea de que algunas intercomunicaciones dentro del micro servicio pueden fallar. Pero al seguir funcionando la lógica normal, se pierde tiempo y recursos en hacer llamadas que posiblemente sean infructuosas. La idea del patrón es monitorizar el servicio externo al que vamos a invocar, para en caso de cualquier error, redirigir el flujo a otro servicio o lógica de negocio que nos haga mantener a nuestro micro servicio operativo y con baja latencia.
Este patrón basa su funcionamiento en el control de estados del propio circuito. Contando con tres estados diferentes:
- Cerrado: Circuito operativo, todo funciona correctamente.
- Abierto: Circuito defectuoso. Se redirige el flujo hacia otro lado, permitiendo la perdida de recursos y seguir ofreciendo servicio.
- Semi-abierto: Estado intermedio que se encargará de comprobar si el servicio externo se encuentra o no disponible y si mantiene el circuito abierto o lo cierra.
- Hystrix: Librería de Netflix sobre la que basaremos el ejemplo. Actualmente (05/2021) ya no se encuentra en desarrollo y si en estado de mantenimiento.
- Resilience4j: Librería basada en Hystrix y orientada a la implementación de varios patrones para la implementación de la resiliencia.
- MicroProfile Fault Tolerance: Librería de la fundación Eclipse para la implementación de patrones de resiliencia.
<dependency> <groupId>org.apache.camel.springboot</groupId> <artifactId>camel-hystrix-starter</artifactId> </dependency>
- circuitBreakerErrorThresholdPercentage: Porcentaje de error a partir del cual considera el circuito abierto y no acepta invocaciones.
- circuitBreakerSleepWindowInMilliseconds: Tiempo a pasar antes de volver a aceptar peticiones.
- metricsRollingStatisticalWindowInMilliseconds: Tiempo de duracion de las métricas asociadas.
- circuitBreakerRequestVolumeThreshold: Número mínimo de request a existir durante la duración de las métricas. Si no se alcanza este número mínimo de invocaciones no se considerará el circuito abierto, independientemente del porcentaje de error.
@Component public class BookHystrixRouter extends RouteBuilder { @Override public void configure() throws Exception { restConfiguration().component("servlet").bindingMode(RestBindingMode.json_xml); rest().get("hystrix/sql").produces(MediaType.APPLICATION_JSON_VALUE)
.route().circuitBreaker().id("hystrix/sql") .to("sql:{{sql.selectAll}}").onFallback()
.setBody(bodyAs(ArrayList.class)).endCircuitBreaker() .log("--- SQL select all books---"); } }
curl --location --request GET 'http://localhost:9090/hystrix/sql'
Una vez arrancada la aplicación, podemos acceder al Portainer, a través de la URL http://localhost:9000, y parar el contenedor de MySQL. En ese momento podremos volver a invocar a la aplicación y ver como las llamadas devuelven un conjunto vacío de elementos.
El método onFallback nos permite responder un mensaje de forma inmediata, pero si lo que queremos hacer es reenviar la llamada a otro servicio externo para ello deberemos utilizar el método onFallbackViaNetwork.
En el siguiente ejemplo que invoca a un Wiremock podremos comprobar esta segunda invocación. Además también nos permitirá visualizar a través de la consola del Wiremock si cuando el circuito se encuentra abierto, se sigue invocando o no al primer servicio. Aspecto que nos era más difícil de verificar con el contenedor de MySQL.
Por ello crearemos un mapping para Wiremock que responda al path /book con la misma respuesta que devuelve la BBDD. Y nuestro ejemplo intentará invocar al path /fail, que al no existir siempre fallará y posteriormente invocará a /book.
rest().get("hystrix/fail").produces(MediaType.APPLICATION_JSON_VALUE).route()
.circuitBreaker().id("hystrix/fail")
.toD("http://localhost:57001/fail?bridgeEndpoint=true").onFallbackViaNetwork()
.toD("http://localhost:57001/book?bridgeEndpoint=true").end().unmarshal().json() .log("--- Wiremock select all books---");
Si invocamos al servicio y a la misma vez consultamos la consola del Wiremock, podremos ver como primero llama a un servicio y luego al otro. Devolviéndonos nuestro microservicio un listado de libros, como hacía el método anterior.
curl --location --request GET 'http://localhost:9090/hystrix/fail'
Y en base a la configuración anteriormente indicada, si realizamos un conjunto elevado de llamadas. Podremos visualizar en la consola de Wiremock, como cuando el circuito se encuentra abierto, no se realizan llamadas al primer servicio.
Por último indicar, que en versiones anteriores de Spring existía la dependencia spring-cloud-starter-hystrix-dashboard que generaba un dashboard para la visualización de las métricas de Hystrix. Pero la cual no es compatible con las versiones actuales de Spring y utilizadas en este ejemplo. En vez de usar esa librería, utilizaremos una imagen de docker llamada mlabouardy/hystrix-dashboard que nos permitirá acceder a dicho dashboard.
Para ello debemos arrancar la imagen en el puerto 8080 y acceder a la URL: http://localhost:8080/hystrix. El siguiente paso será indicar la URL donde Hystrix esta mostrando las métricas. Hay que tener en cuenta, que si la aplicación de Apache Camel no esta siendo lanzada a través de una imagen en la misma red que la del dashboard deberemos indicar la URL de la siguiente forma en sistemas Windows o Mac: http://host.docker.internal:9090/hystrix.stream. A continuación un ejemplo de como se pueden visualizar dichas métricas con la ayuda de esta imagen:
Esto es todo, amigos. Ya hemos aprendido un poco más sobre resiliencia orientado a micro servicios y como hacerlo funcionar fácilmente en nuestra aplicación de Apache Camel. Si quieres acceso al código, lo tienes aquí.
No hay comentarios:
Publicar un comentario