Hystrix介绍
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
github地址:https://github.com/Netflix/Hystrix
服务降级
所谓降级,就是当某个服务出现异常之后,服务器将不再被调用,此时服务端可以自己准备一个本地的fallback回调,返回一个缺省值。 这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。
可能出现服务降级的情况:
- 程序运行异常
- 服务超时
- 服务熔断出发服务降级
- 线程池/信号量打满也会导致服务降级
Hystrix使用
项目准备
搭建项目,本章采用的项目框架如下:
服务降级Fallback
1、在服务提供者模块(test-springcloud-provider-payment8008)中,引入Hystrix的依赖:
1 <!-- hystrix --> 2 <dependency> 3 <groupId>org.springframework.cloud</groupId> 4 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 5 </dependency>
完整pom如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>test-springcloud</artifactId> 7 <groupId>com.test</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>test-springcloud-provider-payment8008</artifactId> 13 14 <dependencies> 15 16 <!-- hystrix --> 17 <dependency> 18 <groupId>org.springframework.cloud</groupId> 19 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 20 </dependency> 21 22 <!-- eureka client --> 23 <dependency> 24 <groupId>org.springframework.cloud</groupId> 25 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 26 </dependency> 27 28 <!-- spring boot --> 29 <dependency> 30 <groupId>org.springframework.boot</groupId> 31 <artifactId>spring-boot-starter-web</artifactId> 32 </dependency> 33 <dependency> 34 <groupId>org.springframework.boot</groupId> 35 <artifactId>spring-boot-starter-actuator</artifactId> 36 </dependency> 37 38 <dependency> 39 <groupId>org.springframework.boot</groupId> 40 <artifactId>spring-boot-devtools</artifactId> 41 <scope>runtime</scope> 42 <optional>true</optional> 43 </dependency> 44 45 <dependency> 46 <groupId>org.projectlombok</groupId> 47 <artifactId>lombok</artifactId> 48 <optional>true</optional> 49 </dependency> 50 <dependency> 51 <groupId>org.springframework.boot</groupId> 52 <artifactId>spring-boot-starter-test</artifactId> 53 <scope>test</scope> 54 </dependency> 55 56 </dependencies> 57 58 <build> 59 <finalName>test-springcloud-provider-payment8008</finalName> 60 </build> 61 </project>
2、配置文件如下:
1 # 端口 2 server: 3 port: 8008 4 5 spring: 6 application: 7 name: cloud-payment-service 8 9 eureka: 10 client: 11 service-url: 12 defaultZone: http://localhost:8761/eureka 13 instance: 14 # instance: 15 instance-id: ${spring.cloud.client.ip-address}:${server.port} 16 # 访问路径可以显示IP地址 17 prefer-ip-address: true
3、编辑启动类,使用注解@EnableCircuitBreaker,允许断路器
1 @SpringBootApplication 2 @EnableEurekaClient 3 //允许断路器 4 @EnableCircuitBreaker 5 public class PaymentMain8008 { 6 public static void main(String[] args) { 7 SpringApplication.run(PaymentMain8008.class, args); 8 } 9 }
4、编辑业务类,并在方法上使用@HystrixCommand注解,表明方法支持服务降级,且设置了超时的降级策略
1 @Service 2 public class PaymentService { 3 4 public String paymentInfo_OK(Integer id) { 5 return "线程池:" + Thread.currentThread().getName() 6 + ",paymentInfo_OK,ID == " + id; 7 } 8 9 10 // fallbackMethod: 设置HystrixCommand服务降级所使用的方法名称,注意该方法需要与原方法定义在同一个类中,并且方法签名也要一致 11 // commandProperties: 设置HystrixCommand属性,如:断路器失败百分比、断路器时间容器大小等 12 // 设置断路器超时降级策略,时间3000毫秒超时 13 @HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler", commandProperties = { 14 @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") 15 }) 16 public String paymentInfo_Timeout(Integer id) { 17 int second = 5000; 18 try { 19 // 休眠5000毫秒 20 TimeUnit.MILLISECONDS.sleep(second); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 // 异常 25 // int n = 10/0; 26 return "线程池:" + Thread.currentThread().getName() 27 + ",paymentInfo_Timeout,ID == " + id 28 + ",耗时" + second + "毫秒"; 29 } 30 31 public String paymentInfo_TimeoutHandler(Integer id) { 32 String result = "线程池:" + Thread.currentThread().getName() 33 + ",paymentInfo_TimeoutHandler,ID == " + id; 34 return result; 35 } 36 }
5、编辑controller
1 @RestController 2 @Slf4j 3 public class PaymentController { 4 5 @Autowired 6 private PaymentService paymentService; 7 8 @Value("${server.port}") 9 private String serverPort; 10 11 @GetMapping(value = "/payment/hystrix/ok/{id}") 12 public String paymentInfo_OK(@PathVariable("id") Integer id) { 13 String result = paymentService.paymentInfo_OK(id); 14 log.info("result===" + result); 15 return result; 16 } 17 18 @GetMapping(value = "/payment/hystrix/timeout/{id}") 19 public String paymentInfo_Timeout(@PathVariable("id") Integer id) { 20 String result = paymentService.paymentInfo_Timeout(id); 21 log.info("result===" + result); 22 return result; 23 } 24 25 }
6、测试,启动注册中心,以及服务提供者项目
访问地址:http://localhost:8008/payment/hystrix/ok/1,正常响应
访问地址:http://localhost:8008/payment/hystrix/timeout/1,服务降级
同时,可以修改业务类中paymentInfo_Timeout方法,让此方法报异常,然后进行访问,发现一样会服务降级
全局服务降级DefaultProperties
1、在服务消费者模块(test-springcloud-order7996)中,引入Hystrix的依赖:
完整pom如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>test-springcloud</artifactId> 7 <groupId>com.test</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>test-springcloud-order7996</artifactId> 13 14 <dependencies> 15 16 <!-- hystrix --> 17 <dependency> 18 <groupId>org.springframework.cloud</groupId> 19 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 20 </dependency> 21 22 <!-- openfeign --> 23 <dependency> 24 <groupId>org.springframework.cloud</groupId> 25 <artifactId>spring-cloud-starter-openfeign</artifactId> 26 </dependency> 27 28 <!-- eureka client --> 29 <dependency> 30 <groupId>org.springframework.cloud</groupId> 31 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 32 </dependency> 33 34 <!-- spring boot --> 35 <dependency> 36 <groupId>org.springframework.boot</groupId> 37 <artifactId>spring-boot-starter-web</artifactId> 38 </dependency> 39 <dependency> 40 <groupId>org.springframework.boot</groupId> 41 <artifactId>spring-boot-starter-actuator</artifactId> 42 </dependency> 43 44 <dependency> 45 <groupId>org.springframework.boot</groupId> 46 <artifactId>spring-boot-devtools</artifactId> 47 <scope>runtime</scope> 48 <optional>true</optional> 49 </dependency> 50 51 <dependency> 52 <groupId>org.projectlombok</groupId> 53 <artifactId>lombok</artifactId> 54 <optional>true</optional> 55 </dependency> 56 <dependency> 57 <groupId>org.springframework.boot</groupId> 58 <artifactId>spring-boot-starter-test</artifactId> 59 <scope>test</scope> 60 </dependency> 61 62 </dependencies> 63 64 <build> 65 <finalName>test-springcloud-order7996</finalName> 66 </build> 67 </project>
2、编辑配置文件如下:
1 # 端口 2 server: 3 port: 7996 4 5 spring: 6 application: 7 name: cloud-order 8 9 eureka: 10 client: 11 service-url: 12 defaultZone: http://localhost:8761/eureka 13 instance: 14 # instance: 15 instance-id: ${spring.cloud.client.ip-address}:${server.port} 16 # 访问路径可以显示IP地址 17 prefer-ip-address: true
3、编辑启动类
使用注解@EnableFeignClients,启动openfeign
使用注解@EnableHystrix,启用Hystrix,查看注解@EnableHystrix,里面也包含了注解@EnableCircuitBreaker
1 @SpringBootApplication 2 @EnableEurekaClient 3 @EnableFeignClients 4 // 启动Hystrix 5 @EnableHystrix 6 public class OrderMain7996 { 7 public static void main(String[] args) { 8 SpringApplication.run(OrderMain7996.class, args); 9 } 10 }
4、编辑一个接口,并使用@FeignClient注解,表明一个是openfeign客户端
1 @Component 2 // openfeign客户端 3 @FeignClient(value = "CLOUD-PAYMENT-SERVICE") 4 public interface PaymentHystrixService { 5 6 @GetMapping(value = "/payment/hystrix/ok/{id}") 7 public String paymentInfo_OK(@PathVariable("id") Integer id); 8 9 @GetMapping(value = "/payment/hystrix/timeout/{id}") 10 public String paymentInfo_Timeout(@PathVariable("id") Integer id); 11 }
5、编写一个controller,内容如下
1)类上需要使用@DefaultProperties(defaultFallback = "paymentClobalFallbackMethod"),定义一个默认的回调方法
2)paymentTimeout方法上需要使用@HystrixCommand 支持服务降级
1 @RestController 2 @Slf4j 3 @DefaultProperties(defaultFallback = "paymentClobalFallbackMethod") 4 public class OrderHystrixController { 5 @Autowired 6 private PaymentHystrixService paymentHystrixService; 7 8 @GetMapping(value = "/consumer/payment/hystrix/ok/{id}") 9 public String paymentInfo_OK(@PathVariable("id") Integer id) { 10 String result = paymentHystrixService.paymentInfo_OK(id); 11 log.info("result===" + result); 12 return result; 13 } 14 // @HystrixCommand(fallbackMethod = "paymentTimeoutFallbackMethod", commandProperties = { 15 // @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500") 16 // }) 17 @GetMapping(value = "/consumer/payment/hystrix/timeout/{id}") 18 // @HystrixCommand 支持服务降级 19 @HystrixCommand 20 public String paymentTimeout(@PathVariable("id") Integer id) { 21 int n = 10/0; 22 String result = paymentHystrixService.paymentInfo_Timeout(id); 23 log.info("result===" + result); 24 return result; 25 } 26 27 public String paymentTimeoutFallbackMethod(@PathVariable("id") Integer id) { 28 String result = "消费者:对方支付系统繁忙,请稍后再试,ID == " + id; 29 log.info("result===" + result); 30 return result; 31 } 32 33 }
6、测试
1)启动Eureka注册中心,关闭服务提供者项目,启动服务消费者模块(test-springcloud-order7996)
2)访问地址:http://localhost:7996/consumer/payment/hystrix/ok/1,不支持服务降级
3)访问地址:http://localhost:7996/consumer/payment/hystrix/timeout/1,支持服务降级
通配服务降级
1、在以上项目的基础上,新增接口实现类PaymentFallbackService,实现接口PaymentHystrixService
1 @Component 2 public class PaymentFallbackService implements PaymentHystrixService { 3 public String paymentInfo_OK(Integer id) { 4 return "PaymentFallbackService——》paymentInfo_OK——》统一处理:" + id; 5 } 6 7 public String paymentInfo_Timeout(Integer id) { 8 return "PaymentFallbackService——》paymentInfo_Timeout——》统一处理:" + id; 9 } 10 }
2、修改接口PaymentHystrixService的配置,如下:
1 @Component 2 // openfeign客户端 3 // fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑, 4 // fallback指定的类必须实现@FeignClient标记的接口 5 @FeignClient(value = "CLOUD-PAYMENT-SERVICE", fallback = PaymentFallbackService.class) 6 public interface PaymentHystrixService {
3、测试
1)启动Eureka注册中心,关闭服务提供者项目,启动服务消费者模块(test-springcloud-order7996)
2)访问地址:http://localhost:7996/consumer/payment/hystrix/ok/1,支持服务降级,且是通配服务降级的返回内容
3)访问地址:http://localhost:7996/consumer/payment/hystrix/timeout/1,支持服务降级,返回的是默认全局的服务降级内容