Spring Cloud:服务熔断Hystrix

深碍√TFBOYSˉ_ 2024-04-21 08:09 134阅读 0赞

✨ Spring Cloud:服务熔断Hystrix

  • 服务容错的核心知识
    • 雪崩效应
    • 服务隔离
    • 熔断降级
    • 服务限流
  • Hystrix介绍
  • Hystrix案例
    • 案例搭建
      • 创建新模块cloud-provider-hystrix-payment8001
      • 添加依赖
      • application.yml
      • 主启动类
      • Service及其实现类
      • Controller
      • 正常测试
    • 高并发测试
      • 使用Jmeter进行测试
      • 创建新模块cloud-consumer-feign-hystrix-order80
      • 测试
      • 测试结论 & 解决方法
    • 服务降级
      • 服务提供者8001降级
      • 服务消费者80降级
      • 默认的fallback
      • 服务降级处理的实现类
    • 服务熔断
      • 熔断机制介绍
      • 熔断状态
      • Hystrix熔断机制的实现
      • 代码实现
          • image.png
    • 服务监控:hystrixDashboard
      • 基本介绍
      • 搭建 Hystrix Dashboard,监控其他模块的运行情况。
        • 新建模块cloud-consumer-hystrix-dashboard9001
        • pom依赖
        • application.yml
        • 主启动类
        • 所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置
        • 启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001
      • 断路器演示
        • 修改cloud-provider-hystrix-payment8001
        • 监控测试
          • 1) 启动1个eureka
          • 2) 观察监控窗口

?个人主页:不断前进的皮卡丘
?博客描述:梦想也许遥不可及,但重要的是追梦的过程,用博客记录自己的成长,记录自己一步一步向上攀登的印记
?个人专栏:微服务专栏

服务容错的核心知识

雪崩效应

在微服务架构中,一个请求需要调用多个服务是非常常见的。如客户端访问A服务,而A服务需要调用B 服务,B服务需要调用C服务,由于网络原因或者自身的原因,如果B服务或者C服务不能及时响应,A服务将处于阻塞状态,直到B服务C服务响应。此时若有大量的请求涌入,容器的线程资源会被消耗完毕, 导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难 性的严重后果,这就是服务故障的“雪崩”效应
在这里插入图片描述

雪崩是系统中的蝴蝶效应导致其发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方
法响应变慢,亦或是某台机器的资源耗尽。从源头上我们无法完全杜绝雪崩源头的发生,但是雪崩的根
本原因来源于服务之间的强依赖,所以我们可以提前评估,做好熔断,隔离,限流。

服务隔离

它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。
当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体
的系统服务。

熔断降级

熔断和降级的真实关系,图文并茂,看完秒懂
在这里插入图片描述

熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。 在互联网 系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整 体的可用性,可以暂时切断对下游服务的调用。这 种牺牲局部,保全整体的措施就叫做熔断.例如你的A服务里面的一个功能依赖B服务,这时候B服务出问题了,返回的很慢。这种情况可能会因为这么一个功能而拖慢了A服务里面的所有功能,因此我们这时候就需要熔断!即当发现A要调用这B时就直接返回错误(或者返回其他默认值啊啥的),就不去请求B了。

降级也就是服务降级,当我们的服务器压力剧增,为了保证核心功能的可用性,可以选择性的降低一些功能的可用性,或者直接关闭该功能。典型的弃车保帅!服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示 **就比如贴吧类型的网站,当服务器吃不消的时候,可以选择把发帖功能关闭,注册功能关闭,改密码,改头像这些都关了,为了确保登录和浏览帖子这种核心的功能。

哪些情况会触发降级
1.程序运行异常
2.超时自动降级
3.服务熔断触发服务降级
4.线程池/信号量打满也会导致服务降级
5.人工降级

:::warning
降级一般而言是我们自身的系统出现了故障而降级。而熔断一般是指依赖的外部接口出现故障,断绝和外部接口之间的关联。
:::

服务限流

服务限流
限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说 系统的吞吐量是可以被测算的,为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流 量并采取少量措施以完成限制流量的目的。比方:推迟解决,拒绝解决,或者者部分拒绝解决等等。

Hystrix介绍

在这里插入图片描述

Hystrix是由Netflflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失 败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

  • 包裹请求:使用HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用 了设计模式中的“命令模式”。
  • 跳闸机制:当某服务的错误率超过一定的阈值时,Hystrix可以自动或手动跳闸,停止请求该服务 一段时间。
  • 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,

发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝 的请求等。

  • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员 自行提供,例如返回一个缺省值。
  • 自我修复:断路器打开一段时间后,会自动进入“半开”状态。

Hystrix案例

案例搭建

创建新模块cloud-provider-hystrix-payment8001

添加依赖

  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>cloud2022</artifactId>
  7. <groupId>com.zyh.springcloud</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>cloud-provider-hystrix-payment8001</artifactId>
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. </properties>
  16. <dependencies>
  17. <!--新增hystrix-->
  18. <dependency>
  19. <groupId>org.springframework.cloud</groupId>
  20. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework.cloud</groupId>
  24. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  25. </dependency>
  26. <!--之前写的公共模块的坐标-->
  27. <dependency>
  28. <groupId>com.zyh.springcloud</groupId>
  29. <artifactId>cloud-api-commons</artifactId>
  30. <version>${project.version}</version>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-web</artifactId>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.springframework.boot</groupId>
  38. <artifactId>spring-boot-starter-actuator</artifactId>
  39. </dependency>
  40. <dependency>
  41. <groupId>org.springframework.boot</groupId>
  42. <artifactId>spring-boot-devtools</artifactId>
  43. <scope>runtime</scope>
  44. <optional>true</optional>
  45. </dependency>
  46. <dependency>
  47. <groupId>org.projectlombok</groupId>
  48. <artifactId>lombok</artifactId>
  49. <optional>true</optional>
  50. </dependency>
  51. <dependency>
  52. <groupId>org.springframework.boot</groupId>
  53. <artifactId>spring-boot-starter-test</artifactId>
  54. <scope>test</scope>
  55. </dependency>
  56. </dependencies>
  57. </project>

application.yml

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

主启动类

在这里插入图片描述

Service及其实现类

Service

  1. package com.zyh.springcloud.service;
  2. /**
  3. * @author zengyihong
  4. * @create 2022--10--16 11:07
  5. */
  6. public interface PaymentService {
  7. public String paymentInfo_OK(Integer id);
  8. public String payment_Timeout(Integer id);
  9. }

实现类

  1. package com.zyh.springcloud.service.impl;
  2. import com.zyh.springcloud.service.PaymentService;
  3. import org.springframework.stereotype.Service;
  4. import java.util.concurrent.TimeUnit;
  5. /**
  6. * @author zengyihong
  7. * @create 2022--10--16 11:08
  8. */
  9. @Service
  10. public class PaymentServiceImpl implements PaymentService {
  11. //成功
  12. @Override
  13. public String paymentInfo_OK(Integer id) {
  14. return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id: " + id + "\t" + "皮卡皮卡";
  15. }
  16. //失败
  17. @Override
  18. public String payment_Timeout(Integer id) {
  19. int timeNumber = 3;
  20. try {
  21. TimeUnit.SECONDS.sleep(timeNumber);
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id: " + id + "\t" + "呜呜呜" + " 耗时(秒)" + timeNumber;
  26. }
  27. }

Controller

  1. package com.zyh.springcloud.controller;
  2. import com.zyh.springcloud.service.PaymentService;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.beans.factory.annotation.Value;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.PathVariable;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import javax.annotation.Resource;
  9. /**
  10. * @author zengyihong
  11. * @create 2022--10--16 11:17
  12. */
  13. @RestController
  14. @Slf4j
  15. public class PaymentController {
  16. @Resource
  17. private PaymentService paymentService;
  18. @Value("${server.port}")
  19. private String serverPort;
  20. @GetMapping("/payment/hystrix/ok/{id}")
  21. public String paymentInfo_OK(@PathVariable("id") Integer id) {
  22. String result = paymentService.paymentInfo_OK(id);
  23. log.info("*******result:" + result + ",端口:" + serverPort);
  24. return result;
  25. }
  26. @GetMapping("/payment/hystrix/timeout/{id}")
  27. public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
  28. String result = paymentService.payment_Timeout(id);
  29. log.info("*******result:" + result + ",端口:" + serverPort);
  30. return result;
  31. }
  32. }

正常测试

  1. 启动eureka7001
  2. 启动cloud-provider-hystrix-payment8001
  3. 访问http://localhost:8001/payment/hystrix/ok/31
  4. 在这里插入图片描述

    每次调用耗费3秒钟:http://localhost:8001/payment/hystrix/timeout/31
    在这里插入图片描述

上述module均OK

  1. 以上述为根基平台,从正确->错误->降级熔断->恢复

高并发测试

使用Jmeter进行测试

开启Jmeter,来20000个并发压死8001,20000个请求都去访问payment_Timeout服务
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

然后需要保存这些配置
在这里插入图片描述

压测的过程中再使用postman访问一下微服务
http://localhost:8001/payment/hystrix/ok/31
http://localhost:8001/payment/hystrix/timeout/31
测试的时候,我们会发现响应速度很慢,会一直在转圈,很卡,这是因为tomcat默认的工作线程数被打满了,所以没有多余的线程来分解压力和处理。刚刚测试的时候,我们只是对服务提供者8001进行测试,如果说这个时候服务消费者80也来进行访问,那么消费者就只能一直等待了,最终导致消费者等不及,可能导致超时异常,而且服务端8001会被拖死。

创建新模块cloud-consumer-feign-hystrix-order80

创建新模块cloud-consumer-feign-hystrix-order80
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>cloud2022</artifactId>
  7. <groupId>com.zyh.springcloud</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>cloud-consumer-feign-hystrix-order80</artifactId>
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. </properties>
  16. <dependencies>
  17. <!--新增hystrix-->
  18. <dependency>
  19. <groupId>org.springframework.cloud</groupId>
  20. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework.cloud</groupId>
  24. <artifactId>spring-cloud-starter-openfeign</artifactId>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework.cloud</groupId>
  28. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  29. </dependency>
  30. <dependency>
  31. <groupId>com.zyh.springcloud</groupId>
  32. <artifactId>cloud-api-commons</artifactId>
  33. <version>${project.version}</version>
  34. </dependency>
  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. <dependency>
  44. <groupId>org.springframework.boot</groupId>
  45. <artifactId>spring-boot-devtools</artifactId>
  46. <scope>runtime</scope>
  47. <optional>true</optional>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.projectlombok</groupId>
  51. <artifactId>lombok</artifactId>
  52. <optional>true</optional>
  53. </dependency>
  54. <dependency>
  55. <groupId>org.springframework.boot</groupId>
  56. <artifactId>spring-boot-starter-test</artifactId>
  57. <scope>test</scope>
  58. </dependency>
  59. </dependencies>
  60. </project>

yml

  1. server:
  2. port: 80
  3. spring:
  4. application:
  5. name: cloud-provider-hystrix-payment-service
  6. eureka:
  7. client:
  8. register-with-eureka: true #表识不向注册中心注册自己
  9. fetch-registry: true #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
  10. service-url:
  11. defaultZone: http://localhost:7001/eureka/

主启动类
在这里插入图片描述

Service

  1. package com.zyh.springcloud.service;
  2. import org.springframework.cloud.openfeign.FeignClient;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.PathVariable;
  5. /**
  6. * @author zengyihong
  7. * @create 2022--10--16 14:53
  8. */
  9. @FeignClient("CLOUD-HYSTRIX-PAYMENT-SERVICE")
  10. public interface PaymentHystrixService {
  11. @GetMapping("/payment/hystrix/ok/{id}")
  12. public String paymentInfo_OK(@PathVariable("id") Integer id);
  13. @GetMapping("/payment/hystrix/timeout/{id}")
  14. public String payment_Timeout(@PathVariable("id") Integer id);
  15. }

Controller

  1. package com.zyh.springcloud.controller;
  2. import com.zyh.springcloud.service.PaymentHystrixService;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.PathVariable;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import javax.annotation.Resource;
  9. /**
  10. * @author zengyihong
  11. * @create 2022--10--16 14:53
  12. */
  13. @RestController
  14. @Slf4j
  15. public class OrderHystrixController {
  16. @Autowired
  17. private PaymentHystrixService paymentHystrixService;
  18. @GetMapping("/consumer/payment/hystrix/ok/{id}")
  19. public String paymentInfo_OK(@PathVariable("id") Integer id) {
  20. String result = paymentHystrixService.paymentInfo_OK(id);
  21. log.info("*******result:" + result);
  22. return result;
  23. }
  24. @GetMapping("/consumer/payment/hystrix/timeout/{id}")
  25. public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
  26. String result = paymentHystrixService.payment_Timeout(id);
  27. log.info("*******result:" + result);
  28. return result;
  29. }
  30. }

测试

5W个线程压8001
在这里插入图片描述

在这里插入图片描述

消费端80微服务再去访问正常的OK微服务8001地址
在这里插入图片描述

测试结论 & 解决方法

8001同一层次的其他接口服务被困死,因为tomcat线程里面的工作线程已经被挤占完毕
80此时调用8001,客户端访问响应缓慢,转圈圈

正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生
那么要怎么解决呢
在这里插入图片描述

服务降级

服务提供者8001降级

我们可用设置自身调用超时时间的峰值,峰值内可以正常运行,否则执行降级方法
一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
在这里插入图片描述

由代码可知,为 payment_Timeout方法编写一个回退方法payment_TimeoutHandler,**payment_TimeoutHandler方法与 payment_Timeout方法具有相同的参数与返回值类型,该方法返回一个默认的错误信息。 **
在 payment_Timeout方法上,使用注解@HystrixCommand的fallbackMethod属性,指定熔断触发的降级方法 是payment_TimeoutHandler 。

  • 因为熔断的降级逻辑方法必须跟正常逻辑方法保证:相同的参数列表和返回值声明
  • 在 payment_Timeout 方法上 HystrixCommand(fallbackMethod = “findProductFallBack”) 用来声明一个降级逻辑的方法

    package com.zyh.springcloud.service.impl;

    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
    import com.zyh.springcloud.service.PaymentService;
    import org.springframework.stereotype.Service;

    import java.util.concurrent.TimeUnit;

    /**

    • @author zengyihong
    • @create 2022—10—16 11:08
      */
      @Service
      public class PaymentServiceImpl implements PaymentService {

      //成功
      @Override
      public String paymentInfo_OK(Integer id) {

      1. return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id: " + id + "\t" + "皮卡皮卡";

      }

      //超时降级演示
      @HystrixCommand(fallbackMethod = “payment_TimeoutHandler”, commandProperties = {

      1. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000") //5秒钟以内就是正常的业务逻辑

      })

  1. //失败
  2. @Override
  3. public String payment_Timeout(Integer id) {
  4. int timeNumber = 13;
  5. try {
  6. TimeUnit.SECONDS.sleep(timeNumber);
  7. } catch (Exception e) {
  8. e.printStackTrace();
  9. }
  10. return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id: " + id + "\t" + "呜呜呜" + " 耗时(秒)" + timeNumber;
  11. }
  12. /**
  13. * 兜底方法,上面方法出问题的话,代替业务方法来返回兜底结果,返回一个出错信息
  14. * 降级方法的声明要求:必须是public,返回结果类型要一致。
  15. * 参数类型,参数个数一致,方法名自定义
  16. * @param id
  17. * @return
  18. */
  19. public String payment_TimeoutHandler(Integer id) {
  20. return "线程池:" + Thread.currentThread().getName() + " payment_TimeoutHandler,系统繁忙,请稍后再试8001 ";
  21. }
  22. }

主启动类激活
@EnableCircuitBreaker
在这里插入图片描述

在这里插入图片描述

服务消费者80降级

服务降级可以在服务提供者侧,也可以在服务消费者侧。更多是在服务消费者侧。
yml

  1. feign:
  2. hystrix:
  3. enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

在主启动类加上@EnableHystrix
在这里插入图片描述

Controller

  1. package com.zyh.springcloud.controller;
  2. import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
  3. import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
  4. import com.zyh.springcloud.service.PaymentHystrixService;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.web.bind.annotation.GetMapping;
  8. import org.springframework.web.bind.annotation.PathVariable;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import javax.annotation.Resource;
  11. /**
  12. * @author zengyihong
  13. * @create 2022--10--16 14:53
  14. */
  15. @RestController
  16. @Slf4j
  17. public class OrderHystrixController {
  18. @Autowired
  19. private PaymentHystrixService paymentHystrixService;
  20. @GetMapping("/consumer/payment/hystrix/ok/{id}")
  21. public String paymentInfo_OK(@PathVariable("id") Integer id) {
  22. String result = paymentHystrixService.paymentInfo_OK(id);
  23. log.info("*******result:" + result);
  24. return result;
  25. }
  26. @HystrixCommand( fallbackMethod = "payment_TimeoutHandler",commandProperties = {
  27. //超过1.5秒就降级自己
  28. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
  29. })
  30. @GetMapping("/consumer/payment/hystrix/timeout/{id}")
  31. public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
  32. String result = paymentHystrixService.payment_Timeout(id);
  33. log.info("*******result:" + result);
  34. return result;
  35. }
  36. /**
  37. * 兜底方法,上面方法出现问题,就来执行这个方法,返回一个出错信息
  38. * @param id
  39. * @return
  40. */
  41. public String payment_TimeoutHandler(Integer id){
  42. return "我是消费者80,对方支付系统繁忙,或者自己运行出错,请检查自己";
  43. }
  44. }

测试
在这里插入图片描述

默认的fallback

我们刚才把fallback写在了某个业务方法上,如果这样的方法很多,那岂不是要写很多。所以我们可以
把Fallback配置加在类上,实现默认fallback。
在这里插入图片描述

  1. package com.zyh.springcloud.controller;
  2. import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
  3. import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
  4. import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
  5. import com.zyh.springcloud.service.PaymentHystrixService;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. import org.springframework.web.bind.annotation.PathVariable;
  10. import org.springframework.web.bind.annotation.RestController;
  11. /**
  12. * @author zengyihong
  13. * @create 2022--10--16 14:53
  14. */
  15. @RestController
  16. @Slf4j
  17. @DefaultProperties(defaultFallback = "globalHandler")
  18. public class OrderHystrixController {
  19. @Autowired
  20. private PaymentHystrixService paymentHystrixService;
  21. @GetMapping("/consumer/payment/hystrix/ok/{id}")
  22. public String paymentInfo_OK(@PathVariable("id") Integer id) {
  23. String result = paymentHystrixService.paymentInfo_OK(id);
  24. log.info("*******result:" + result);
  25. return result;
  26. }
  27. // @HystrixCommand( fallbackMethod = "payment_TimeoutHandler",commandProperties = {
  28. // //超过1.5秒就降级自己
  29. // @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
  30. // })
  31. @HystrixCommand
  32. @GetMapping("/consumer/payment/hystrix/timeout/{id}")
  33. public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
  34. String result = paymentHystrixService.payment_Timeout(id);
  35. log.info("*******result:" + result);
  36. return result;
  37. }
  38. /**
  39. * 兜底方法,上面方法出现问题,就来执行这个方法,返回一个出错信息
  40. * @param
  41. * @return
  42. */
  43. // public String payment_TimeoutHandler(Integer id){
  44. // return "我是消费者80,对方支付系统繁忙,或者自己运行出错,请检查自己";
  45. // }
  46. public String globalHandler(){
  47. return "我是全局降级处理方法";
  48. }
  49. }

在这里插入图片描述

在这里插入图片描述

服务降级处理的实现类

本次案例服务降级处理是在客户端80实现完成的,与服务端8001没有关系,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理。
image.png
仅仅只有这个实现类还不行,我们还得告诉service,我们为他准备了一个服务降级的实现类
image.png
测试
image.png

服务熔断

熔断机制介绍

  • 熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长的时候,为了保护系统的整体可用性,熔断器会暂时切断请求对该服务的调用,并快速返回一个友好的错误响应。这种熔断状态不是永久的,在经历了一定的时间后,熔断器会再次检测该微服务是否恢复正常,若服务恢复正常则恢复其调用链路。
  • 当检测到该节点微服务调用响应正常以后,恢复调用链路。
  • 在Spring Cloud框架中,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状态,当失败的调用到一定阙值的时候,默认是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是@HystrixCommand
  • https://martinfowler.com/bliki/CircuitBreaker.html (大神级别论文)

熔断状态

在熔断机制中涉及了三种熔断状态:

  • 熔断关闭状态(Closed):当服务访问正常时,熔断器处于关闭状态,服务调用方可以正常地对服务进行调用。
  • 熔断开启状态(Open):默认情况下,在固定时间内接口调用出错比率达到一个阈值(例如 50%),熔断器会进入熔断开启状态。进入熔断状态后,后续对该服务的调用都会被切断,熔断器会执行本地的降级(FallBack)方法。
  • 半熔断状态(Half-Open): 在熔断开启一段时间之后,熔断器会进入半熔断状态。在半熔断状态下,熔断器会尝试恢复服务调用方对服务的调用,允许部分请求调用该服务,并监控其调用成功率。如果成功率达到预期,则说明服务已恢复正常,熔断器进入关闭状态;如果成功率仍旧很低,则重新进入熔断开启状态。

image.png

Hystrix熔断机制的实现

在 Spring Cloud 中,熔断机制是通过 Hystrix 实现的。Hystrix 会监控微服务间调用的状况,当失败调用到一定比例时(例如 5 秒内失败 20 次),就会启动熔断机制。
Hystrix 实现服务熔断的步骤如下:

  1. 当服务的调用出错率达到或超过 Hystix 规定的比率(默认为 50%)后,熔断器进入熔断开启状态。
  2. 熔断器进入熔断开启状态后,Hystrix 会启动一个休眠时间窗,在这个时间窗内,该服务的降级逻辑会临时充当业务主逻辑,而原来的业务主逻辑不可用。
  3. 当有请求再次调用该服务时,会直接调用降级逻辑快速地返回失败响应,以避免系统雪崩。
  4. 当休眠时间窗到期后,Hystrix 会进入半熔断转态,允许部分请求对服务原来的主业务逻辑进行调用,并监控其调用成功率。
  5. 如果调用成功率达到预期,则说明服务已恢复正常,Hystrix 进入熔断关闭状态,服务原来的主业务逻辑恢复;否则 Hystrix 重新进入熔断开启状态,休眠时间窗口重新计时,继续重复第 2 到第 5 步。

代码实现

  1. ** **修改cloud-provider-hystrix-payment8001中的PaymentService,添加一个public String paymentCircuitBreaker(Integer id)方法;
  2. 在 PaymentService 接口的实现类 PaymentServiceImpl 添加 paymentCircuitBreaker() 的方法实现及其回退方法

    //服务熔断

    1. @Override
    2. @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
    3. @HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
    4. @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //当在配置时间窗口内(10s)达到此数量(10个)的失败后,打开断路器,默认是20个失败的请求
    5. @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //断路多久以后开始尝试是否恢复,默认5s
    6. @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //出错百分比阈值,当达到此阈值后,开始短路。默认50%
    7. })
    8. public String paymentCircuitBreaker(Integer id){
    9. if (id < 0){
    10. throw new RuntimeException("*****id 不能负数");
    11. }
    12. String serialNumber = IdUtil.simpleUUID();//hutool.cn工具包
    13. return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
    14. }
    15. public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
    16. return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
    17. }
" class="reference-link">image.png
  1. 在 PaymentController 中添加一个 paymentCircuitBreaker() 方法对外提供服务

image.png

  1. 测试

服务监控:hystrixDashboard

基本介绍

除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stram项目实现了对以上指示的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。

搭建 Hystrix Dashboard,监控其他模块的运行情况。

新建模块cloud-consumer-hystrix-dashboard9001
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>cloud2022</artifactId>
  7. <groupId>com.zyh.springcloud</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>cloud-consumer-hystrix-dashboard9001</artifactId>
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. </properties>
  16. <dependencies>
  17. <!--新增hystrix dashboard-->
  18. <dependency>
  19. <groupId>org.springframework.cloud</groupId>
  20. <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</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>
  43. </project>
application.yml
  1. server:
  2. port: 9001 #端口号
  3. hystrix:
  4. dashboard:
  5. proxy-stream-allow-list: "*"
主启动类

image.png

所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-actuator</artifactId>
  4. </dependency>
启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001

image.png

断路器演示

修改cloud-provider-hystrix-payment8001

注意:新版本Hystrix需要在主启动类MainAppHystrix8001中指定监控路径

  1. package com.zyh.springcloud;
  2. import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.boot.web.servlet.ServletRegistrationBean;
  6. import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
  7. import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
  8. import org.springframework.context.annotation.Bean;
  9. /**
  10. * @author zengyihong
  11. * @create 2022--10--16 11:05
  12. */
  13. @SpringBootApplication
  14. @EnableEurekaClient
  15. /**
  16. * 开启熔断功能
  17. */
  18. @EnableCircuitBreaker
  19. public class PaymentHystrixMain8001 {
  20. public static void main(String[] args) {
  21. SpringApplication.run(PaymentHystrixMain8001.class, args);
  22. }
  23. /**
  24. *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
  25. *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
  26. *只要在自己的项目里配置上下面的servlet就可以了
  27. */
  28. @Bean
  29. public ServletRegistrationBean getServlet() {
  30. HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
  31. ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
  32. registrationBean.setLoadOnStartup(1);
  33. registrationBean.addUrlMappings("/hystrix.stream");
  34. registrationBean.setName("HystrixMetricsStreamServlet");
  35. return registrationBean;
  36. }
  37. }
监控测试
1) 启动1个eureka
2) 观察监控窗口

9001监控8001
http://localhost:9001/hystrix
image.png
http://localhost:8001/hystrix.stream
发送一次请求
image.png
然后将以下信息填到 Hystrix 监控页面中
image.png
image.png
image.png

发表评论

表情:
评论列表 (有 0 条评论,134人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring cloud 熔断Hystrix

    在微服务的体系下,服务之间是与依赖关系的!服务的调用中也存在着各种可能的问题,如果一个服务处理缓慢,可能会导致服务被阻塞住,导致整个系统被拖慢,甚至是崩溃。这种情况下可以