0%

SpringCloud(七)Hystrix断路器

本章主要内容:介绍Hystrix是什么、服务熔断、服务降级以及服务监控HystrixDashBoard

概述

分布式系统面临的问题-雪崩效应

分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应.

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

如图:Service E的慢响应或宕机会导致 Service BService C调用 Serivce E的线程阻塞、Service A调用 BC的线程阻塞。如果是一个请求阻塞片刻自然没有问题;但是在高并发场景下 Service E的不可用将连带着导致很多微服务内部大量线程阻塞占用大量系统资源。

对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和.比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障.这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统.

Hystrix是什么

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

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

Hystrix能干什么

  • 服务熔断
  • 服务降级
  • 服务限流
  • 接近实时的监控系统

官网资料


服务熔断

是什么

熔断机制是应对雪崩效应的一种微服务链路保护机制

当扇出链路的某个微服务不可用或者响应时间太长,会进行服务的降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息.当检测到该节点微服务调用响应正常后恢复调用链路.在SpringCloud框架里熔断机制通过Hystrix实现.Hystrix监控微服务调用的状况,失败的调用会启动熔断机制.熔断机制的注解是@HystrixCommand.

触发熔断的条件可以是响应过长、服务提供者抛出异常、服务不可用等。

实战

本实战仍基于之前的 clouddemo工程基础之上

  1. 新建 clouddemo-provider4-hystrix工程,对该工程做下列修改

  2. 拷贝 clouddemo-provider中的依赖,并添加 hystrix依赖:

    1
    2
    3
    4
    5
    6
    <!--hystrix-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    </dependency>
    ...
  3. 拷贝 clouddemo-providerjavaresources中的文件

    1. 将端口改为 8004
    2. instance-id改为 clouddemo-dept-8004-hystrix(id任意,不和之前的三个 provider冲突即可)
  4. 修改 DeptController添加 @HystrixCommand注解:

    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
    package top.zhenganwen.clouddemoprovider.controller;

    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import top.zhenganwen.clouddemo.entity.Dept;
    import top.zhenganwen.clouddemoprovider.service.DeptService;

    @RestController
    @RequestMapping("dept")
    public class DeptController {
    @Autowired
    private DeptService service;

    @GetMapping("get/{id}")
    @HystrixCommand(fallbackMethod = "processHystrix_GET")
    public Dept get(@PathVariable("id") Long id) {
    Dept dept = service.get(id);
    if (dept == null) {
    throw new RuntimeException();
    }
    return dept;
    }

    public Dept processHystrix_GET(@PathVariable("id") Long id) {
    return new Dept().setDeptno(id).setDname("id为" + id + "的部门不存在!@HystrixCommand因方法抛出异常而被触发").setDb_source("this database don't exist");
    }
    }

    这里当根据id查不到部门时抛出一个异常,如果启用了熔断机制,则会根据 @HystrixCommand注解中 fallbackMethod指定的方法名调用相应的补救方法以快速响应调用方,从而避免该异常造成调用方的线程阻塞。

  5. 在启动类上添加 @EnableCircuitBreaker以使 @HystrixCommand生效:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package top.zhenganwen.clouddemoprovider;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

    @SpringBootApplication
    @EnableEurekaClient
    @EnableCircuitBreaker
    public class ProviderApplication_Hystrix{

    public static void main(String[] args) {
    SpringApplication.run(ProviderApplication_Hystrix.class, args);
    }
    }
  6. 测试熔断机制:

    1. 启动3个 eureka server(启动微服务之前先启动 eureka集群)

    2. 启动 clouddemo-provider4-hystrix

    3. 访问 localhost:8004/dept/get/1,返回了查出的部门:

      1
      { deptno: 1, dname: "人事部", db_source: "test" }
    4. 访问 localhost:8004/dept/get/99,返回了补救响应:

      1
      2
      3
      4
      5
      {
      deptno: 11,
      dname: "id为11的部门不存在!@HystrixCommand因方法抛出异常而被触发",
      db_source: "this database don't exist"
      }

开启了熔断之后(@EnableCircuitBreaker),Spring Cloud能通过 AOP的方式将包含 @HystrixCommand注解的 bean(这里是 DeptController)按照 @HystrixCommand注解属性对该 bean进行增强(服务方法调用异常时执行补救方法 fallbackMethod)。并且,由 Hystrix中的 circuit breaker来计算何时关闭、恢复该服务以及该服务异常时该做什么


服务降级

是什么:整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来

与服务熔断的区别:服务降级处理是在客户端实现完成的,与服务端没有关系

实战

  1. 创建一个 FallbackFactory实现类 DeptClientFallbackFactory

    1. 泛型指定为 @FeignClient的接口类型
    2. 在实现方法 create中返回要添加服务降级机制的服务接口的匿名内部类对象
    3. 当服务提供方不可用时,调用方就会直接执行该匿名内部类中相应的方法并相应结果(就避免了调用不可用服务导致阻塞的情况)
    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
    package top.zhenganwen.clouddemo.fallback;

    import feign.hystrix.FallbackFactory;
    import org.springframework.stereotype.Component;
    import top.zhenganwen.clouddemo.entity.Dept;
    import top.zhenganwen.clouddemo.service.DeptService;

    import java.util.List;

    @Component
    public class DeptClientFallbackFactory implements FallbackFactory<DeptService> {
    @Override
    public DeptService create(Throwable throwable) {
    return new DeptService() {
    @Override
    public Boolean add(Dept dept) {
    return null;
    }

    @Override
    public List<Dept> list() {
    return null;
    }

    @Override
    public Dept get(Long id) {
    return new Dept().setDeptno(id).setDname("id为" + id + "的部门不存在!或服务繁忙,请稍后再试").setDb_source("this database don't exist");
    }
    };
    }
    }
  2. 在该类上添加 @Component注解(容易遗漏,并且要放在能被 @ComponentScan扫描的位置,clouddemo-dept-feign中的 @ComponentScan("top.zhenganwen.clouddemo")能够扫描到该类)

  3. 在客户端接口的@FeignClient中添加 fallbackFactory属性(将刚才创建的服务降级机制用于该接口):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package top.zhenganwen.clouddemo.service;

    import org.springframework.cloud.netflix.feign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import top.zhenganwen.clouddemo.entity.Dept;
    import top.zhenganwen.clouddemo.fallback.DeptClientFallbackFactory;

    import java.util.List;

    @FeignClient(value = "CLOUDDEMO-DEPT",fallbackFactory = DeptClientFallbackFactory.class)
    public interface DeptService {

    @PostMapping("/dept/add")
    Boolean add(Dept dept);

    @GetMapping("/dept/list")
    List<Dept> list();

    @GetMapping("/dept/get/{id}")
    Dept get(@PathVariable("id") Long id);
    }

    mvn clean installcommon工程重新安装

  4. 在客户端工程 clouddemo-dept-feign中添加开启 hystrixdept-feign依赖了 common,而 common依赖了 hystrix

    1
    2
    3
    feign:
    hystrix:
    enabled: true
  5. 测试服务降级

    1. 启动3个 eureka server

    2. 启动 clouddemo-provider4-hystrix

    3. 启动 clouddemo-dept-feign

    4. 访问 localhost/dept/get/1能够正常响应

    5. 关闭 clouddemo-provider4-hystrix,模拟场景:

      银行业务繁忙,柜台业务员被调走,但在窗口上放置了一个“暂停服务”的告示牌,因此顾客看到告示牌后不会再此窗口前等待而会掉头回家下次再来。

    6. 访问 localhost/dept/get/1,响应如下:

      1
      2
      3
      4
      5
      { 
      deptno: 1,
      dname: "id为1的部门不存在!或服务繁忙,请稍后再试",
      db_source: "this database don't exist"
      }

      注意:这个响应并不是服务端给的(clouddemo-provider4-hystrix已被停掉了),服务端虽然停掉了,但客户端还是能快速响应避免服务调不通导致的阻塞。并且补救响应能够给以“服务之后可能会恢复,稍后再行调用”的暗示,无形中减少了该服务异常情况下的访问量,减轻系统负担。(这就好比有人发现柜台服务员不在,于是在返程的路途中提示前往柜台的人柜台服务员不在,下次再来)

    服务降级的作用:即使此时服务端已经down了,但是我们做了服务降级处理,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器


服务监控HystrixDashboard

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

实战

  1. 新建 clouddemo-hystrix-dashboard控制台工程(本例用来监控服务端),添加如下依赖:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!-- hystrix和 hystrix-dashboard相关 -->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
    </dependency>
  2. 在启动类上添加 @EnableHystrixDashboard

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package top.zhenganwen.clouddemo;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

    @SpringBootApplication
    @EnableHystrixDashboard
    public class HystrixDashBoardApplication {

    public static void main(String[] args) {
    SpringApplication.run(HystrixDashBoardApplication.class, args);
    }
    }
  3. 调整端口为 9001并启动,访问 http://localhost:9001/hystrix.stream显示如下画面:

  4. 启动服务端工程 provider4-hystrix

    1. 先启动3个 eureka

    2. 再启动 provider4-hystrix

    3. 访问 localhost:8004/dept/get/1(刚启动的服务端,一定要先访问一下)

    4. 访问 localhost:8004/hystrix.stream,返回的是实时的 hystrix流信息

      1
      2
      3
      ping: 

      data: {"type":"HystrixCommand","name":"get","group":"DeptController","currentTime":1533118698770,"isCircuitBreakerOpen":false,"errorPercentage":0,"errorCount":0,"requestCount":1,"rollingCountBadRequests":0,"rollingCountCollapsedRequests":0,"rollingCountEmit":0,"rollingCountExceptionsThrown":0,"rollingCountFailure":0,"rollingCountFallbackEmit":0,"rollingCountFallbackFailure":0,"rollingCountFallbackMissing":0,"rollingCountFallbackRejection":0,"rollingCountFallbackSuccess":0,"rollingCountResponsesFromCache":0,"rollingCountSemaphoreRejected":0,"rollingCountShortCircuited":0,"rollingCountSuccess":0,"rollingCountThreadPoolRejected":0,"rollingCountTimeout":0,"currentConcurrentExecutionCount":0,"rollingMaxConcurre...
    5. localhost:9001/hystrix页面中输入要检测的服务端点:localhost:8004/hystrix.streamDelay监测延时(默认每隔2s监测),Title(给服务起名,任意):

      最后点击 Monitor Stream

      如果报错 Unable to connect to Command Metric Stream.,则访问 localhost:8004确定有hystrix流不断响应(否则先访问一下 localhost:8004/dept/get/1

      Hystrix Dashboard Wiki上详细说明了图上每个指标的含义,如下图:

总结

要使用 Hystrix Dashboard必须满足一下条件:

  1. 服务端引入了 spring-boot-starter-actuator依赖
  2. 服务端启动类必须添加 @EnableCircuitBreaker,因为 Hystrix Dashboard监测的是服务端的 /hystrix.streamhystrix通过actuator提供的一个端点,用来记录通过断路器 circuit breaker发送的请求)
  3. Hystrix Dashboard工程需要添加 hystrixhystrix-dashboard依赖,并在启动类上添加 @EnableHystrixDashboard注解
  4. 如果服务端出于刚启动状态,则需访问以下服务端url如 /dept/get/1,否则 /hystrix.stream不会显示 data信息
  5. Dashboard工程填写服务端 hostname:port/hystrix.stream

Dashboard说明

  • Delay:该参数用来控制服务器上轮询监控信息的延迟时间,默认是2000毫秒,可以通过配置该属性来降低客户端的网络和CPU消耗.
  • Title:该参数对应了监控面板头部标题Hystrix Stream之后的内容,默认会使用具体监控实例的URL,可以通过配置该信息来展示更合适的标题
  • 实心圆:共有两种含义.它通过颜色的变化代表了 实例健康程度,它的健康度从绿色<黄色<橙色<红色递减.
    该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大.所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压力实例.
  • 曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势.(可以通过不断刷新/dept/get/1来测试它的变化)
鼓励一下~