SpringCloud课程:15.Hystrix断路器简介 与 服务降级

Hystrix断路器

一、概述

分布式系统面临的问题

复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候不可避免地失败。

服务雪崩

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的 “扇出效应”  如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓“雪崩效应”

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的,可处理的备选响应(Fallback).而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用发的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩

 

1.    作用

服务降级
服务熔断
接近实时的监控

2.    现状:停更进维

官网:https://github.com/Netflix/Hystrix/wiki/How-To-Use

Hystrix官宣,停更进维

Hystrix is no longer in active development, and is currently in maintenance mode.

二、Hystrix重要概念

1.    服务降级 FallBack

        向调用方返回一个复合预期的可处理的备选响应,如 “服务器忙,请稍后再试!”不让客户等待并立刻返回一个友好提示。FallBack
哪些情况会触发降级

  •             程序运行异常
  •             超时
  •             服务熔断触发服务降级
  •             线程池/信号量打满也会导致服务降级

2.    服务熔断   Break         

        类似保险丝达到最大服务访问后,直接拒绝访问,然后用服务降级的方法返回友好提示。
        就是保险丝->服务降级-进而熔断-恢复调用链路

3.    服务限流  Flowlimit

        秒杀高并发等操作,严禁拥挤,排队1秒N个,有序进行

三、Hystrix 案例

1.构建

1.1 新建module

cloud-provider-hystrix-payment8001

1.2 pom

需引入 spring-cloud-starter-netflix-hystrix 和 Eureka

1 <dependencies> 2 <dependency> 3 <groupId>org.springframework.cloud</groupId> 4 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 5 </dependency> 6 <!--eureka-client--> 7 <dependency> 8 <groupId>org.springframework.cloud</groupId> 9 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 10 </dependency> 11 12 <dependency> 13 <groupId>org.springframework.boot</groupId> 14 <artifactId>spring-boot-starter-web</artifactId> 15 </dependency> 16 <dependency> 17 <groupId>org.springframework.boot</groupId> 18 <artifactId>spring-boot-starter-actuator</artifactId> 19 </dependency> 20 21 22 <dependency> 23 <groupId>org.springframework.boot</groupId> 24 <artifactId>spring-boot-devtools</artifactId> 25 <scope>runtime</scope> 26 <optional>true</optional> 27 </dependency> 28 <dependency> 29 <groupId>org.projectlombok</groupId> 30 <artifactId>lombok</artifactId> 31 <optional>true</optional> 32 </dependency> 33 <dependency> 34 <groupId>org.springframework.boot</groupId> 35 <artifactId>spring-boot-starter-test</artifactId> 36 <scope>test</scope> 37 </dependency> 38 </dependencies>

1.3 yml

1server: 2 port: 8001 3spring: 4 application: 5 name: cloud-provider-hystrix-payment 6eureka: 7 client: 8 register-with-eureka: true 9 fetchRegistry: true 10 service-url: 11 defaultZone: http://eureka7001.com:7001/eureka

1.4 main

1@SpringBootApplication 2@EnableEurekaClient 3public class HystrixPaymentMain8001 { 4 public static void main(String[] args) { 5 SpringApplication.run(HystrixPaymentMain8001.class,args); 6 } 7}

1.5 service

1@Service 2public class PaymentService { 3 /*正常访问OK的*/ 4 public String paymentInfo_OK(Integer id){ 5 return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,ID: "+id; 6 } 7 public String paymentInfo_Timeout(Integer id) { 8 try{ 9 TimeUnit.SECONDS.sleep(3);} 10 catch ( InterruptedException e){ 11 e.printStackTrace(); 12 } 13 return "线程池:"+Thread.currentThread().getName()+" paymentInfo_Timeout,ID:"+id; 14 } 15}

1.6 controller

1@RestController 2@Slf4j 3public class PaymentController { 4 @Resource 5 private PaymentService paymentService; 6 @Value("${server.port}") 7 private String serverPort; 8 9 @GetMapping("/payment/hystrix/ok/{id}") 10 public String paymentInfo_OK(@PathVariable("id")Integer id){ 11 String result=paymentService.paymentInfo_OK(id); 12 log.info("****result:"+result); 13 return result; 14 } 15 @GetMapping("/payment/hystrix/timeout/{id}") 16 public String paymentInfo_TimeOut(@PathVariable("id")Integer id){ 17 String result=paymentService.paymentInfo_Timeout(id); 18 log.info("****result:"+result); 19 return result; 20 } 21 22 23}

1.7 启动本服务 和 Eureka服务

1.8 查看正常访问耗时

3000ms 

不到10ms

2.Jmeter高并发测试8001

2.1 新建线程组

2.2 开启压测后 两个接口耗时明显增多

开启压测后查看耗时

ok:1700ms

timeout:  4200ms

tomcat的默认工作线程数被打满了,没有多余的线程来分解压力和处理。

2.3 结论

上面是服务提供者自测,如果消费者访问,只能干等,最终导致消费端80不满意,服务端8001直接被拖死。

3.新建消费者80 使用消费者测试接口

3.1. 新建module cloud-consumer-feign-hystrix-order80

3.2 pom

1 <dependencies> 2 <!--open feign--> 3 <dependency> 4 <groupId>org.springframework.cloud</groupId> 5 <artifactId>spring-cloud-starter-openfeign</artifactId> 6 </dependency> 7 <!--eureka-client--> 8 <dependency> 9 <groupId>org.springframework.cloud</groupId> 10 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 11 </dependency> 12 <dependency> 13 <groupId>com.zhl.springcloud</groupId> 14 <artifactId>cloud-api-commons</artifactId> 15 <version>${project.version}</version> 16 </dependency> 17 18 <dependency> 19 <groupId>org.springframework.boot</groupId> 20 <artifactId>spring-boot-starter-web</artifactId> 21 </dependency> 22 <dependency> 23 <groupId>org.springframework.boot</groupId> 24 <artifactId>spring-boot-starter-actuator</artifactId> 25 </dependency> 26 <dependency> 27 <groupId>org.springframework.boot</groupId> 28 <artifactId>spring-boot-devtools</artifactId> 29 <scope>runtime</scope> 30 <optional>true</optional> 31 </dependency> 32 <dependency> 33 <groupId>org.projectlombok</groupId> 34 <artifactId>lombok</artifactId> 35 <optional>true</optional> 36 </dependency> 37 <dependency> 38 <groupId>org.springframework.boot</groupId> 39 <artifactId>spring-boot-starter-test</artifactId> 40 <scope>test</scope> 41 </dependency> 42 </dependencies>

3.3. yml

1server: 2 port: 80 3 4spring: 5 application: 6 name: cloud-consumer-feign-hystrix-order80 7 8eureka: 9 client: 10 register-with-eureka: true 11 fetchRegistry: true 12 service-url: 13 defaultZone: http://eureka7001.com:7001/eureka

3.4 main

1@SpringBootApplication 2@EnableFeignClients 3public class FeignHystixOrderMainF80 { 4 public static void main(String[] args) { 5 SpringApplication.run(FeignHystixOrderMainF80.class,args); 6 } 7}

3.5 Service

com.zhl.springcloud.service.PaymentHystrixService

1@Component 2@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT") 3public interface PaymentHystrixService { 4 @GetMapping("/payment/hystrix/ok/{id}") 5 public String paymentInfo_OK(@PathVariable("id")Integer id); 6 @GetMapping("/payment/hystrix/timeout/{id}") 7 public String paymentInfo_TimeOut(@PathVariable("id")Integer id); 8}

3.6 Controller

com.zhl.springcloud.controller.OrderHystrixController

1@RestController 2@Slf4j 3public class OrderHystrixController { 4 @Resource 5 PaymentHystrixService paymentHystrixService; 6 7 @GetMapping("/consumer/payment/hystrix/ok/{id}") 8 public String paymentInfo_OK(@PathVariable("id")Integer id){ 9 return paymentHystrixService.paymentInfo_OK(id); 10 } 11 @GetMapping("/consumer/payment/hystrix/timeout/{id}") 12 public String paymentInfo_TimeOut(@PathVariable("id")Integer id){ 13 return paymentHystrixService.paymentInfo_TimeOut(id); 14 } 15}

3.7 调用测试

测试OK接口 耗时8ms

开启JMeter进行压测后,再访问

4.故障现象和导致原因

8001 访问繁忙,客户端相应缓慢。

5.结论

正因为有上述故障或不佳表现,才有 降级、容错、限流技术诞生。

6.如何解决?解决的需求

  • 服务提供者超时、服务器变慢:消费者不能一直等待,必须有服务降级
  • 服务提供者出错(宕机或程序运行出错):消费者不能一直等待,必须有服务降级
  • 消费者等待时间小于服务提供者运行时间,消费者自己处理降级。

7. 服务降级

7.1.降级配置

    @HystrixCommand

7.2 服务提供者分析

    设置自身调用超时时间峰值,峰值内可以正常运行超过了需要有兜底的方法处理,作服务降级fallback

7.3 服务提供者8001服务降级代码优化

    一旦调用服务方法失败并抛出了错误信息后会自动调用@HystrixCommand标注好的fallbackMethod调用类中指定的方法

    超时 或 降级 都由fallbackMethod处理

    使用@HystrixCommand 注解 标注超时处理方法,

7.4 服务提供者8001服务降级优化

7.4.1 Service:

    com.zhl.springcloud.service.PaymentService

1@Service 2public class PaymentService { 3 /*正常访问OK的*/ 4 public String paymentInfo_OK(Integer id){ 5 return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,ID: "+id; 6 } 7 8 /*超时 或 降级 都由fallbackMethod处理*/ 9 @HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = { 10 //正常超时时间3秒,超时执行fallbackMethod 11 @HystrixProperty( name="execution.isolation.thread.timeoutInMilliseconds",value = "3000") 12 }) 13 public String paymentInfo_Timeout(Integer id) { 14 int timenumner=5; 15 try{ 16 TimeUnit.SECONDS.sleep(timenumner);} 17 catch ( InterruptedException e){ 18 e.printStackTrace(); 19 } 20 return "线程池:"+Thread.currentThread().getName()+" paymentInfo_Timeout,ID:"+id+"\t"+"耗时:"+timenumner; 21 } 22 /*降级处理*/ 23 public String paymentInfo_TimeoutHandler(Integer id){ 24 return "线程池:"+Thread.currentThread().getName()+" paymentInfo_Timeout,ID:"+id+"\t"+"降级处理"; 25 } 26}

7.4.2 主启动类激活 添加新注解 @EnableCircuitBreaker

1@SpringBootApplication 2@EnableEurekaClient 3@EnableCircuitBreaker 4public class HystrixPaymentMain8001 { 5 public static void main(String[] args) { 6 SpringApplication.run(HystrixPaymentMain8001.class,args); 7 } 8}

7.4.3 超时降级测试

服务需要5秒,3秒返回降级信息。

7.4.4 异常测试

修改业务类

1 /*超时 或 降级 都由fallbackMethod处理*/ 2 @HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = { 3 //正常超时时间3秒,超时执行fallbackMethod 4 @HystrixProperty( name="execution.isolation.thread.timeoutInMilliseconds",value = "3000") 5 }) 6 public String paymentInfo_Timeout(Integer id) { 7 int timenumner=5; 8 /*测试报错*/ 9 int age=10/0; 10 try{ 11 TimeUnit.SECONDS.sleep(timenumner);} 12 catch ( InterruptedException e){ 13 e.printStackTrace(); 14 } 15 return "线程池:"+Thread.currentThread().getName()+" paymentInfo_Timeout,ID:"+id+"\t"+"耗时:"+timenumner; 16 } 17 /*降级处理*/ 18 public String paymentInfo_TimeoutHandler(Integer id){ 19 return "线程池:"+Thread.currentThread().getName()+" 系统繁忙或运行保存,请稍后再试,ID:"+id+"\t"+"降级处理"; 20 }

访问测试:

7.5 消费者服务80 服务降级

7.5.1 YML 配置OpenFeign 支持 Hystrix

1server: 2 port: 80 3 4spring: 5 application: 6 name: cloud-consumer-feign-hystrix-order80 7eureka: 8 client: 9 service-url: 10 defaultZone: http://eureka7001.com:7001/eureka 11#OpenFeign开启Hystrix 12feign: 13 hystrix: 14 enabled: true

7.5.2 Main 开启熔断器@EnableCircuitBreaker

1@SpringBootApplication 2@EnableFeignClients 3@EnableCircuitBreaker 4public class FeignHystixOrderMainF80 { 5 public static void main(String[] args) { 6 SpringApplication.run(FeignHystixOrderMainF80.class,args); 7 } 8}

7.5.3 Controller

com.zhl.springcloud.controller.OrderHystrixController

1@RestController 2@Slf4j 3public class OrderHystrixController { 4 @Resource 5 PaymentHystrixService paymentHystrixService; 6 7 @GetMapping("/consumer/payment/hystrix/ok/{id}") 8 public String paymentInfo_OK(@PathVariable("id")Integer id){ 9 return paymentHystrixService.paymentInfo_OK(id); 10 } 11 /*1.5秒超时*/ 12 @GetMapping("/consumer/payment/hystrix/timeout/{id}") 13 @HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = { 14 @HystrixProperty( name="execution.isolation.thread.timeoutInMilliseconds",value = "1500") 15 }) 16 public String paymentInfo_TimeOut(@PathVariable("id")Integer id){ 17 return paymentHystrixService.paymentInfo_TimeOut(id); 18 } 19 /*降级处理*/ 20 public String paymentInfo_TimeoutHandler(Integer id){ 21 return "我是消费者80,对方支付系统繁忙,请10秒钟后再试或者自己运行出错请检查自己 (╥╯^╰╥)"; 22 } 23}

7.5.4 服务提供者业务代码 5秒超时,3秒业务处理时间

1@Service 2public class PaymentService { 3 /*正常访问OK的*/ 4 public String paymentInfo_OK(Integer id){ 5 return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,ID: "+id; 6 } 7 8 /*超时 或 降级 都由fallbackMethod处理*/ 9 @HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = { 10 //正常超时时间3秒,超时执行fallbackMethod 11 @HystrixProperty( name="execution.isolation.thread.timeoutInMilliseconds",value = "5000") 12 }) 13 public String paymentInfo_Timeout(Integer id) { 14 int timenumner=3; 15 /*测试报错*/ 16 try{ 17 TimeUnit.SECONDS.sleep(timenumner);} 18 catch ( InterruptedException e){ 19 e.printStackTrace(); 20 } 21 return "线程池:"+Thread.currentThread().getName()+" paymentInfo_Timeout,ID:"+id+"\t"+"耗时:"+timenumner; 22 } 23 /*降级处理*/ 24 public String paymentInfo_TimeoutHandler(Integer id){ 25 return "线程池:"+Thread.currentThread().getName()+" 系统繁忙或运行保存,请稍后再试,ID:"+id+"\t"+"降级处理"; 26 } 27}

7.5.5 超时测试效果 提供者3秒 消费者1.5秒

7.5.6 消费者自己出错测试

int age=10/0 测试出错

1 /*1.5秒超时*/ 2 @GetMapping("/consumer/payment/hystrix/timeout/{id}") 3 @HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = { 4 @HystrixProperty( name="execution.isolation.thread.timeoutInMilliseconds",value = "1500") 5 }) 6 public String paymentInfo_TimeOut(@PathVariable("id")Integer id){ 7 int age=10/0; 8 return paymentHystrixService.paymentInfo_TimeOut(id); 9 } 10 /*降级处理*/ 11 public String paymentInfo_TimeoutHandler(Integer id){ 12 return "我是消费者80,对方支付系统繁忙,请10秒钟后再试或者自己运行出错请检查自己, (╥╯^╰╥)"; 13 }

7.6 服务降级全局配置

目前每个业务对应一个兜底的方法,代码膨胀,需要统一和自定义分开。

7.6.1 Controller级别配置

    在Controller加@DefaultPropertie注解,指定控制器内全局处理方法 .

com.zhl.springcloud.controller.OrderHystrixController

     在接口上只引用@HystrixCommand ,不用指定fallbackMethod. 

    定义一个Controller全局的处理方法,这里为ControllerGlobalFallBack

注意:

接口级别的Fallback Method需要参数与返回类型与接口一致。

类级别的 FallbackMethod 这里为ControllerGlobalFallBack 不能有参数,否则报错 fallback method wasn't found:

1@RestController 2@Slf4j 3/*Controller级别默认处理方法*/ 4@DefaultProperties(defaultFallback = "ControllerGlobalFallBack") 5public class OrderHystrixController { 6 @Resource 7 PaymentHystrixService paymentHystrixService; 8 9 @GetMapping("/consumer/payment/hystrix/ok/{id}") 10 public String paymentInfo_OK(@PathVariable("id")Integer id){ 11 return paymentHystrixService.paymentInfo_OK(id); 12 } 13 /*1.5秒超时*/ 14 @GetMapping("/consumer/payment/hystrix/timeout/{id}") 15 /*自定义指定处理方法*/ 16 /*@HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = { 17 @HystrixProperty( name="execution.isolation.thread.timeoutInMilliseconds",value = "1500") 18 })*/ 19 @HystrixCommand 20 public String paymentInfo_TimeOut(@PathVariable("id")Integer id){ 21 //int age=10/0; 22 return paymentHystrixService.paymentInfo_TimeOut(id); 23 } 24 /*降级处理*/ 25 public String paymentInfo_TimeoutHandler(Integer id){ 26 return "我是消费者80,对方支付系统繁忙,请10秒钟后再试或者自己运行出错请检查自己, (╥╯^╰╥)"; 27 } 28 /*Controller全局FallBack处理*/ 29 public String ControllerGlobalFallBack(){ 30 return "ControllerGlobalFallBack"; 31 } 32}

7.6.2 服务调用接口统一配置

新建一个类 实现FeignClient所在接口

1@Component 2public class HystrixPaymentFallBackService implements PaymentHystrixService { 3 @Override 4 public String paymentInfo_OK(Integer id) { 5 return "---HystrixPaymentServiceImpl_paymentInfo_OK fallback "; 6 } 7 8 @Override 9 public String paymentInfo_TimeOut(Integer id) { 10 return "----HystrixPaymentServiceImpl_paymentInfo_TimeOut fallback"; 11 } 12}

YML开启Feign

1#OpenFeign开启Hystrix 2feign: 3 hystrix: 4 enabled: true

controller 中取消 @HystrixCommand 的注解

1@RestController 2@Slf4j 3/*Controller级别默认处理方法*/ 4//@DefaultProperties(defaultFallback = "ControllerGlobalFallBack") 5public class OrderHystrixController { 6 @Resource 7 PaymentHystrixService paymentHystrixService; 8 9 @GetMapping("/consumer/payment/hystrix/ok/{id}") 10 public String paymentInfo_OK(@PathVariable("id")Integer id){ 11 return paymentHystrixService.paymentInfo_OK(id); 12 } 13 /*1.5秒超时*/ 14 @GetMapping("/consumer/payment/hystrix/timeout/{id}") 15 /*自定义指定处理方法*/ 16 /*@HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = { 17 @HystrixProperty( name="execution.isolation.thread.timeoutInMilliseconds",value = "1500") 18 })*/ 19 /* @HystrixCommand*/ 20 public String paymentInfo_TimeOut(@PathVariable("id")Integer id){ 21 //int age=10/0; 22 return paymentHystrixService.paymentInfo_TimeOut(id); 23 } 24 /*降级处理*/ 25 public String paymentInfo_TimeoutHandler(Integer id){ 26 return "我是消费者80,对方支付系统繁忙,请10秒钟后再试或者自己运行出错请检查自己, (╥╯^╰╥)"; 27 } 28 /*Controller全局FallBack处理*/ 29 public String ControllerGlobalFallBack(){ 30 return "ControllerGlobalFallBack"; 31 } 32}

访问timeout接口测试

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Nebula Graph 在微众银行的实践

本文为微众银行大数据平台工程师——周可在nMeetup深圳场的演讲文字稿,演讲视频参见:B站(戳「阅读原文」观看)!(https://oscimg.oschina.net/oscnet/884d81502c744b76be00eb2a1b07d3c7.png)自我介绍下,我是微众银行大数据平台的工程

Nepxion Discovery 5.5.0 发布

!(https://oscimg.oschina.net/oscnet/f81c043194ef4732880459d00c1a720e.png)发布日志功能更新:增加基于Opentracing调用链的支持,目前支持UberJaeger,实现在SpringCloudGateway、Zuul和服务上的灰度