Loading... 本节示例代码在: [https://github.com/laolunsi/spring-boot-stack](https://github.com/laolunsi/spring-boot-stack) --- 在没有良好异常处理机制的微服务架构中,可以预见的是,一旦某个服务发生故障,依赖于此服务的服务就会产生连环性的破坏,导致“雪崩效应”。 ![file](http://zfh-public-blog.oss-cn-beijing.aliyuncs.com/image-1577285635364.png) 为了解决这一问题,提出了`断路器`的概念。 官网: > Netflix has created a library called [Hystrix](https://github.com/Netflix/Hystrix) that implements the [circuit breaker pattern](https://martinfowler.com/bliki/CircuitBreaker.html). In a microservice architecture, it is common to have multiple layers of service calls, as shown in the following example: Netflix提供了Hystrix库,用于实现断路器模型。在微服务架构中,通常有多层服务调用。 下面的示例演示了`Hystrix`分别在ribbon和feign这两种服务调用方式中的配合使用。 本示例采用`SpringBoot 2.1.7.RELEASE`和`SpringCloud Greenwich.SR2`。在使用SpringBoot2.0.x和SpringCloud Finchley.RELEASE版本时,发现Feign的Hystrix开启配置是没有提示的(也会生效,但是没有代码提示,很奇怪。所以把版本换成SpringBoot 2.0.x和SpringCloud Finchley.RELEASE也可以正常运行的) 本节依然使用consul做服务中心。 ------ ## 一、创建service-producer 首先创建一个服务提供者`service-producer`,引入`consul-discovery`依赖,并添加一个测试接口即可。(与之前的教程基本一致) 这里不具体描述了,可以直接看源码:https://github.com/laolunsi/spring-cloud-examples/tree/master/05-ServiceHystrix/service-producer 或者参考之前的教程 下面我们就在使用feign和ribbon这两种服务调用方式中,分别如何去使用断路器Hystrix。 ------ ## 二、基于Feign使用Hystrix 引入`consul`、`feign`和`hystrix`的依赖: ```xml <properties> <java.version>1.8</java.version> <!--<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>--> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 引入consul-discovery --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <!-- 引入feign,用于调用其他服务的接口 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- 健康检查 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- hystrix断路器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` 注意这里使用的SpringCloud版本是`Greenwich.SR2`,与之对应的SpringBoot版本是2.1.x.RELEASE 配置: ```yaml server: port: 8510 spring: application: name: service-consumer-feign cloud: consul: host: localhost port: 8500 discovery: register: true instance-id: ${spring.application.name}:${server.port} service-name: ${spring.application.name} port: ${server.port} # 加入这个配置,用于启动feign自带的断路器 feign: hystrix: enabled: true ``` 修改启动类: ```java package com.example.serviceconsumerfeign; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients /*@EnableHystrix*/ public class ServiceConsumerFeignApplication { public static void main(String[] args) { SpringApplication.run(ServiceConsumerFeignApplication.class, args); } } ``` 注:在配置文件中我们启用了Feign自带的Hystrix,所以即使启动类不写@EnableHystrix,断路器依然会起作用。 下面编写测试Feign的测试接口和API(与之前的教程相同): ```java package com.example.serviceconsumerfeign; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(value = "consumer/feign") public class ConsumerAction { @Autowired private ProducerApi producerApi; @GetMapping(value = "test") public String test(String name) { String producerRes = producerApi.hello(name); String res = "测试consumer/test接口,基于feign调取服务server-producer的hello接口,返回:" + producerRes; System.out.println(res); return res; } } ``` Api类: ```java package com.example.serviceconsumerfeign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; /** * 调用service-producer服务接口 */ @FeignClient(name = "service-producer", fallback = ProducerApiHystrix.class) public interface ProducerApi { @GetMapping(value = "producer/hello/{name}") public String hello(@PathVariable("name") String name); } ``` 看到了没有,上面的`@FeignClient`注解中,我添加了一个fallback属性,它的值对应一个自定义的class——`ProducerApiHystrix`。 下面我们来实现这个class: ```java package com.example.serviceconsumerfeign; import org.springframework.stereotype.Component; /** * ProducerApi对应的断路器 */ @Component public class ProducerApiHystrix implements ProducerApi { @Override public String hello(String name) { return "sorry, " + name + ", this service is unavailable temporarily. We are returning the defaultValue by hystrix."; } } ``` 这就是断路器类,它实际上是`ProducerApi`的实现类,实现了后者需要调用的接口。那么,当请求发生异常、超时等情况时,Hysytix就会使得这个类生效,返回一个默认值。 而如果使用断路器,我们来测试看看: 1. 正常情况: ![file](http://zfh-public-blog.oss-cn-beijing.aliyuncs.com/image-1577286091103.png) 2. 异常情况,比如我们关掉service-producer: ![file](http://zfh-public-blog.oss-cn-beijing.aliyuncs.com/image-1577286108435.png) 而如果不使用断路器,且被调用的服务断了,那么会报异常: `com.netflix.client.ClientException: Load balancer does not have available server for client: service-producer` ------ ## 三、基于Ribbon使用Hystrix 基于ribbon使用hystrix,相比于feign更加简单: 引入依赖`consul-discovery`、`ribbon`、`hystrix`: ``` <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 引入consul-discovery --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <!-- 引入ribbon,用于调用其他服务的接口 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <!-- 健康检查 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- hystrix断路器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` 配置文件不需要修改: ```yaml server: port: 8511 spring: application: name: service-consumer-ribbon cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${server.port} port: ${server.port} service-name: ${spring.application.name} register: true ``` 使用@EnableHystrix注解开启断路器: ```java @SpringBootApplication @EnableDiscoveryClient @EnableHystrix public class ServiceConsumerRibbonApplication { public static void main(String[] args) { SpringApplication.run(ServiceConsumerRibbonApplication.class, args); } /** * 注入RestTemplate Bean,并开启负载均衡 * @return */ @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } ``` 编写测试接口和对应的断路器: ```java package com.example.serviceconsumerribbon; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping(value = "consumer/ribbon") public class ConsumerAction { @Autowired private RestTemplate restTemplate; private static final String service_producer_name = "service-producer"; @GetMapping(value = "test") @HystrixCommand(fallbackMethod = "testHystrix") public String test(String name) { String producerRes = restTemplate.getForObject( "http://" + service_producer_name + "/producer/hello/" + name, String.class); String res = "测试consumer/test接口,基于ribbon调取服务server-producer的hello接口,返回:" + producerRes; System.out.println(res); return res; } /** * test接口的断路器 */ private String testHystrix(String name) { return "sorry, " + name + ", this service is unavailable temporarily. We are returning the defaultValue by hystrix."; } } ``` 我们可以看到,ribbon服务调用中,断路器Hystrix是使用`@HystrixCommand`注解在方法上进行的,对应的属性是`fallBackMethod`,然后我们只要实现这个方法即可。 测试: 1. 正常情况: ![file](http://zfh-public-blog.oss-cn-beijing.aliyuncs.com/image-1577286144257.png) 2. 异常情况,比如关闭service-producer: ![file](http://zfh-public-blog.oss-cn-beijing.aliyuncs.com/image-1577286157182.png) 如果没有开启断路器,而请求未开启服务的接口,就会报错: `java.lang.IllegalStateException: Request URI does not contain a valid hostname: http://service_producer/producer/hello/ye` ------ 参考: > 1. 方志朋-SpringCloud第四篇-断路器(Hystrix)[Finchley版]:[https://blog.csdn.net/forezp/article/details/81040990](https://blog.csdn.net/forezp/article/details/81040990) > 2. 纯洁的微笑-SpringCloud(4)-熔断器(Hystrix):[http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html](http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html) Last modification:December 25, 2019 © Allow specification reprint Support Appreciate the author AliPayWeChat Like 0 请作者喝杯肥宅快乐水吧!