在本系列的 上一篇文章 中,我们演示了如何适用 Gateway 的限流功能。本文中,我们将演示如何使用 Hystrix 的熔断功能。
新建一个微服务项目
在 IDEA 中新建一个普通的 maven 项目,将其 groupId 命名为: cn.com.hohistar.tutorial, artifactId 命名为: springcloud-hystrix-service, 然后将项目的 pom.xml 替换为如下内容:
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/> </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>
|
新增服务消费者
在本示例中,我们将构建一个访问(消费)前面建立的 todo-service 的类,并在该类的方法中使用熔断机制。
新建数据类
在 src/main/java 中新建一个名为: cn.com.hohistar.tutorial.springcloudhystrixservice.model 的包(package), 并在其中增加一个名为: Todo 的类, 代码如下:
1 2 3 4 5 6 7 8 9 10
| @Data public class Todo {
private Long id;
private String title;
private String desc;
}
|
因为所有的服务返回都别规范为 ApiResult 的格式,所以还需要加入 ApiResult 类的定义在 model 包中。 代码如下:
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; }
|
定义访问服务
在 src/main/java 中新建一个名为: cn.com.hohistar.tutorial.springcloudhystrixservice.service 的包(package),并在其中新增一个名为: HystrixedClientService 的类, 并填入以下代码:
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(); }
}
|
新建API服务
在 src/main/java 中新建一个名为: cn.com.hohistar.tutorial.springcloudhystrixservice.api 的包(package),并在其中新增一个名为: HystrixedClientController 的类, 并填入以下代码:
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(); }
}
|
新建启动类
在 src/main/java 中新建一个名为: cn.com.hohistar.tutorial.springcloudhystrixservice 的包(package),并在其中新增一个名为: SpringcloudHystrixServiceApplication 的类, 并填入以下代码:
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); } }
|
建立配置文件
在 src/main/resource 目录中新建名为 application.yml 的文件,加入以下内容:
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
|
启动应用
启动应用,然后在浏览器中访问:
1
| http://localhost:9090/todo
|
可以得到 todo-service 中返回的 json 数据。类似如下:
1
| succ":true,"code":null,"msg":null,"data":[{"id":1,"title":"Call Metting","desc":""},{"id":2,"title":"Print File","desc":""}]}
|
验证熔断机制
Hystrix默认的超时时间是1秒,也就是服务消费者在等待服务提供者返回信息时,如果超过1秒没有得到返回信息的话,就认为服务提供者故障,直接执行本地设置的降级策略,也就是fallbackMethod指定的方法。
因此我们修改 todo-service 项目,将 cn.com.hohistar.tutorial.springcloudtodoservice.service 包中的 TodoService 类的方法中加入强制延迟 20 秒, 修改后的代码如下:
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; } }
|
这时再访问
1
| http://localhost:9090/todo
|
可以看到返回的结果为空的 json 数组,并且在后台的日志中能看到 todoFailed 方法被执行了。
修改默认的超时时间
可在 application.yml 中加入一下内容,将超时时间设置为3秒:
1 2 3 4 5 6 7
| hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000
|
熔断配置项信息
Hystrix官方熔断配置项信息