1. Introducción
Bienvenidos al siguiente post de Spring Cloud. Veremos a continuación Hystrix
!, que es básicamente la implementación del patrón Cirtuit Breaker que ha adoptado la gente de Netflix OSS.
Cada vez es más común consumir servicios externos a nuestras aplicaciones ya sea haciendo uso de servicios web, rest o los antiguos rmi o llamada a procedimientos remotos. La mayoría de las ocasiones dichos servicios estarán operativos por lo que la interconexión entre nuestro sistema y el externo será satisfactoria. En este momento decimos que el circuito está cerrado. Sin embargo hay ocasiones en que esto no es así y la conexión falla, por lo que decimos que el circuito está abierto.
Lo que nos interesa sobre este patrón son dos cosas.
- Dar una respuesta fácil, rápida y transparente cuando el circuito está abierto de tal forma que el sistema pueda recuperarse
- Tener la capacidad de monitorizar todos los circuitos de nuestra aplicación
Para encontrar más información visitar el post del gran Martin Flower sobre este patrón.
Os podéis descargar el código de ejemplo de mi GitHub aquí.
- Java 8
- Gradle 3.1
- SpringBoot 1.5.2.RELEASE
- Spring 4.3.7.RELEASE
- SpringCloud 1.1.5.RELEASE
2. Eureka
Necesitamos Eureka para realizar el registro y descubrimiento de nuestros microservicios. Para más información visitar el post.
package com.jorgehernandezramirez.spring.springcloud.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class Application { public Application(){ //For Spring } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
3. Microservicio de ejemplo Backend
Creamos un microservicio de ejemplo sobre el que implementar el patrón Cirtuit Breaker. Debemos:
- Incluir dependencia
spring-cloud-starter-hystrix
yspring-boot-starter-actuator
- Utilizar la anotación
@EnableCircuitBreaker
en el fichero Application.java - Utilizar la anotación
@HystrixCommand
en aquellos métodos que llamen a servicios externos y sean susceptibles de fallar.
Incluirmos las dependencias spring-cloud-starter-hystrix
y spring-boot-starter-actuator
. Además incluimos spring-cloud-starter-eureka
para registrarnos en Eureka.
group 'com.jorgehernandezramirez.spring.springboot' version '1.0-SNAPSHOT' buildscript { repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE") } } apply plugin: 'java' apply plugin: 'idea' apply plugin: 'maven' apply plugin: 'spring-boot' dependencyManagement { imports { mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR2' } } sourceCompatibility = 1.8 springBoot { mainClass = "com.jorgehernandezramirez.spring.springcloud.backend.Application" } repositories { mavenCentral() } dependencies { compile('org.springframework.cloud:spring-cloud-starter-eureka') compile('org.springframework.cloud:spring-cloud-starter-hystrix') compile('org.springframework.boot:spring-boot-starter-actuator') }
Hacemos uso de la anotación @EnableCircuitBreaker
package com.jorgehernandezramirez.spring.springcloud.backend; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableCircuitBreaker @EnableDiscoveryClient @SpringBootApplication public class Application { public Application(){ //For Spring } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Creamos un controlador de ejemplo en donde simulamos que el circuito siempre está abierto. Mediante el uso de la anotación @HystrixCommand
indicamos cual es el método fallback que se debe ejecutar en caso de que dicho circuito esté abierto. PD: Un circuito está abierto cuando ocurre una excepción no controlada
package com.jorgehernandezramirez.spring.springcloud.backend.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.sun.jersey.api.client.ClientHandlerException; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/token") public class TokenController { public TokenController(){ //For Spring } @HystrixCommand(fallbackMethod = "getTokenHystrixFallbackMethod") @RequestMapping(method = RequestMethod.GET) public String getToken() { //Here we throw a ClientHandlerException simulating error of communication throw new ClientHandlerException(); } private String getTokenHystrixFallbackMethod() { return "El servicio se encuentra no disponible. Inténtelo más tarde."; } }
Circuito abierto
Si ejecutamos
Obtenemos la respuesta del fallback
El servicio se encuentra no disponible. Inténtelo más tarde.
Monitorización
Para monitorizar el estado de los circuitos de nuestro microservicio
Obtendremos algo como
data: {“type”:”HystrixCommand”,”name”:”getToken”,”group”:”TokenController”,”currentTime”:1490003090680
,”isCircuitBreakerOpen”:true…
4. Turbine
Como ya sabemos, en un entorno cloud, podemos tener múltiples instancias de un microservicio sin saber a pripori en qué máquinas ni puertos se encuentran alojadas. Por este motivo se hace necesario un mecanismo que nos indique el estado de los circuitos de todas las instancia de un microservicio y no solamente de uno en concreto como vimos en el punto anterior. Es aquí cuando aparece Turbine
!
Básicamente lo que hace es preguntarle a Eureka
en donde se encuentra las instancias de nuestro microservicio, para ir llamando al controlador /hystrix.stream de cada uno de ellos, con el fin mostrarnos la información unificada.
Para ver el estado de todas las instancias de nuestro microservicio basta con llamar a (arrancamos turbine en el puerto 8081)
Para utilizar turbine necesitaremos
- Incluir dependencia
spring-cloud-starter-turbine
- Utilizar anotación @EnableTurbine
- Configuración de los microservicios que vamos a monitorizar en application.yml
group 'com.jorgehernandezramirez.spring.springcloud' version '1.0-SNAPSHOT' buildscript { repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE") } } apply plugin: 'java' apply plugin: 'idea' apply plugin: 'maven' apply plugin: 'spring-boot' dependencyManagement { imports { mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR6' } } sourceCompatibility = 1.8 springBoot { mainClass = "com.jorgehernandezramirez.spring.springcloud.turbine.Application" } repositories { mavenCentral() } dependencies { compile('org.springframework.cloud:spring-cloud-starter-turbine') }
package com.jorgehernandezramirez.spring.springcloud.turbine; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.turbine.EnableTurbine; @EnableTurbine @EnableDiscoveryClient @SpringBootApplication public class Application { public Application(){ //For Spring } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
En el fichero application.yml indicamos el cluster y el nombre del microservicio registrado en Eureka. Evidentemente necesitamos configurar la conexión a Eureka.
server: port: 8081 eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ instance: hostname: localhost nonSecurePort: 8081 turbine: aggregator: clusterConfig: BACKEND appConfig: backend
Monitorización
Para monitorizar el estado de los circuitos de nuestro microservicio
Obtendremos algo como
data: {“rollingCountFallbackSuccess”:1,”rollingCountFallbackFailure”:0,
“propertyValue_circuitBreakerRequestVolumeThreshold”:20,”propertyValue_circuitBreakerForceOpen”:false,
“propertyValue_metricsRollingStatisticalWindowInMilliseconds”:10000,”latencyTotal_mean”:0,
“rollingMaxConcurrentExecutionCount”:1,”type”:”HystrixCommand”,”rollingCountResponsesFromCache”:0,
“rollingCountBadRequests”:0,”rollingCountTimeout”:0,”propertyValue_executionIsolationStrategy”:”THREAD”,
“rollingCountFailure”:1,”rollingCountExceptionsThrown”:0,”rollingCountFallbackMissing”:0,”threadPool”:
“TokenController”,”latencyExecute_mean”:0,”isCircuitBreakerOpen”:false,”errorCount”:1,
“rollingCountSemaphoreRejected”:0,”group”:”TokenController”,”latencyTotal”: …
5. Hystrix Dashboard
Ya hemos visto como monitorizar los circuitos de nuestros microservicios. Sin embargo la interpretación de esta información puede no ser clara ya que debemos leer el resultado json de los controladores /hystrix.stream o /turbine.stream. Por este motivo aparece Hystrix Dashboard
!. Básicamente lo que hace es leer la información de estos controladores y pintar en pantalla los resultados. Necesitaremos
- Incluir dependencia
spring-cloud-starter-hystrix-dashboard
- Utilizar anotación
@EnableHystrixDashboard
package com.jorgehernandezramirez.spring.springcloud.hystrixdashboard; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @EnableHystrixDashboard @SpringBootApplication public class Application { public Application(){ //For Spring } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Arrancamos en el puerto 8082
Arrancamos
Vamos a http://localhost:8082/hystrix e indicamos la url localhost:8081/turbine.stream?cluster=BACKEND
El resultado se muestra a continuación. Podemos ver el estado del circuito getToken del microservicio backend
6. Conclusiones
En este post hemos visto como Hystrix
es una solución para implementar el patrón Circuit Breaker en donde podemos monitorizar el estado de todos nuestros circuitos en tiempo real.
El mayor inconveniente de Hystrix es que no guarda un histórico de lo ocurrido en nuestros circuitos. De esta forma únicamente podemos ver la foto actual del sistema.