I have introduced you to Hystrix and Hystrix Dashboard to monitor calling to other services of service in the Microservices system. One problem is: we have a lot of services in a Microservices system, we have to open the Hystrix Dashboard of each service to monitor them, aren’t we? It’s more convenient if we can combine the Hystrix Dashboard of all services into a Dashboard and just need to monitor this Hystrix Dashboard. Thankfully we can do this with using Netflix’s Turbine or Turbine Stream and their wrappers from Spring Cloud Netflix. How is it in details? Let’s find out about Turbine Stream first in this tutorial!
To be an example of this tutorial, I will create 3 services with 1 service with a request URL will be called from the other 2 services.
As follows:
Service 1 exposes a request URL “/hello”:
We only need to use the Web dependency for this service. Controller expose request URL “/hello” has the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.huongdanjava.springcloudturbinestreamservice1; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class Service1Controller { @GetMapping("/hello") public String hello() { return "Hello !!!"; } } |
application.properties:
1 |
server.port=8082 |
2 service calls to Service 1 above will use Web, Hystrix, Actuator as follows:
Service 2:
Service2Controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.huongdanjava.springcloudturbinestreamservice2; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; @RestController public class Service2Controller { @GetMapping("/service2-call-hello") @HystrixCommand(fallbackMethod = "defaultCallHello") public String service2CallHello() { return new RestTemplate().getForObject("http://localhost:8082/hello", String.class); } public String defaultCallHello() { return "Hello from service 2!!!"; } } |
SpringCloudTurbineService2Application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdanjava.springcloudturbinestreamservice2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; @SpringBootApplication @EnableCircuitBreaker public class SpringCloudTurbineStreamService2Application { public static void main(String[] args) { SpringApplication.run(SpringCloudTurbineStreamService2Application.class, args); } } |
application.properties:
1 2 3 |
server.port=8083 management.endpoints.web.exposure.include=* |
Service 3
Service3Controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.huongdanjava.springcloudturbinestreamservice3; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; @RestController public class Service3Controller { @GetMapping("/service3-call-hello") @HystrixCommand(fallbackMethod = "defaultCallHello") public String service3CallHello() { return new RestTemplate().getForObject("http://localhost:8082/hello", String.class); } public String defaultCallHello() { return "Hello from service 3!!!"; } } |
SpringCloudTurbineService3Application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdanjava.springcloudturbinestreamservice3; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; @SpringBootApplication @EnableCircuitBreaker public class SpringCloudTurbineStreamService3Application { public static void main(String[] args) { SpringApplication.run(SpringCloudTurbineStreamService3Application.class, args); } } |
application.properties:
1 2 3 |
server.port=8084 management.endpoints.web.exposure.include=* |
As you can see, both services 2 and 3 call the “/hello” URL request of Service 1 and they are all enabled for Circuit Breaker support.
Next, I will create a separate project that acts as a Hystrix Dashboard, if you don’t have a Turbine Stream, you can only monitor 1 service at a time:
SpringCloudTurbineHystrixDashboardApplication:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdanjava.springcloudturbinestreamhystrixdashboard; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @SpringBootApplication @EnableHystrixDashboard public class SpringCloudTurbineStreamHystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudTurbineStreamHystrixDashboardApplication.class, args); } } |
application.properties:
1 |
server.port=8081 |
Now if you run all the applications:
Request to Hystrix Dashboard:
Enter Hystrix Stream URL of service 2 or service 3, then click Monitor Stream.
Request the URL http://localhost:8083/service2-call-hello or http://localhost:8084/service3-call-hello, you will see Hystrix Dashboard will have the following results:
You can see that we cannot monitor multiple services at the same time in Hystrix Dashboard.
Now let’s try using Turbine Stream.
To use Turbine Stream, you need to use an MQ server.
All data will be sent to this MQ server.
Here, I will use the RabbitMQ server! You can refer to the tutorial about installing RabbitMQ with Management UI for it to start a server RabbitMQ for yourself.
My RabbitMQ starts as follows:
The username and password for this RabbitMQ will be guest/guest!
OK, now we will configure service 2 and service 3 so that they can send data to the MQ server. I will add each service to the dependency project as after:
1 2 3 4 5 6 7 8 |
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-hystrix-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> |
Currently, the latest version of Spring Cloud is having problems, Turbine Stream does not work with this version, you need to use Spring Cloud version Greenwich.M1. Please open pom.xml file of 2 services 2 and 3, edit Spring Cloud version as follows:
1 2 3 4 |
<properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.M1</spring-cloud.version> </properties> |
Configure RabbitMQ information so that these services send data to MQ server in application.properties file as follows:
1 2 3 4 |
spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest |
After configuring the send data section to MQ Server for services, you need to create another project that serves as Turbine Stream and combine the data in the MQ server to send to Hystrix Dashboard.
The project is as follows:
Result:
In the SpringCloudTurbineStreamApplication class, add the annotation @EnableTurbineStream to implement Turbine Stream. By default Turbine Stream will try to register it with Eureka Server so if you do not disable it, there will be many errors.
Please disable this and configure RabbitMQ server information in the application.properties file of the project. This is as follows:
1 2 3 4 5 6 7 8 9 |
server.port=8989 spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest eureka.client.register-with-eureka=false eureka.client.fetch-registry=false |
OK, now start all the apps on you guys:
Go to Hystrix Dashboard, enter Turbine Stream information http://localhost:8989 turbine.stream, click the Monitor Stream button, call 2 service requests 2 and 3: http://localhost:8083/service2-call-hello, http://localhost:8084/service3-call-hello, you will see the following result:
subha
Error proxying request: http://localhost:8989
java.net.SocketTimeoutException: Read timed out