前言
- 本次服务降级主要演示的是:服务提供者和服务调用者两端的降级演示
环境准备
- EurekaServer集群 搭建地址
- 服务提供者
hystrix-client8011
- 服务调用者
hystrix-feign-client80
服务提供者降级
-
创建
hystrix-client8011
:(SpringBoot项目的创建这里不做介绍) -
更改POM文件
<dependencies> <!-- 引入hystrix依赖包 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--必须添加的依赖--> <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.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
-
添加application.yml
server: port: 8011 #端口号 spring: application: name: hystrix-client-service #服务名称 eureka: client: serviceUrl: defaultZone: http://eurekaServer8761.com:8761/eureka/,http://eurekaServer8762.com:8762/eureka/ #eurekaServer集群地址 instance: instance-id: service8011 prefer-ip-address: true
-
添加主启动类
/** * 主启动类 * @EnableEurekaClient:开启Eureka客户端组件 * @EnableCircuitBreaker:开启Hystrix组件 */ @SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker public class HystrixClientMain8011 { public static void main(String[] args) { SpringApplication.run(HystrixClientMain8011.class,args); } }
-
提供接口及实现类
public interface UserServiceInter { String success(String userId); String timeOut(String userId); }
@Service public class UserServiceInterImpl implements UserServiceInter { @Override public String success(String userId) { return "执行成功!"; } /** * @HystrixCommand:对接口进行降级的注解 * fallbackMethod:指定降级方法 * commandProperties:用于配置降级属性,多个属性用逗号分隔 * @HystrixProperty:降级属性的注解 * name:属性名称,这里以超时为例 * value:最大超时时间,执行时间大于等于它时直接降级 */ @HystrixCommand(fallbackMethod = "fallbackTimeOut", commandProperties = { @HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds", value = "2000") }) @Override public String timeOut(String userId) { try { TimeUnit.MILLISECONDS.sleep(3000); return "执行成功,耗时三秒钟!"; } catch (Exception e) { e.printStackTrace(); } return null; } /** *降级方法 */ public String fallbackTimeOut(String userId){ return "hystrix-client8011:服务器忙请稍后再试!userId="+userId; } }
-
编写Controller
@RestController @Slf4j public class TestController { @Value("${server.port}") private String port; @Autowired private UserServiceInter userServiceInter; @GetMapping(value = "service/success/{userId}") public String success(@PathVariable("userId") String userId){ return userServiceInter.success(userId); } @GetMapping(value = "service/timeOut/{userId}") public String timeOut(@PathVariable("userId") String userId){ return userServiceInter.timeOut(userId); } }
-
访问:
http://127.0.0.1:8011/service/timeOut/123
,可以看到降级的方法执行了
服务调用者客户端降级
-
创建
hystrix-feign-client80
:(SpringBoot项目的创建这里不做介绍) -
更改POM文件
<dependencies> <!-- hystrix核心依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!--openfeign依赖,用于调用服务提供者--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--eureka依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--必须添加的依赖--> <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.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
-
添加application.yml
server: port: 80 #端口号 spring: application: name: hystrix-feign-client-consumption #服务名称(消费者) eureka: client: serviceUrl: defaultZone: http://eurekaServer8761.com:8761/eureka/,http://eurekaServer8762.com:8762/eureka/ ribbon: ReadTimeout: 5000 #建立连接所用的时间,指的是两端连接的时间设置5秒钟 ConnectTimeout: 5000 #建立连接后从服务器读取到资源所用的时间设置5秒钟 feign: hystrix: enabled: true #开启hystrix的支持 logging: level: com.dwj.springcloud.service.UserServiceInter: debug #feign日志以debug级别监控与服务提供者绑定的接口
-
添加主启动类
/** * @EnableFeignClients:开启OpenFeign * @EnableHystrix:开启Hystrix组件 */ @SpringBootApplication @EnableFeignClients @EnableHystrix public class HystrixFeignClientMain80 { public static void main(String[] args) { SpringApplication.run(HystrixFeignClientMain80.class,args); } }
-
新建接口,使用OpenFeign完成对服务提供者的接口绑定,可参考OpenFeign基本介绍
@Component @FeignClient(value = "HYSTRIX-CLIENT-SERVICE") public interface UserServiceInter { @GetMapping(value = "service/success/{userId}") String success(@PathVariable("userId") String userId); @GetMapping(value = "service/timeOut/{userId}") String timeOut(@PathVariable("userId") String userId); }
-
编写Controller
@RestController @Slf4j public class TestController { @Value("${server.port}") private String port; @Autowired private UserServiceInter userServiceInter; @GetMapping(value = "consumption/success/{userId}") public String success(@PathVariable("userId") String userId){ return userServiceInter.success(userId); } /** *设置最大超时时间为一秒钟,当服务接口响应时间大于等于一秒钟时直接降级 */ @HystrixCommand(fallbackMethod = "timeOutFallback", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") }) @GetMapping(value = "consumption/timeOut/{userId}") public String timeOut(@PathVariable("userId") String userId){ return userServiceInter.timeOut(userId); } /** *降级方法 */ public String timeOutFallback(@PathVariable("userId") String userId){ return "hystrix-feign-client80:客户端繁忙请稍后再试userId="+userId; } }
-
修改服务提供者
hystrix-client8011
的降级时间,最大超时时间为五秒钟,方法执行时间为三秒钟所以该服务接口正常/** * @HystrixCommand:对接口进行降级的注解 * fallbackMethod:指定降级方法 * commandProperties:用于配置降级属性,多个属性用逗号分隔 * @HystrixProperty:降级属性的注解 * name:属性名称,这里以超时为例 * value:最大超时时间,执行时间大于等于它时直接降级 */ @HystrixCommand(fallbackMethod = "fallbackTimeOut", commandProperties = { @HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds", value = "5000") }) @Override public String timeOut(String userId) { try { TimeUnit.MILLISECONDS.sleep(3000); return "执行成功,耗时三秒钟!"; } catch (Exception e) { e.printStackTrace(); } return null; }
-
访问:
http://127.0.0.1/consumption/timeOut/1234
,服务提供者接口正常,但调用者客户端自己最大超时时间是一秒钟,所以直接降级
-
注意:在实际服务调用中,一般都是调用者客户端进行降级,很少出现客户端和服务端都降级的情况这里只做演示
全局服务降级
-
上面虽然实现了服务降级,但是如果一个类中有多个方法都需要超时降级,那岂不是要在每个方法头上写@HystrixCommand降级配置
-
解决方案:@DefaultProperties
/** * @DefaultProperties:针对本类的全局降级注解 * defaultFallback:默认全局降级方法 */ @RestController @Slf4j @DefaultProperties(defaultFallback = "defaultFallback", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") }) public class TestController { @Value("${server.port}") private String port; @Autowired private UserServiceInter userServiceInter; @GetMapping(value = "consumption/success/{userId}") public String success(@PathVariable("userId") String userId){ return userServiceInter.success(userId); } @HystrixCommand @GetMapping(value = "consumption/timeOut/{userId}") public String timeOut(@PathVariable("userId") String userId){ return userServiceInter.timeOut(userId); } /** *降级方法 */ public String defaultFallback(){ return "hystrix-feign-client80:客户端繁忙请稍后再试"; } }
-
全局降级以后一个类只需要配置一次降级属性,如果需要针对某个方法进行自定义降级处理,再单独定义即可
-
强调一点:上面虽然配置的是对超时进行降级,但是如果程序出现运行时异常、服务宕机也一样会走降级的方法
通用服务降级
-
虽然全局服务降级可以节省部分代码量,但是降级的逻辑和我们的业务代码耦合度太高
-
解决方法:在OpenFeign的接口绑定上增加fallback属性
/** * fallback:降级类的字节码文件,该类实现了本接口,故完成对每个接口方法的降级 */ @Component @FeignClient(value = "HYSTRIX-CLIENT-SERVICE",fallback = UserServiceFallback.class) public interface UserServiceInter { @GetMapping(value = "service/success/{userId}") String success(@PathVariable("userId") String userId); @GetMapping(value = "service/timeOut/{userId}") String timeOut(@PathVariable("userId") String userId); }
@Component public class UserServiceFallback implements UserServiceInter { @Override public String success(String userId) { return "hystrix-feign-client80:客户端繁忙请稍后再试,success方法"; } @Override public String timeOut(String userId) { return "hystrix-feign-client80:客户端繁忙请稍后再试,timeOut方法"; } }
-
客户端Controller代码恢复
@RestController @Slf4j public class TestController { @Value("${server.port}") private String port; @Autowired private UserServiceInter userServiceInter; @GetMapping(value = "consumption/success/{userId}") public String success(@PathVariable("userId") String userId){ return userServiceInter.success(userId); } @GetMapping(value = "consumption/timeOut/{userId}") public String timeOut(@PathVariable("userId") String userId){ return userServiceInter.timeOut(userId); } }
-
修改
hystrix-client8011
的接口实现类@Service public class UserServiceInterImpl implements UserServiceInter { @Override public String success(String userId) { /** * 模拟一个运行时异常 */ int i = 10/0; return "执行成功!"; }
-
访问:
http://127.0.0.1/consumption/success/123
,客户端直接降级
本文只是Hystrix服务降级简单入门,如有错误请各位读者及时提出。以上案列均参考尚硅谷周阳老师B站视频