In the previous article of this series, we demonstrated how to apply the Gateway’s current limiting function. In this article, we will demonstrate how to use Hystrix’s circuit breaker function.

Create a new microservice project

Create a new ordinary maven project in IDEA, name its groupId: cn.com.hohistar.tutorial and artifactId: springcloud-hystrix-service, and then replace the pom.xml of the project with the following::

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>cn.com.hohistar.tutorial</groupId>
<artifactId>springcloud-hystrix-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>springcloud-hystrix-service</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>


</project>

New service consumers

In this example, we will build a class that accesses (consume) the todo-service created earlier, and uses the fuse mechanism in the methods of this class.。

New data class

Create a new package named: cn.com.hohistar.tutorial.springcloudhystrixservice.model in src/main/java, and add a class named: Todo in it, the code is as follows:

1
2
3
4
5
6
7
8
9
10
@Data
public class Todo {

private Long id;

private String title;

private String desc;

}

Because all service returns are not standardized to the format of ApiResult, it is also necessary to add the definition of the ApiResult class in the model package. code show as below:

1
2
3
4
5
6
7
8
9
10
11
@Data
public class ApiResult<T> {

private Boolean succ;

private String code;

private String msg;

private T data;
}

Define access service

Create a new package named: cn.com.hohistar.tutorial.springcloudhystrixservice.service in src/main/java, and add a new class named: HystrixedClientService in it, and fill in the following code:

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
32
@Service
public class HystrixedClientService {

private static final Logger LOG = LoggerFactory.getLogger(HystrixedClientService.class);

@Autowired
RestTemplate restTemplate;

@HystrixCommand(fallbackMethod = "todoFailed")
public ApiResult< List<Todo> > getTodos() {

ApiResult< List<Todo> > resp = restTemplate.getForObject("http://cloud-todo-service/todo", ApiResult.class);

return resp;
}

public ApiResult<List<Todo>> todoFailed() {

LOG.info("todo failed");
ApiResult<List<Todo>> resp = new ApiResult<>();
resp.setSucc(false);
return resp;
}

@Bean
@LoadBalanced
public RestTemplate restTemplate() {

return new RestTemplate();
}

}

New API service

Create a new package named: cn.com.hohistar.tutorial.springcloudhystrixservice.api in src/main/java, and add a new class named: HystrixedClientController in it, and fill in the following code:

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class HystrixedClientController {

@Autowired
private HystrixedClientService clientService;

@GetMapping("/todo")
public ApiResult< List<Todo> > getTodos() {
return clientService.getTodos();
}

}

New startup class

Create a new package named: cn.com.hohistar.tutorial.springcloudhystrixservice in src/main/java, and add a new class named: SpringcloudHystrixServiceApplication in it, and fill in the following code:

1
2
3
4
5
6
7
8
9
10
@EnableEurekaClient
@EnableCircuitBreaker
@SpringBootApplication
public class SpringcloudHystrixServiceApplication {

public static void main(String[] args) {

SpringApplication.run(SpringcloudHystrixServiceApplication.class, args);
}
}

Create a configuration file

Create a new file named application.yml in the src/main/resource directory and add the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 9090

spring:
application:
name: cloud-hystrix-service

eureka:
instance:
leaseRenewalIntervalInSeconds: 1
leaseExpirationDurationInSeconds: 2
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
healthcheck:
enabled: true
lease:
duration: 5

Start the app

Start the application, and then visit in the browser:

1
http://localhost:9090/todo

You can get the json data returned in todo-service. Similar to the following:

1
succ":true,"code":null,"msg":null,"data":[{"id":1,"title":"Call Metting","desc":""},{"id":2,"title":"Print File","desc":""}]}

Verify the fusing mechanism

Hystrix’s default timeout time is 1 second, that is, when the service consumer is waiting for the service provider to return information, if the return information is not received for more than 1 second, the service provider is considered to be faulty and the locally set downgrade strategy is directly executed, that is The method specified by fallbackMethod.

Therefore, we modify the todo-service project and add a mandatory delay of 20 seconds to the method of the TodoService class in the cn.com.hohistar.tutorial.springcloudtodoservice.service package. The modified code is 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
@RestController
public class TodoService {

@GetMapping("/todo")
public List<Todo> getAllTodo() {

List<Todo> todos = new ArrayList<>();

Todo t = new Todo();
t.setTitle("make a call");
t.setDesc("call jack");

todos.add(t);

try {
Thread.currentThread().sleep(20 * 1000);
} catch(Exception e) {
e.printStackTrace();
}

return todos;
}
}

Visit now

1
http://localhost:9090/todo

You can see that the returned result is an empty json array, and you can see that the todoFailed method is executed in the background log.

Modify the default timeout

You can add the following content in application.yml and set the timeout to 3 seconds:

1
2
3
4
5
6
7
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000

Fuse configuration item information

Hystrix official fuse configuration item information