Hystrix服务熔断和降级

在微服务架构中,存在着那么多的服务单元,若一个单元出现故障,就会因依赖关系形成故障蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构就更加的不稳定;为了解决这样的问题,因此产生了断路器模式.
在Spring Cloud中使用了Hystrix 来实现断路器的功能;Hystrix是Netflix开源的微服务框架套件之一,该框架目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力;Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能.

Ribbon熔断

工程结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ribbon-hystrix
├── pom.xml
├── src
│   └── main
│   ├── java
│   │   └── com
│   │   └── cloud
│   │   └── ribbon
│   │   └── hystrix
│   │   ├── Application.java
│   │   ├── service
│   │   │   └── RibbonService.java
│   │   └── www
│   │   └── RibbonController.java
│   └── resources
│   └── application.properties

Maven依赖

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
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</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-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
</dependencies>

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

主类示例

在应用主类中使用@EnableCircuitBreaker@EnableHystrix注解开启Hystrix的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@EnableHystrix
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

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

@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}

}

配置文件

1
2
3
4
5
6
7
8
9
10
### 应用端口
server.port=8080
spring.application.name=consumer-ribbon

### 应用注册信息
eureka.client.service-url.defaultZone=http://admin:pwd123@node1.test.com:8001/eureka/,http://admin:pwd123@node1.test.com:8002/eureka/,http://admin:pwd123@node3.test.com:8003/eureka/

### 链路追踪
spring.sleuth.sampler.percentage=1
spring.zipkin.base-url=http://172.30.12.197:9411/

熔断服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Slf4j
@Service
public class RibbonService {

@Autowired
private RestTemplate restTemplate;

/**
* 接口熔断
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "getRemoteFallback")
public String getRemote(long id) {
Assert.isTrue(id >0, "参数必须大于0");
return restTemplate.getForObject("http://SERVER-PROVIDER/api/select/{0}", String.class, id);
}

private String getRemoteFallback(long id) {
return "服务熔断[Ribbon]: 入参:" + id;
}
}

Feign熔断

工程结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
feign-hystrix
├── pom.xml
├── src
│   └── main
│   ├── java
│   │   └── com
│   │   └── cloud
│   │   └── feign
│   │   └── hystrix
│   │   ├── Application.java
│   │   ├── service
│   │   │   ├── FeignService.java
│   │   │   └── FeignServiceHystrix.java
│   │   └── www
│   │   └── FeignController.java
│   └── resources
│   └── application.properties

Maven依赖

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
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</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-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
</dependencies>

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

主类示例

1
2
3
4
5
6
7
8
9
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

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

熔断服务

接口定义
1
2
3
4
5
6
@FeignClient(name="SERVER-PROVIDER", fallback = FeignServiceHystrix.class)
public interface FeignService {

@GetMapping(value="/api/select/{id}")
public String getRemote(@PathVariable("id") long id);
}
接口实现
1
2
3
4
5
6
7
8
@Component
public class FeignServiceHystrix implements FeignService{

@Override
public String getRemote(long id) {
return "服务熔断[Feign]: 入参:" + id;
}
}

测试熔断

启动注册中心、zipkin(可选项目)、服务提供者、服务消费者, 效果图如下:

正常访问测试

1
2
3
4
[root@localhost ~ ]$ curl -XGET '172.30.12.197:8080/api/invoke/select/1'
测试数据1
[root@localhost ~ ]$ curl -XGET '172.30.12.197:9090/api/invoke/select/1'
测试数据1

熔断访问测试

停用服务端,进行客户端访问

1
2
3
4
[root@localhost ~ ]$ curl -XGET '172.30.12.197:8080/api/invoke/select/111'
服务熔断[Ribbon]: 入参:111
[root@localhost ~ ]$ curl -XGET '172.30.12.197:9090/api/invoke/select/111'
服务熔断[Feign]: 入参:111

Hystrix监控

应用添加熔断监控

Hystrix Dashboard监控单实例节点需要通过访问实例的/hystrix.stream接口来实现,所以必须保证待监控示例中满足如下配置:

Maven依赖
1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
开启熔断

确保在服务实例的主类中已经使用@EnableCircuitBreaker或@EnableHystrix注解,开启了断路器功能.

地址检测

访问实例的hystrix.stream接口,检测地址是否可以正常访问.

1
2
3
4
5
[root@localhost ~]$ curl http://172.30.12.197:8080/hystrix.stream
ping:

[root@localhost ~]$ curl http://172.30.12.197:9090/hystrix.stream
ping:

Hystrix单体监控

工程结构
1
2
3
4
5
6
7
8
9
10
11
12
hystrix-dashboard
├── pom.xml
├── src
│   └── main
│   ├── java
│   │   └── com
│   │   └── cloud
│   │   └── hystrix
│   │   └── dashboard
│   │   └── Application.java
│   └── resources
│   └── application.properties
Maven依赖
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
<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-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
主类示例
1
2
3
4
5
6
7
8
@EnableHystrixDashboard
@SpringCloudApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
注册查看

面板访问

访问面板:http://172.30.12.197:9090/hystrix.stream

Hystrix Dashboard 共支持三种不同的监控方式:

  • 默认的集群监控:通过URL http://turbine-hostname:port/turbine.stream 开启,实现对默认集群的监控.
  • 指定的集群监控:通过URL http://turbine-hostname:port/turbine.stream?cluster=[clusterName] 开启,实现对clusterName集群的监控.
  • 单体应用的监控:通过URL http://hystrix-app:port/hystrix.stream 开启,实现对具体某个服务实例的监控.

前两者都是对集群的监控,需要整合Turbine才能实现,这里我们先来实现单个服务实例的监控.

监控单体实例

监控Ribbon消费实例,在监控面板中键入Ribbon示例的示例监控地址http://172.30.12.197:8080/hystrix.stream; 调用Ribbon消费实例,查看监控面板.

监控Feign消费实例,在监控面板中键入Ribbon示例的示例监控地址http://172.30.12.197:9090/hystrix.stream; 调用Feign消费实例,查看监控面板.

Hystrix聚合监控

在复杂的分布式系统中,相同服务的节点经常需要部署上百甚至上千个,很多时候,运维人员希望能够把相同服务的节点状态以一个整体集群的形式展现出来,这样可以更好的把握整个系统的状态; 为此Netflix提供了一个开源项目(Turbine)来提供把多个hystrix.stream的内容聚合为一个数据源供Dashboard展示.

工程结构
1
2
3
4
5
6
7
8
9
10
11
12
hystrix-turbine
├── pom.xml
├── src
│   └── main
│   ├── java
│   │   └── com
│   │   └── cloud
│   │   └── turbine
│   │   └── hystrix
│   │   └── Application.java
│   └── resources
│   └── application.properties
Maven依赖
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
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-turbine</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-hystrix-dashboard</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
主类示例

使用@EnableTurbine注解开启Turbine.

1
2
3
4
5
6
7
8
9
10
11
@EnableTurbine
@EnableDiscoveryClient
@SpringBootApplication
@EnableHystrixDashboard
public class Application {

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

}
配置文件
1
2
3
4
5
6
7
8
9
10
11
### 应用基础配置
server.port=9999
spring.application.name=hystrix-turbine

### 监控示例配置
turbine.appConfig=CONSUMER-RIBBON,CONSUMER-FEIGN
turbine.aggregator.clusterConfig= default
turbine.clusterNameExpression= new String("default")

### 应用注册信息
eureka.client.service-url.defaultZone=http://admin:pwd123@node1.test.com:8001/eureka/,http://admin:pwd123@node1.test.com:8002/eureka/,http://admin:pwd123@node3.test.com:8003/eureka/

参数说明:

  • turbine.appConfig: 配置Eureka中的serviceId列表,表明监控哪些服务.
  • turbine.aggregator.clusterConfig: 指定聚合哪些集群,多个使用”,”分割,默认为default; 可使用 http://.../turbine.stream?cluster={clusterConfig之一} 访问
  • turbine.clusterNameExpression:

    1. clusterNameExpression指定集群名称,默认表达式appName;此时:turbine.aggregator.clusterConfig需要配置想要监控的应用名称;
    2. 当clusterNameExpression: default时,turbine.aggregator.clusterConfig可以不写,因为默认就是default;
    3. 当clusterNameExpression: metadata[‘cluster’]时,假设想要监控的应用配置了eureka.instance.metadata-map.cluster: ABC,则需要配置,同时turbine.aggregator.clusterConfig: ABC
注册查看

监控查看

查看聚合监控

1
2
[root@localhost ~]$ curl http://172.30.12.197:9999/turbine.stream
: ping

访问监控面板http://172.30.12.197:9999/hystrix,监控聚合监控地址http://172.30.12.197:9999/turbine.stream,分别执行实例调用.

监控指标

参考资料: http://blog.didispace.com/spring-cloud-starter-dalston-5-1/

技术博客