I have introduced you to Hystrix and Hystrix Dashboard with the problems that they solve in a Microservices system. For Reactive Web Service applications, using Hystrix and Hystrix Dashboard will be a little different. Currently, the @HystrixCommand annotation will not work with the Reactive Web Service applications, we have to use the HystrixCommands class to solve our problems. How is it in details? In this tutorial, I will show you how to use Hystrix and Hystrix Dashboard from Spring Cloud Netflix in the Reactive Web Service application with Spring WebFlux!
For this example, I will use the Reactive Web Service I created in the tutorial about Reactive Web Service using Spring WebFlux.
I will expose an additional URL request in this Reactive Web Service:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdanjava.springwebfluxannotation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class HelloController { @GetMapping(value = "/hello") public Flux<String> findAll() { return Flux.just("Hello, Huong Dan Java"); } } |
I will create another application using WebClient to consume the Reactive Web Service with Hystrix and Hystrix Dashboard as follows:
Result:
Next, I will add a new URL request using WebClient to consume Reactive Web Service in this tutorial. My controller has the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.huongdanjava.springwebfluxhystrix; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Flux; @RestController public class SpringWebfluxHystrixController { @RequestMapping(value = "/consume-hello") public String findAll() { String url = "http://localhost:8080"; WebClient webClient = WebClient.create(url); Flux<String> flux = webClient.get() .uri("/hello") .retrieve() .bodyToFlux(String.class); return flux.blockFirst(); } } |
Since the Reactive Web Service is currently running on port 8080, I will add the server.port property in the application.properties file of this application with the value of 8081 so this application runs on another port.
1 |
server.port=8081 |
Run both applications and the results when I request to the address http://localhost:8081/consume-hello will be as follows:
OK, now I will use Hystrix to implement Circuit Breaker, when Reactive Web Service is not available, our application will return to your default value.
To do this, I will add annotation @EnableCircuitBreaker into the main class of the application as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdanjava.springwebfluxhystrix; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; @SpringBootApplication @EnableCircuitBreaker public class SpringWebfluxHystrixApplication { public static void main(String[] args) { SpringApplication.run(SpringWebfluxHystrixApplication.class, args); } } |
As I said, @HystrixCommand annotation currently does not work with Spring WebFlux, we will use HystrixCommands class as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.huongdanjava.springwebfluxhystrix; import org.springframework.cloud.netflix.hystrix.HystrixCommands; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Flux; @RestController public class SpringWebfluxHystrixController { @RequestMapping(value = "/consume-hello") public String findAll() { String url = "http://localhost:8080"; WebClient webClient = WebClient.create(url); Flux<String> flux = webClient.get() .uri("/hello") .retrieve() .bodyToFlux(String.class); Flux<String> fluxAsDefault = HystrixCommands.from(flux) .commandName("hello") .fallback(Flux.just("Hello, Khanh Nguyen")) .toFlux(); return fluxAsDefault.blockFirst(); } } |
As you can see, using HystrixCommands we will create a fallback from the returned Flux or Mono object. Command name here is the name that will be used to distinguish, so we can know which calling request URL is having problems.
If you now stop the Reactive Web Service, restart the application and request the URL http://localhost:8081/consume-hello, the result will be as follows:
Now let’s try to configure you to use Hystrix Dashboard.
We need to add the @EnableHystrixDashboard annotation in the main class of the application as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.huongdanjava.springwebfluxhystrix; 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 @EnableHystrixDashboard public class SpringWebfluxHystrixApplication { public static void main(String[] args) { SpringApplication.run(SpringWebfluxHystrixApplication.class, args); } } |
Next, we will configure the Actuator enable API to provide data for Hystrix Dashboard in application.properties file as follows:
1 |
management.endpoints.web.exposure.include=* |
OK, now try accessing the URL http://localhost:8081/hystrix, enter http://localhost:8081/actuator/hystrix.stream, press “Monitor Stream” and then request to the address http://localhost:8081/consume-hello, you will see Hystrix Dashboard display as follows:
Start the Reactive Web Service again, you will see the following result in Hystrix Dashboard when requesting to http://localhost:8081/consume-hello:
No errors are displayed, but it seems that with Spring WebFlux, Hystrix Dashboard has not yet displayed the number request of success.