Spring Cloud 入门教程(07):熔断机制 -- 断路器

Spring Cloud的断路器

对断路器模式不太清楚的话,可以参看另一篇博文:断路器(Curcuit Breaker)模式,下面直接介绍Spring Cloud的断路器如何使用。

SpringCloud Netflix实现了断路器库的名字叫Hystrix. 在微服务架构下,通常会有多个层次的服务调用. 下面是微服架构下, 浏览器端通过API访问后台微服务的一个示意图:
enter description here
一个微服务的超时失败可能导致瀑布式连锁反映,下图中,Hystrix通过自主反馈实现的断路器, 防止了这种情况发生。
enter description here
图中的服务B因为某些原因失败,变得不可用,所有对服务B的调用都会超时。当对B的调用失败达到一个特定的阀值(5秒之内发生20次失败是Hystrix定义的缺省值), 链路就会被处于open状态, 之后所有所有对服务B的调用都不会被执行, 取而代之的是由断路器提供的一个表示链路open的Fallback消息. Hystrix提供了相应机制,可以让开发者定义这个Fallbak消息.

open的链路阻断了瀑布式错误, 可以让被淹没或者错误的服务有时间进行修复。这个fallback可以是另外一个Hystrix保护的调用, 静态数据,或者合法的空值. Fallbacks可以组成链式结构,所以,最底层调用其它业务服务的第一个Fallback返回静态数据.

具体实践

下面,进入正题,在之前的两HELLO WORLD服务集群中加入断路器, 防止其中一个Hello world挂掉后, 导致系统发生连锁超时失败。

1. 添加hystrix库支持断路器

在maven工程(前面章节中介绍的Ribbon或者Feign工程)的pom.xml中

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

2. 在Ribbon应用中使用断路器

1). 在Spring Boot启动类上添加@EnableCircuitBreaker注解

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
package org.cwalzy.ribbon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;

@SpringBootApplication
@EnableDiscoveryClient//@EnableDiscoveryClient向服务中心注册
@EnableCircuitBreaker
@EnableHystrixDashboard
public class ServiceRibbonApplication {

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

@Bean//注册了一个叫restTemplate的bean
@LoadBalanced//@ LoadBalanced注册表明,这个restRemplate是需要做负载均衡的。 利用用ribbon进行负载均衡,会轮流的调用处于两个不同端口的Hello world服务
RestTemplate restTemplate() {
return new RestTemplate();
}

//https://www.cnblogs.com/mark7/p/8920288.html
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/actuator/hystrix.stream");
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}

2). 用@HystrixCommand注解标注访问服务的方法

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
package org.cwalzy.ribbon;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class HelloService {
@Autowired RestTemplate restTemplate;

/* @HystrixCommand注解定义了一个断路器,它封装了getHelloContant()方法,
* 当它访问的SERVICE-HELLOWORLD失败达到阀值后,将不会再调用SERVICE-HELLOWORLD,
* 取而代之的是返回由fallbackMethod定义的方法serviceFailure()。
*
* @HystrixCommand注解定义的fallbackMethod方法,需要特别注意的有两点
*
* 第一, fallbackMethod的返回值和参数类型需要和被@HystrixCommand注解的方法完全一致。否则会在运行时抛出异常。比如本例中,serviceFailure()的返回值和getHelloContant()方法的返回值都是String。
*
* 第二, 当底层服务失败后,fallbackMethod替换的不是整个被@HystrixCommand注解的方法(本例中的getHelloContant), 替换的只是通过restTemplate去访问的具体服务。可以从中的system输出看到, 即使失败,控制台输出里面依然会有“call SERVICE-HELLOWORLD”。
* */
@HystrixCommand(fallbackMethod = "serviceFailure")
public String getHelloContent() {
//这里关键代码就是, restTemplate.getForObject方法会通过ribbon负载均衡机制, 自动选择一个Hello word服务,
return restTemplate.getForObject("http://SERVICE-HELLOWORLD/",String.class);
}

public String serviceFailure() {
return "hello world service is not available !";
}
}

@HystrixCommand注解定义了一个断路器,它封装了getHelloContant()方法, 当它访问的SERVICE-HELLOWORLD失败达到阀值后,将不会再调用SERVICE-HELLOWORLD, 取而代之的是返回由fallbackMethod定义的方法serviceFailure()。@HystrixCommand注解定义的fallbackMethod方法,需要特别注意的有两点:

第一,fallbackMethod的返回值和参数类型需要和被@HystrixCommand注解的方法完全一致。否则会在运行时抛出异常。比如本例中,serviceFailure()的返回值和getHelloContant()方法的返回值都是String。

第二,当底层服务失败后,fallbackMethod替换的不是整个被@HystrixCommand注解的方法(本例中的getHelloContant),替换的只是通过restTemplate去访问的具体服务。可以从中的system输出看到, 即使失败,控制台输出里面依然会有“call SERVICE-HELLOWORLD”。

启动eureka服务,只启动两个Helloworld服务,然后中断其中一个(模拟其中一个微服务挂起),访问 http://localhost:8901/ 然后刷新, 由于有负载均衡可以看到以下两个页面交替出现。可以看到第二个被挂起的服务,被定义在Ribbon应该里面的错误处理方法替换了。
enter description here
enter description here

3. 在Feign应用中使用断路器

1). Feign内部已经支持了断路器,所以不需要想Ribbon方式一样,在Spring Boot启动类上加额外注解

2). 用@FeignClient注解添加fallback类, 该类必须实现@FeignClient修饰的接口。

1
2
3
4
5
FeignClient(name = "SERVICE-HELLOWORLD", fallback = HelloWorldServiceFailure.class)
public interface HelloWorldService {
@RequestMapping(value = "/",method = RequestMethod.GET)
String sayHello();
}

3). 创建HelloWorldServiceFailure类, 必须实现被@FeignClient修饰的HelloWorldService接口。注意添加@Component或者@Service注解,在Spring容器中生成一个Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
package org.cwalzy.springcloudeureka;

import org.springframework.stereotype.Component;

@Component
public class HelloWorldServiceFailure implements HelloWorldService {

@Override
public String sayHello() {
System.out.println("hello world service is not available ! we are repairing");
return "hello world service is not available ! we are repairing";
}
}

4). 激活断路器

Spring Cloud之前的Brixton版本中,Feign是缺省是自动激活了断路器的,但最近的Dalston版本已经将缺省配置修改为禁止。

原因参见: https://github.com/spring-cloud/spring-cloud-netflix/issues/1277 , 这一点要注意。所以要在Feign中使用断路器, 必须在application.yml中添加如下配置:

1
2
3
feign:
hystrix:
enabled: true

5). 启动Feign应用, 访问 http://localhost:8902/hello , 可以一看到和Ribbon一样的效果。

本文转载自 https://www.cnblogs.com/chry/p/7279856.html
原文基于 spring boot 1.5.3.RELEASE 版本
而本文中有一些改动 是基于 spring boot 2.1.5.RELEASE 版本