欢迎访问Spring Cloud中国社区

《重新定义Spring Cloud实战》由Spring Cloud中国社区倾力打造,基于Spring Cloud的Finchley.RELEASE版本,本书内容宽度足够广、深度足够深,而且立足于生产实践,直接从生产实践出发,包含大量生产实践的配置。欢迎加微信Software_King进群答疑,国内谁在使用Spring Cloud?欢迎登记

Spring Cloud Feign 中使用Hystrix进行请求降级和快速失败

晏霖 · 16天前 · 290 ·

前言
微服务中经常会用到熔断器来增强服务依赖的稳定性,他可以在网络连接缓慢,资源繁忙,暂时不可用,服务脱机等情况中进行服务的快速失败,并可自我恢复,以避免请求线程的堆积造成大量资源的浪费。

相信读者看这篇文章的目的都是解决实际问题,并不是来看我分析源码的,如果对源码感兴趣的我推荐《重新定义》,所以我们直接上干货,下面我们就简单的利用Feign中集成的Hystrix进行快速失败和请求降级处理。

正文
首先准备一个父项目,里面只有各个服务所需的依赖包,下面就是父项目的pom,可以看到我这次演练的项目有三个,分别是eureka-server,consumer-service,provider-service

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.0.3.RELEASE</version>
  5. </parent>
  1. <modelVersion>4.0.0</modelVersion>
  2. <groupId>cn.springcloud.book</groupId>
  3. <artifactId>ch6-2</artifactId>
  4. <version>0.0.1-SNAPSHOT</version>
  5. <packaging>pom</packaging>
  6. <modules>
  7. <module>ch6-2-eureka-server</module>
  8. <module>ch6-2-consumer-service</module>
  9. <module>ch6-2-provider-service</module>
  10. </modules>
  11. <!-- 管理依赖 -->
  12. <dependencyManagement>
  13. <dependencies>
  14. <dependency>
  15. <groupId>org.springframework.cloud</groupId>
  16. <artifactId>spring-cloud-dependencies</artifactId>
  17. <version>Finchley.RELEASE</version>
  18. <type>pom</type>
  19. <scope>import</scope>
  20. </dependency>
  21. </dependencies>
  22. </dependencyManagement>
  23. <dependencies>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-actuator</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-web</artifactId>
  31. </dependency>
  32. </dependencies>
  33. <!--注意: 这里必须要添加, 否者各种依赖有问题 -->
  34. <repositories>
  35. <repository>
  36. <id>spring-milestones</id>
  37. <name>Spring Milestones</name>
  38. <url>https://repo.spring.io/libs-milestone</url>
  39. <snapshots>
  40. <enabled>false</enabled>
  41. </snapshots>
  42. </repository>
  43. </repositories>
  44. <build>
  45. <plugins>
  46. <plugin>
  47. <groupId>org.springframework.boot</groupId>
  48. <artifactId>spring-boot-maven-plugin</artifactId>
  49. </plugin>
  50. </plugins>
  51. </build>

下面创建eureka-server服务
下面依次是pom,yml,和启动类的代码,这里不做解释了,前几个博客已经介绍过类似的内容。

  1. <parent>
  2. <groupId>cn.springcloud.book</groupId>
  3. <artifactId>ch6-2</artifactId>
  4. <version>0.0.1-SNAPSHOT</version>
  5. </parent>
  6. <artifactId>ch6-2-eureka-server</artifactId>
  7. <properties>
  8. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  9. <java.version>1.8</java.version>
  10. </properties>
  11. <dependencies>
  12. <dependency>
  13. <groupId>org.springframework.cloud</groupId>
  14. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  15. </dependency>
  16. </dependencies>
  17. <build>
  18. <plugins>
  19. <plugin>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-maven-plugin</artifactId>
  22. </plugin>
  23. </plugins>
  24. </build>
  25. server:
  26. port: 8761
  27. eureka:
  28. instance:
  29. hostname: localhost
  30. client:
  31. registerWithEureka: false
  32. fetchRegistry: false
  33. serviceUrl:
  34. defaultZone: http://localhost:8761/eureka/
  1. @SpringBootApplication
  2. @EnableEurekaServer
  3. public class EurekaServerApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(EurekaServerApplication.class, args);
  6. }
  7. }

接下来创建一个被feign调用的服务:provider-service。
这里很简单,就是定一个请求地址,用来映射到feign接口上的URl。

下面代码依次是provider-service中的controller、启动类、pom、yml

这里其实重点就是利用了spring-cloud-starter-netflix-hystrix,别的都没什么可解释的。

  1. @RestController
  2. public class TestController {
  3. @RequestMapping(value = "/getUser",method = RequestMethod.GET)
  4. public String getUser(@RequestParam("username") String username){
  5. return "This is real user";
  6. }
  1. @SpringBootApplication
  2. @EnableDiscoveryClient
  3. public class ProviderApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(ProviderApplication.class, args);
  6. }
  7. }
  1. <parent>
  2. <groupId>cn.springcloud.book</groupId>
  3. <artifactId>ch6-2</artifactId>
  4. <version>0.0.1-SNAPSHOT</version>
  5. </parent>
  6. <artifactId>ch6-2-provider-service</artifactId>
  7. <properties>
  8. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  9. <java.version>1.8</java.version>
  10. </properties>
  11. <dependencies>
  12. <dependency>
  13. <groupId>org.springframework.cloud</groupId>
  14. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework.cloud</groupId>
  18. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  19. </dependency>
  20. </dependencies>
  21. <build>
  22. <plugins>
  23. <plugin>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-maven-plugin</artifactId>
  26. </plugin>
  27. </plugins>
  28. </build>

最后创建一个consumer-service工程,这里的代码相对上两个服务要多一些,不过我会按照一定的顺序给大家展示,

首先是controller、Feign接口,Feign的快速返回/降级方法、pom、yml,一共五块代码

  1. @RestController
  2. public class TestController {
  3. @Autowired
  4. private IUserService userService;
  5. @RequestMapping(value = "/getUser",method = RequestMethod.GET)
  6. public String getUser(@RequestParam("username") String username) throws Exception{
  7. return userService.getUser(username);
  8. }

@FeignClient(name = “sc-provider-service”, fallback = UserServiceFallback.class)
public interface IUserService {

  1. @RequestMapping(value = "/getUser",method = RequestMethod.GET)
  2. String getUser(@RequestParam("username") String username);

}

  1. @Component
  2. public class UserServiceFallback implements IUserService{
  3. /**
  4. * 出错则调用该方法返回友好错误
  5. * @param username
  6. * @return
  7. */
  8. public String getUser(String username){
  9. return "The user does not exist in this system, please confirm username";
  10. }
  11. }
  1. <parent>
  2. <groupId>cn.springcloud.book</groupId>
  3. <artifactId>ch6-2</artifactId>
  4. <version>0.0.1-SNAPSHOT</version>
  5. </parent>
  6. <artifactId>ch6-2-consumer-service</artifactId>
  7. <properties>
  8. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  9. <java.version>1.8</java.version>
  10. </properties>
  11. <dependencies>
  12. <dependency>
  13. <groupId>org.springframework.cloud</groupId>
  14. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework.cloud</groupId>
  18. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework.cloud</groupId>
  22. <artifactId>spring-cloud-starter-openfeign</artifactId>
  23. </dependency>
  24. </dependencies>
  25. <build>
  26. <plugins>
  27. <plugin>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-maven-plugin</artifactId>
  30. </plugin>
  31. </plugins>
  32. </build>
  1. server:
  2. port: 8888
  3. spring:
  4. application:
  5. name: sc-consumer-service
  6. eureka:
  7. client:
  8. serviceUrl:
  9. defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
  10. instance:
  11. prefer-ip-address: true
  12. feign:
  13. hystrix:
  14. enabled: true

好了,五块代码粘贴完毕,下面是对上面代码的解释,

首先controller是我们对外暴露的请求入口,调用了当前服务的Feign接口,重点是这句话@FeignClient(name = “sc-provider-service”, fallback = UserServiceFallback.class),name参数代表请求映射的服务spring. application.name,fallback参数指定的是一个类,这个类必须要实现当前的Feign接口才可以,用于feign调用sc-provider-service服务时失败的快速返回类。然后就是UserServiceFallback类一定注入spring容器。

同样,这个工程也需要spring-cloud-starter-netflix-hystrix 依赖,另外这里我用了openfeign你可以理解为feign的升级版。

还有一点需要注意的是高版本中feign的hystrix是默认关闭的,所有我们要手动打开

三个服务分别启动,首先eureka-service先启动。

我们可以看到消费者工程和生产者工程分别注册上了eureka,我们访问
http://localhost:8888/getUser?username=1

可以看到通过feign访问到了provider-service工程的controller方法了,

下面我们把provider-service工程手动关闭,同样访问http://localhost:8888/getUser?username=1,当然是访问不到的了,但是返回

我们知道这句话是我fallback参数指定类的方法返回的,

这样一个简单的利用feign 集成的熔断器实现快速返回的例子。

注:对本文有异议或不明白的地方微信探讨,wx:15524579896