Spring Cloud 服务容错保护 Hystrix

  由于后起之秀 Sentinel 的诞生,不建议在新项目中再使用 Hystrix。

序言

  在微服务架构中,存在着那么多的服务单元,若一个单元出现故障,就很容易因依赖关系而引发故障的蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构更加不稳定。

  为了解决这样的问题, 产生了熔断器等一系列的服务保护机制。

  在分布式架构中, 断路器模式的作用也是类似的,当某个服务单元发生故障(类似电器发生短路) 之后, 通过熔断器的故障监控(类似熔断保险丝), 向调用方返回一个错误响应, 而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。

  Hystrix 就是一种熔断器,具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能.。

Hystrix:服务容错保护

快速入门

  改造原有 demo,在consumer加入 Hystrix 熔断器。

① 添加依赖

  在consumerpom.xml文件加入依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

② 添加注解

  在consumer的启动类上加入@SpringCloudApplication注解:

1
2
3
4
5
6
7
8
9
10
11
12
@SpringCloudApplication
public class ConsumerApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}

public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class);
}
}

  该注解相当于以下 3 个注解的集合体 :

  • @SpringBootApplication
  • @EnableDiscoveryClient
  • @EnableCircuitBreaker

  之后在控制类上加入 Hystrix 相关的注解@HystrixCommand,如对consumer模块的ConsumerController类修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequestMapping("consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;

@GetMapping("{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallback")
public String queryById(@PathVariable("id") Integer id) {
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}

public String queryByIdFallback(Integer id) {
return "不好意思,服务器太拥挤了,请稍后再试";
}
}

  这代表当调用queryById方法一定时间(默认 1 秒)未执行时,Hystric 将转而执行指定的queryByIdFallback方法以避免故障的蔓延。

疑问

:我们只对queryById方法使用了 Hystric 熔断器,但具体场景可能不止一个方法需要这么做,如何解决这个问题呢?
:我们可以直接在controller类上使用@DefaultProperties注解并指定方法,之后在相关方法上使用@HystrixCommand注解,熔断超时都会转发到那个方法,改造的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
@RequestMapping("consumer")
@DefaultProperties(defaultFallback = "defaultFallback")
public class ConsumerController {

@Autowired
private RestTemplate restTemplate;

@GetMapping("{id}")
@HystrixCommand
public String queryById(@PathVariable("id") Integer id) {
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}

public String defaultFallback() {
return "不好意思,服务器太拥挤了,请稍后再试";
}
}

:前面我们的查询方法使用 Hystrix 熔断器后默认的超时时间为 1 秒,但在不同的场景中可能需要设置不同的时间,该怎么修改呢?
:可以对具体的方法上的@HystrixCommand设置超时时间,示例如下:

1
2
3
4
5
6
7
8
@GetMapping("{id}")
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
})
public String queryById(@PathVariable("id") Integer id) {
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}

③ 修改 user-service 模块并测试

  首先我们修改user-service模块的controller层的UserController类来模拟线程故障:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserServiceImpl userService;

@GetMapping("/{id}")
public User queryById(@PathVariable("id") Integer id) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return userService.queryById(id);
}
}

  启动 3 个模块进行测试,访问http://localhost:8080/consumer/1的结果如下:

1
不好意思,服务器太拥挤了,请稍后再试

④ 修改配置文件

:前面 Hystrix 熔断器可以对具体的方法设置熔断时间,那有没有配置能设置全局超时参数呢?
:当然有,在application.yml文件添加如下配置即可修改相关模块 Hystrix 熔断器的全局默认时间为 2 秒了:

1
2
3
4
5
6
7
hystrix:
commond:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000

参考

0%