【Hystrix】01-Hystrix与SpringCloud

Hystrix与SpringCloud

主要内容

需要先学习:注册中心(Eureka)、服务调用(OpenFeign)

概念

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

熔断机制:是对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或响应时间太长,会进行服务的降级,进而熔断该节点的为服务调用,快速返回错误的相应信息。当检测到该节点微服务调用相应正常一会,恢复调用链路

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

核心

服务降级、服务熔断、服务限流

  1. 服务降级

    发生情况:1. 程序异常;2.超时;3. 服务熔断触发;4.线程池的线程都被占用

    处理:当某服务不可用时,向调用方返回一个符合预期的,可处理的备选响应。

  2. 服务熔断

    处理:服务访问量达到最大时,避免服务器宕机,拒绝新的服务访问。然后利用服务降级、返回友好提示

    流程:服务降级→进行熔断→恢复调用链路

  3. 服务限流

    处理:秒杀等高并发操作进行时,避免请求量急剧升高。对服务进行排队,有序进行。

服务降级(fallback)-使用

服务降级:当服务调用运行超时或运行异常时,会对服务进行降级处理,让其直接调用fallback方法。

下面分为四点进行应用:

  1. 当接口运行超时或异常时,执行指定的服务降级回调方法(生产者自我降级)
  2. 当接口使用feign调用时,运行超时或异常,执行指定的服务降级回调方法(消费者服务降级)
  3. 在控制层的一个类中添加一个默认的降级回调方法(降级优化)
  4. 当借口使用feign调用时,为每个feign的interface类编写实现类,在这个实现类中编写回调方法(降级的最优处理):相当于2、3之上的优化方案

注意:这四个内容中,4最为重要。仅实现1和4即可

生产者自我降级

超时、程序异常等都会导致服务降级降级

注意:服务降级降级一般放在消费端,一下生产端的降级仅作演示操作

以下注解放在控制层方法上

注解 属性 说明 注意
@HystrixCommand 启动熔断配置
fallbackMethod 服务熔断后,调用的方法名 方法的参数要与该方法参数一致
commandProperties 具体熔断配置 该配置是一个集合
@HystrixProperty 熔断方式:超时…
name 熔断名:execution.isolation.thread.timeoutInMilliseconds(超时)
value 熔断异常值:3000(3s)
  1. 注册中心

  2. 服务生产者

    1. 新建项目,项目目录如下,具体代码看后面步骤

      img-04

    2. pom.xml依赖

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      <dependencies>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix</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>
    3. application.yml配置文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      server:
      port: 8011
      spring:
      application:
      name: cloud-hystrix-payment
      eureka:
      client:
      fetch-registry: true
      register-with-eureka: true
      service-url:
      defaultZone: http://eureka7001.com:7001/eureka
    4. HystrixPaymentMain8011.java启动类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      /**
      * 1. 启动熔断器注解:@EnableHystrix@EnableCircuitBreaker 注解功能类似,为继承关系
      * @author zsq
      * @create 2021-08-19-00:45:08
      */
      @EnableHystrix
      @EnableEurekaClient
      @SpringBootApplication
      public class HystrixPaymentMain8011 {

      public static void main(String[] args) {
      SpringApplication.run(HystrixPaymentMain8011.class);
      }

      }
    5. PaymentController.java控制层接口

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      @Slf4j
      @RestController
      public class PaymentController {

      @GetMapping("/ok/{id}")
      public String hystrixOk(@PathVariable("id") String id) {
      String result = "ok : id = " + id + ", thread_name = " + Thread.currentThread().getName() + ", O(∩_∩)O";
      log.info("result = {}", result);
      return result;
      }

      @GetMapping("/timeout/{id}")
      public String hystrixTimeout(@PathVariable("id") String id) {
      int timeout = 5;
      try {
      TimeUnit.SECONDS.sleep(timeout);
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      String result = "timeout = " + timeout + ": id = " + id + ", thread_name = " + Thread.currentThread().getName() + ", O(∩_∩)O";
      log.info("result = {}", result);
      return result;
      }

      @GetMapping("/err")
      public String hystrixError() {
      int age = 10 / 0;
      String result = "error : " + ", thread_name = " + Thread.currentThread().getName() + ", 🤭";
      log.info("result = {}", result);
      return result;
      }

      }
    6. 启动项目并调用接口,查看是否程序是否正常运行

    7. 添加熔断降级代码

      PaymentController.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      /**
      * 熔断的注解中,HystrixProperty.name可以使用开发工具的find everywhere 搜索'HystrixCommandProperties',即可查看到可以使用的name
      */
      @Slf4j
      @RestController
      public class PaymentController {

      @GetMapping("/ok/{id}")
      public String hystrixOk(@PathVariable("id") String id) {
      String result = "ok : id = " + id + ", thread_name = " + Thread.currentThread().getName() + ", O(∩_∩)O";
      log.info("result = {}", result);
      return result;
      }

      /**
      * 配置3s的超时时间
      */
      @GetMapping("/timeout/{id}")
      @HystrixCommand(fallbackMethod = "hystrixTimeoutFallback", commandProperties = {
      @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
      })
      public String hystrixTimeout(@PathVariable("id") String id) {
      // timeout:超时时间,可以自己设置。查看超时与未超时是否有区别,配置是否生效
      int timeout = 5;
      try {
      TimeUnit.SECONDS.sleep(timeout);
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      String result = "timeout = " + timeout + ": id = " + id + ", thread_name = " + Thread.currentThread().getName() + ", O(∩_∩)O";
      log.info("result = {}", result);
      return result;
      }

      public String hystrixTimeoutFallback(String id) {
      String result = "hystrixTimeoutFallback - timeout : id = " + id + ", thread_name = " + Thread.currentThread().getName() + ", 😭";
      log.warn("result = {}", result);
      return result;
      }

      @GetMapping("/err")
      @HystrixCommand(fallbackMethod = "hystrixErrorFallback", commandProperties = {
      @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
      })
      public String hystrixError() {
      int age = 10 / 0;
      String result = "error : " + ", thread_name = " + Thread.currentThread().getName() + ", 🤭";
      log.info("result = {}", result);
      return result;
      }

      public String hystrixErrorFallback() {
      String result = "fallback - error : " + ", thread_name = " + Thread.currentThread().getName() + ", 🤭";
      log.warn("result = {}", result);
      return result;
      }

      }
  3. 调用接口http://localhost:8011/ok/1, http://localhost:8011/timeout/1, localhost:8011/err

    • http://localhost:8011/ok/1正常

      img-01

    • http://localhost:8011/timeout/1:程序运行超时熔断

      img-02

    • localhost:8011/err:程序运行异常熔断

      img-03

消费者服务降级

消费者使用OpenFeign调用生产者服务,发生熔断的情况

  1. 服务消费者

    1. 新建项目,项目目录如下,具体代码看后面步骤

      img-05

    2. pom.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <dependencies>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</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>
    3. application.yml

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      @EnableFeignClients
      @EnableEurekaClient
      @SpringBootApplication
      public class HystrixPaymentFeignMain8021 {

      public static void main(String[] args) {
      SpringApplication.run(HystrixPaymentFeignMain8021.class);
      }

      }
    5. FeignPaymentController

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      @Slf4j
      @RestController
      @RequestMapping("/consumer")
      public class FeignPaymentController {

      @Resource
      private PaymentFeign paymentFeign;

      @GetMapping("/ok/{id}")
      public String hystrixOk(@PathVariable("id") String id) {
      String result = paymentFeign.hystrixOk(id);
      log.info("{}", result);
      return result;
      }

      @GetMapping("/timeout/{id}")
      public String hystrixTimeout(@PathVariable("id") String id) {
      String result = paymentFeign.hystrixTimeout(id);
      log.info("{}", result);
      return result;
      }

      @GetMapping("/err")
      public String hystrixError() {
      String result = paymentFeign.hystrixError();
      log.info("{}", result);
      return result;
      }

      }
    6. PaymentFeign

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      @Component
      @FeignClient(name = "CLOUD-HYSTRIX-PAYMENT")
      public interface PaymentFeign {

      @GetMapping("/ok/{id}")
      String hystrixOk(@PathVariable("id") String id);

      @GetMapping("/timeout/{id}")
      String hystrixTimeout(@PathVariable("id") String id);

      @GetMapping("/err")
      String hystrixError();

      }
    7. 启动项目并调用接口,查看是否程序是否正常运行。若无误,进行以下步骤

  2. 消费者配置熔断

    1. application.yml修改配置文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      server:
      port: 8021
      spring:
      application:
      name: cloud-hystrix-payment-feign
      eureka:
      client:
      fetch-registry: true
      register-with-eureka: true
      service-url:
      defaultZone: http://eureka7001.com:7001/eureka
      # 启动hystrix(我试了一下,不加这个也不影响hystrix的使用)
      feign:
      hystrix:
      enabled: true
    2. HystrixPaymentFeignMain8021.java添加启动Hystrix注解

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @EnableHystrix
      @EnableFeignClients
      @EnableEurekaClient
      @SpringBootApplication
      public class HystrixPaymentFeignMain8021 {

      public static void main(String[] args) {
      SpringApplication.run(HystrixPaymentFeignMain8021.class);
      }

      }
    3. FeignPaymentController.java控制层

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      @Slf4j
      @RestController
      @RequestMapping("/consumer")
      public class FeignPaymentController {

      @Resource
      private PaymentFeign paymentFeign;

      @GetMapping("/ok/{id}")
      public String hystrixOk(@PathVariable("id") String id) {
      String result = paymentFeign.hystrixOk(id);
      log.info("{}", result);
      return result;
      }

      /**
      * 当feign的调用时间超过1s时,就会调用fallbackMethod中运行
      * 目前还不知道如何解决,推测应该是feign的超时时间配置
      */
      @GetMapping("/timeout/{id}")
      @HystrixCommand(fallbackMethod = "hystrixTimeoutFallback", commandProperties = {
      @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
      })
      public String hystrixTimeout(@PathVariable("id") String id) {
      String result = paymentFeign.hystrixTimeout(id);
      log.info("{}", result);
      return result;
      }

      public String hystrixTimeoutFallback(String id) {
      String result = "feign - hystrixTimeoutFallback: thread_name = " + Thread.currentThread().getName() + ", id = " + id + "/(ㄒoㄒ)/~~";
      log.info("{}", result);
      return result;
      }

      @GetMapping("/err")
      @HystrixCommand(fallbackMethod = "hystrixErrorFallback", commandProperties = {
      @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
      })
      public String hystrixError() {
      String result = paymentFeign.hystrixError();
      log.info("{}", result);
      return result;
      }

      public String hystrixErrorFallback() {
      String result = "feign - hystrixErrorFallback: thread_name = " + Thread.currentThread().getName() + "/(ㄒoㄒ)/~~";
      log.info("{}", result);
      return result;
      }

      }
  3. 重启程序,并调用接口进行测试http://localhost:8021/consumer/ok/1, http://localhost:8021/consumer/timeout/1, http://localhost:8021/consumer/err

    • http://localhost:8021/consumer/ok/1

      正常运行

      img-06

    • http://localhost:8021/consumer/timeout/1

      调用该接口时,feign有自己默认的超时时间1s,若超过1s同样会调用消费者的fallback方法

      img-07

    • http://localhost:8021/consumer/err

      该接口返回的是生产者的fallback结果。若关闭生产者,则会调用消费者的fallback方法。

      img-08

服务降级优化(控制层设置默认的降级方法)

控制层中的每个类,都可以为其配置一个默认的fallback方法,供该类使用。

下面用消费者的代码进行演示

  1. PaymentController.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    /**
    * 1. 在该类中编写默认的fallback方法:defaultFallback
    * 2. 类上添加注解'@DefaultProperties(defaultFallback = "defaultFallback")'
    * 3. 将'hystrixError'方法的'@HystrixCommand'注解中的内容注释掉,让其使用默认的fallback方法
    * @author zsq
    * @create 2021-08-19-00:59:39
    */
    @Slf4j
    @RestController
    @RequestMapping("/consumer")
    @DefaultProperties(defaultFallback = "defaultFallback")
    public class FeignPaymentController {

    @Resource
    private PaymentFeign paymentFeign;

    @GetMapping("/err")
    @HystrixCommand
    // @HystrixCommand(fallbackMethod = "hystrixErrorFallback", commandProperties = {
    // @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    // })
    public String hystrixError() {
    String result = paymentFeign.hystrixError();
    log.info("{}", result);
    return result;
    }

    public String hystrixErrorFallback() {
    String result = "feign - hystrixErrorFallback: thread_name = " + Thread.currentThread().getName() + "/(ㄒoㄒ)/~~";
    log.info("{}", result);
    return result;
    }

    public String defaultFallback() {
    String result = "defaultFallback - (●'◡'●)";
    log.info("result = {}", result);
    return result;
    }

    }
  2. localhost:8021/comsumer/err调用接口

    1. 此时返回的是生产者的熔断结果

      img-09

    2. 关闭生产者,再次调用,结果为默认的fallback方法的返回值

      img-10

服务降级最优处理

  1. 项目目录如下

    img-11

  2. 编写代码

    1. pom.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <dependencies>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</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>
    2. application.yml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      server:
      port: 8021
      spring:
      application:
      name: cloud-hystrix-payment-feign
      eureka:
      client:
      fetch-registry: true
      register-with-eureka: true
      service-url:
      defaultZone: http://eureka7001.com:7001/eureka
      # 启动hystrix,必须使用该注解,feign调用的熔断才会启动
      feign:
      hystrix:
      enabled: true
    3. HystrixPaymentFeignMain8021.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @EnableHystrix
      @EnableFeignClients
      @EnableEurekaClient
      @SpringBootApplication
      public class HystrixPaymentFeignMain8021 {

      public static void main(String[] args) {
      SpringApplication.run(HystrixPaymentFeignMain8021.class);
      }

      }
    4. FeignPaymentController.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      @Slf4j
      @RestController
      @RequestMapping("/consumer")
      @DefaultProperties(defaultFallback = "defaultFallback")
      public class FeignPaymentController {

      @Resource
      private PaymentFeign paymentFeign;

      @GetMapping("/ok/{id}")
      public String hystrixOk(@PathVariable("id") String id) {
      String result = paymentFeign.hystrixOk(id);
      log.info("{}", result);
      return result;
      }

      @GetMapping("/timeout/{id}")
      public String hystrixTimeout(@PathVariable("id") String id) {
      String result = paymentFeign.hystrixTimeout(id);
      log.info("{}", result);
      return result;
      }

      @GetMapping("/err")
      public String hystrixError() {
      String result = paymentFeign.hystrixError();
      log.info("{}", result);
      return result;
      }

      }
    5. PaymentFeign.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      /**
      * 1. 指定Feign服务熔断的回调类:@FeignClients(fallback) = PaymentFallback.class
      */
      @Component
      @FeignClient(name = "CLOUD-HYSTRIX-PAYMENT", fallback = PaymentFallback.class)
      public interface PaymentFeign {

      @GetMapping("/ok/{id}")
      String hystrixOk(@PathVariable("id") String id);

      @GetMapping("/timeout/{id}")
      String hystrixTimeout(@PathVariable("id") String id);

      @GetMapping("/err")
      String hystrixError();

      }
    6. PaymentFallback.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      @Slf4j
      @Component
      public class PaymentFallback implements PaymentFeign {

      @Override
      public String hystrixOk(String id) {
      String result = "PaymentFallback - hystrixOk - id = " + id;
      log.info(result);
      return result;
      }

      @Override
      public String hystrixTimeout(String id) {
      String result = "PaymentFallback - hystrixTimeout - id = " + id;
      log.info(result);
      return result;
      }

      @Override
      public String hystrixError() {
      String result = "PaymentFallback - hystrixError - ";
      log.info(result);
      return result;
      }

      }
  3. 调用接口http://localhost:8021/consumer/ok/1, http://localhost:8021/consumer/timeout/1, http://localhost:8021/consumer/err

    为了使feign的fallback方法被调用,关闭生产者服务,结果如下:

    • http://localhost:8021/consumer/ok/1

      img-12

    • http://localhost:8021/consumer/timeout/1

      img-13

    • http://localhost:8021/consumer/err

      img-14

服务熔断(circuit_break)

熔断机制:是对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或响应时间太长,会进行服务的降级,进而熔断该节点的为服务调用,快速返回错误的相应信息。当检测到该节点微服务调用相应正常一会,恢复调用链路

熔断类型:

  1. 熔断打开:

    请求不在调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时常,则进入半熔断状态

  2. 熔断关闭:

    熔断管理,不再对服务进行熔断

  3. 熔断半开:

    部分请求根据规则调用当前服务,如果请求成功且符合规则,则认为当前规则恢复正常,关闭熔断。

熔断图解

img-16

服务熔断配置

  1. 项目目录如下

    img-04

  2. 编写代码

    1. pom.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <dependencies>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      </dependency>
      </dependencies>
    2. application.yml

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      @EnableHystrix
      @EnableEurekaClient
      @SpringBootApplication
      public class HystrixPaymentMain8011 {

      public static void main(String[] args) {
      SpringApplication.run(HystrixPaymentMain8011.class);
      }

      }
    4. PaymentController.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      @Slf4j
      @RestController
      public class PaymentController {

      @GetMapping("/positive/{id}")
      @HystrixCommand(fallbackMethod = "hystrixPositiveFallback", commandProperties = {
      // circuitBreaker.enabled:启动服务熔断
      @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
      // circuitBreaker.sleepWindowInMilliseconds:服务跳闸后,服务熔断时间(这一时间段内,不会进行重试操作)
      @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
      // circuitBreaker.requestVolumeThreshold:请求次数
      @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
      // circuitBreaker.errorThresholdPercentage:请求失败率(在一段时间内,请求超时、异常与总请求数量占比)高于value时启动服务熔断
      @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
      })
      public String hystrixPositive(@PathVariable("id") Integer id) {
      if (id < 0) {
      String result = "hystrixPositive - id = " + id + ", id不能为负数啊兄弟~~~~~";
      log.info(result);
      throw new RuntimeException();
      }
      String result = "hystrixPositive - id = " + id + ", id是整数很棒兄弟!!!!!";
      log.info(result);
      return result;
      }

      public String hystrixPositiveFallback(Integer id) {
      String result = "fallback - hystrixPositiveFallback - id = " + id + " ┭┮﹏┭┮";
      log.info(result);
      return result;
      }

      }
  3. PayController.hystrixPositive注解说明

    属性 说明
    circuitBreaker.enabled 启动服务熔断
    circuitBreaker.sleepWindowInMilliseconds 服务跳闸后,服务熔断时间(这一时间段内,不会进行重试操作)
    circuitBreaker.requestVolumeThreshold 根据最新的n个请求,计算请求失败率
    circuitBreaker.errorThresholdPercentage 请求失败率(在一段时间内,请求超时、异常与总请求数量占比)大于等于value时启动服务熔断

    代码中配置的熔断含义:统计10s内,最新的10个请求,当此时请求失败率高于50%时,服务器将熔断10s

  4. 启动项目,测试接口

    localhost:8011/positive/1(接口一):

    ​ 正常结果:hystrixPositive - id = 1, id是整数很棒兄弟!!!!!

    ​ 熔断结果:fallback - hystrixPositiveFallback - id = 1 ┭┮﹏┭┮

    localhost:8011/positive/-1(接口二):

    ​ 结果:fallback - hystrixPositiveFallback - id = -1 ┭┮﹏┭┮

    测试熔断:调用接口一5次,调用接口二5次时(请求失败率>=50%),接下来的10s再次调用接口一时,将返回熔断结果。10s之后,返回正常结果。

    注意:在代码中,我设置的circuitBreaker.requestVolumeThreshold为10。该项设置,会使服务器根据最新的十个请求计算失败率。

    ​ 重启项目后,先调用接口二5次,在调用接口一5次。此时,接口一返回结果为正常结果。接下来的十秒内再次调用接口一时,返回的结果为熔断结果。因为前十次的请求失败率为50%,大于等于50%。

服务限流(略)

见alibaba的Sentinal

Hystrix DashBoard

Hystrix的监控仪表盘,一个准实时的调用监控。

Hystrix DashBoard 监控仪表盘

代码及应用

  1. 新建项目,项目目录如下,具体代码看后面步骤

    img-17

  2. 项目代码

    1. pom.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <dependencies>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-netflix-eureka-client</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</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>
      </dependencies>
    2. application.yml

      1
      2
      3
      4
      5
      server:
      port: 8031
      spring:
      application:
      name: cloud-hystrix-dashboard
    3. HystrixDashBoardMain8031.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      /**
      * 1. 注解‘@EnableHystrixDashboard’启动Hystrix监控仪表盘
      */
      @EnableHystrixDashboard
      @SpringBootApplication
      public class HystrixDashBoardMain8031 {

      public static void main(String[] args) {
      SpringApplication.run(HystrixDashBoardMain8031.class);
      }

      }
    4. 在熔断的项目(cloud-hystrix-payment-8011, cloud-hystrix-payment-feign-8021)中添加如下配置。

      有了这个配置,Hystrix Dashboard才能获取Hystrix的监控信息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      /**
      * 此配置是为了服务监控配置,与服务容错本身无关,Spring Cloud 升级后的坑
      * ServletRegistrationBean因为springboot的默认路径不是hystrix.stream
      * 只要在自己的项目里配置下面的servlet就可以了
      * @return ServletRegistrationBean
      */
      @Bean
      public ServletRegistrationBean getServlet() {
      HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
      ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
      registrationBean.setLoadOnStartup(1);
      registrationBean.addUrlMappings("/hystrix.stream");
      registrationBean.setName("HystrixMetricsStreamServlet");
      return registrationBean;
      }
  3. 重新启动项目cloud-hystrix-payment-8011, cloud-hystrix-payment-feign-8021, cloud-hystrix-dashboard-8031

    1. 打开页面localhost:8031/hystrix

      img-18

    2. 输入http://localhost:8011/hystrix.stream,打开监控页面

      该网页的主要信息:

      7色:绿、蓝、青、橙、紫、红、黑

      1圈:健康颜色:绿>黄>橙>红

      1线:请求量的折线图

      img-19

    3. 使用localhost:8011/postive/1(接口一)和localhost:8011/positive/-1(接口二)进行演示

      1. 只调用接口一,调用10次时。结果如下:

        img-20

      2. 只调用接口二,调用10次时。结果如下:

        此时失败率为100%,且熔断器状态为open

        img-21

        接入接下来还想继续请求接口一,此时蓝色的数量会改变:

        img-22

-------------本文结束感谢您的阅读-------------