Circuit Breaker - VidoniJorge/gs-spring-cloud-netflix GitHub Wiki
Es la implementación del patrón de Circuit Breaker creada por Netfix.
La idea principal que tenemos que tener en cuenta cuando estamos implementando un Circuit Breaker, es como vamos a tratar en nuestro cliente alguna falla durante la comunicación con algún servicio o con el procesamiento del response o request.
A continuación, ejemplificaremos como configurar hystrix en nuestras MS.
Asumiremos que el lector de este articulo sabe cómo crear un MS básico.
Partido de un servicio creado, tendremos que seguir los siguientes pasos:
- Configurar dependencias
- Habilitar Circuit Breaker
- Configurar nuestro Circuit Breaker
Group: 'org.springframework.cloud' ; artifact: 'spring-cloud-starter-netflix-hystrix'
Group: 'org.springframework.cloud' ; artifact: 'spring-cloud-starter-hystrix'
Para configurar las dependencias en nuestro proyecto solo tenemos que agregar en nuestro archivo gradle o maven, las siguientes las dependencias especificadas anteriormente.
Gradle
dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' compile 'org.springframework.boot:spring-boot-starter-web' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-hystrix' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix' compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-hystrix-dashboard' }
Maven
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</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-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
Para indicarle a Spring que nuestra aplicación utilizara el patrón de circuit breaker lo único que tenemos que hacer es agregar en nuestra clase SpringBootApplication la anotación @EnableCircuitBreaker.
Ejemplo:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @SpringBootApplication @EnableCircuitBreaker public class HystrixApplication { public static void main(String[] args) { SpringApplication.run(HystrixApplication.class, args); } }
Primero que nada, nos dirigimos a nuestro Controller y le agregamos la anotación @HystrixCommand sobre el método que queremos aplicar el patrón de Circuit bracker.
Una vez agregada la anotación @HystrixCommand le tenemos settear un valor al atributo fallbackMethod de la anotación. Este atributo contendrá el nombre del método que se invocará cuando se abra el circuito.
El nombre del método queda a nuestra libre imaginación, lo importante es que tenga los mismos parámetros de trata y salida que el método al que le estamos aplicando el patrón de Circuit Breaker.
En nuestro ejemplo lo configuraremos de la siguiente forma:
@HystrixCommand(fallbackMethod = "defaultMgs")
Ejemplo:
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.http.ResponseEntity; import org.springframework.beans.factory.annotation.Autowired; @Controller public class ProductController { @Autowired ProductService productService; @HystrixCommand(fallbackMethod = "defaultMgs") @GetMapping(value = { "/get", "/get/{id}" }) public ResponseEntity<?> getProduct(@PathVariable(value = "id", required = false) Optional<String> id) { return productService.getProduct(id); } public ResponseEntity<?> defaultMgs(Optional<String> id) { return new ResponseEntity<>("Error",HttpStatus.NOT_FOUND); } }
Como se puede observar en el ejemplo nuestro método defaultMgs tiene declarado los mismos parámetros de entrada y salida que nuestro método getProduct.
La configuración realizada hasta el momento contiene todos los valores por defecto que establece Spring. Ahora bien, ¿qué pasa si necesitamos modificar estos valore por defecto?, para esto utilizamos el atributo commandProperties. La sintaxis que se tiene que utilizar el commandProperties es la siguiente
@HystrixCommand( ... , commandProperties = { @HystrixProperty(name="<propertie value>",value="<value>"), ..., @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="10") })
La lista entera de properties que se pueden usar se pueden visualizar en el siguiente [enlace] (https://github.com/Netflix/Hystrix/wiki/Configuration#CommandCircuitBreaker).
Las propiedades con las que vamos a trabajar en nuestro ejemplo son:
- circuitBreaker.requestVolumeThreshold: Esta propiedad establece el número mínimo de solicitudes en una ventana móvil que disparará el circuito.
- circuitBreaker.sleepWindowInMilliseconds: Esta propiedad establece la cantidad de tiempo, después de disparar el circuito, para rechazar solicitudes antes de permitir intentos nuevamente para determinar si el circuito debe cerrarse nuevamente.
- circuitBreaker.errorThresholdPercentage: Esta propiedad establece el porcentaje de error en o por encima del cual el circuito debe abrirse y comenzar las solicitudes de cortocircuito a la lógica de recuperación.
Ejemplo completo:
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.http.ResponseEntity; import org.springframework.beans.factory.annotation.Autowired; @Controller public class ProductController { @Autowired ProductService productService; @HystrixCommand(fallbackMethod = "defaultMgs", commandProperties = { @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value="5"), @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="30000"), @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="10") }) @GetMapping(value = { "/get", "/get/{id}" }) public ResponseEntity<?> getProduct(@PathVariable(value = "id", required = false) Optional<String> id) { return productService.getProduct(id); } public ResponseEntity<?> defaultMgs(Optional<String> id) { return new ResponseEntity<>("Error",HttpStatus.NOT_FOUND); } }