SpringCloud - Hystrix断路器-服务熔断与降级和HystrixDashboard

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: SpringCloud - Hystrix断路器-服务熔断与降级和HystrixDashboard

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

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

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


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


【1】服务熔断

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

当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务调用的状况,当失败的调用到一定阈值就会启动熔断机制(默认是5秒内20次调用失败就会启动熔断机制)。

熔断机制的注解:@HystrixCommand。

① 参考/microservicecloud-provider-dept-8001创建/microservicecloud-provider-dept-hystrix-8001

基础代码延用上篇博文Feign负载均衡与Ribbon代码


② 添加Hystrix依赖

  <dependencies>
    <!-- 引入自己定义的api通用包,可以使用Dept部门Entity -->
    <dependency>
      <groupId>com.web.springcloud</groupId>
      <artifactId>microservicecloud-api</artifactId>
      <version>${project.version}</version>
    </dependency>
    <!-- actuator监控信息完善 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- Hystrix -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-hystrix</artifactId>
    </dependency>
    <!-- 将微服务provider侧注册进eureka -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jetty</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-test</artifactId>
    </dependency>
    <!-- 修改后立即生效,热部署 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>springloaded</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
    </dependency>
  </dependencies>

③ yml文件配置

server:
  port: 8001
mybatis:
  config-location: classpath:mybatis/mybatis.cfg.xml        # mybatis配置文件所在路径
  type-aliases-package: com.web.springcloud.entities    # 所有Entity别名类所在包
  mapper-locations:
  - classpath:mybatis/mapper/**/*.xml                       # mapper映射文件
spring:
   application:
    name: microservicecloud-dept
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: com.mysql.jdbc.Driver              # mysql驱动包
#    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/clouddb01              # 数据库名称
    username: root
    password: 123456
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: true
    testOnReturn: false
    poolPreparedStatements: true
    #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
#    dbcp2:
#      min-idle: 5                                           # 数据库连接池的最小维持连接数
#      initial-size: 5                                       # 初始化连接数
#      max-total: 5                                          # 最大连接数
#      max-wait-millis: 200                                  # 等待连接获取的最大超时时间
eureka:
  client: #客户端注册进eureka服务列表内
    service-url: 
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/      
#      defaultZone: http://localhost:7001/eureka/
  instance:
    instance-id: microservicecloud-dept8001-hystrix # 自定义服务实例Id
    prefer-ip-address: true     #访问路径可以显示IP地址     
# http://192.168.2.100:8001/info优化显示
info: 
  app.name: web-microservicecloud
  company.name: www.web.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$

项目结构如下图:



④ 修改DeptController

@RestController
public class DeptController
{
  @Autowired
  private DeptService service = null;
  @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
  //一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod中的指定方法
  @HystrixCommand(fallbackMethod = "processHystrix_Get")
  public Dept get(@PathVariable("id") Long id)
  {
    Dept dept = this.service.get(id);
    if (null == dept) {
      throw new RuntimeException("该ID:" + id + "没有没有对应的信息");
    }
    return dept;
  }
  public Dept processHystrix_Get(@PathVariable("id") Long id)
  {
    Dept dept = new Dept();
    dept.setDeptno(id);
    dept.setDname("该ID:" + id + "没有没有对应的信息,null--@HystrixCommand");
    dept.setDb_source("no this database in MySQL");
    return dept;
  }
}

⑤ 主程序类添加熔断支持

@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
@EnableDiscoveryClient//服务发现
@EnableCircuitBreaker //对Hystrix熔断机制支持
public class DeptProvider8001_Hystrix_App
{
  public static void main(String[] args)
  {
    SpringApplication.run(DeptProvider8001_Hystrix_App.class, args);
  }
}

⑥ 测试

首先启动三个服务中心7001 7002 7003然后启动/microservicecloud-provider-dept-hystrix-8001,接着启动/microservicecloud-consumer-dept-80,浏览器使用消费者请求一个数据库中不存在的记录。


【2】服务降级

一句话简要描述:整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。

服务降级处理是在客户端(消费者)完成的与服务端没有关系。

参考【1】服务熔断的处理方式,会发现这种方式是很可怕的。第一如果有多个方法,对应的FallBack方法也会随之膨胀;第二异常处理与业务逻辑高耦合,完全不符合Spring AOP面向切面的思想。

上面说了,服务降级是在客户端(消费者)处理的,又需要与业务解耦,使用面向切面的思想。再次参考@FeignClient注解源码:

/**
 * Annotation for interfaces declaring that a REST client with that interface should be
 * created (e.g. for autowiring into another component). If ribbon is available it will be
 * used to load balance the backend requests, and the load balancer can be configured
 * using a <code>@RibbonClient</code> with the same name (i.e. value) as the feign client.
 *
 * @author Spencer Gibb
 * @author Venil Noronha
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
  /**
   * The name of the service with optional protocol prefix. Synonym for {@link #name()
   * name}. A name must be specified for all clients, whether or not a url is provided.
   * Can be specified as property key, eg: ${propertyKey}.
   */
  @AliasFor("name")
  String value() default "";
  /**
   * The service id with optional protocol prefix. Synonym for {@link #value() value}.
   *
   * @deprecated use {@link #name() name} instead
   */
  @Deprecated
  String serviceId() default "";
  /**
   * The service id with optional protocol prefix. Synonym for {@link #value() value}.
   */
  @AliasFor("value")
  String name() default "";
  /**
   * Sets the <code>@Qualifier</code> value for the feign client.
   */
  String qualifier() default "";
  /**
   * An absolute URL or resolvable hostname (the protocol is optional).
   */
  String url() default "";
  /**
   * Whether 404s should be decoded instead of throwing FeignExceptions
   */
  boolean decode404() default false;
  /**
   * A custom <code>@Configuration</code> for the feign client. Can contain override
   * <code>@Bean</code> definition for the pieces that make up the client, for instance
   * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
   *
   * @see FeignClientsConfiguration for the defaults
   */
  Class<?>[] configuration() default {};
  /**
   * Fallback class for the specified Feign client interface. The fallback class must
   * implement the interface annotated by this annotation and be a valid spring bean.
   */
  Class<?> fallback() default void.class;
/**注意这里注释!!!!*/
  /**
   * Define a fallback factory for the specified Feign client interface.
   * //为指定的 Feign客户端接口定义一个fallback factory。
   * The fallback factory must produce instances of fallback classes that implement the interface
   * annotated by {@link FeignClient}. 
   * // 该工程必须产生一个标注了@FeignClient注解接口的实现类的实例。
   * The fallback factory must be a valid spring bean.
   *// 该工厂必须是一个可用的Spring bean--要注册进容器
   * @see feign.hystrix.FallbackFactory for details.
   */
  Class<?> fallbackFactory() default void.class;
  /**
   * Path prefix to be used by all method-level mappings. Can be used with or without
   * <code>@RibbonClient</code>.
   */
  String path() default "";
  /**
   * Whether to mark the feign proxy as a primary bean. Defaults to true.
   */
  boolean primary() default true;
}

解决方案如下:

① 修改/microservicecloud-api工程,根据已有的DeptClientService新建一个实现了FallbackFactory接口的类DeptClientServiceFallbackFactory

DeptClientServiceFallbackFactory实例如下:

@Component // 不要忘记添加,不要忘记添加
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService>
{
  @Override
  public DeptClientService create(Throwable throwable)
  {
    return new DeptClientService() {
      @Override
      public Dept get(long id)
      {
        Dept dept = new Dept();
        dept.setDeptno(id);
        dept.setDname("该ID:" + id + "没有没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭");
        dept.setDb_source("no this database in MySQL");
        return dept;
      }
      @Override
      public List<Dept> list()
      {
        return null;
      }
      @Override
      public boolean add(Dept dept)
      {
        return false;
      }
    };
  }
}

② 修改DeptClientService上的@FeignClient

@FeignClient(value = "MICROSERVICECLOUD-DEPT",fallbackFactory=DeptClientServiceFallbackFactory.class)
public interface DeptClientService
{
  @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
  public Dept get(@PathVariable("id") long id);
  @RequestMapping(value = "/dept/list", method = RequestMethod.GET)
  public List<Dept> list();
  @RequestMapping(value = "/dept/add", method = RequestMethod.POST)
  public boolean add(Dept dept);
}

③ 修改/microservicecloud-consumer-dept-feign工程的yml

server:
  port: 80
spring:
  application:
    name: microservicecloud-consumer
feign:
  hystrix:
    enabled: true
eureka:
  instance:
    prefer-ip-address: true # 注册服务的时候使用服务的ip地址
  client:
    register-with-eureka: false # 不向服务注册中心注册自己
    service-url: 
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/  
#      defaultZone: http://localhost:7001/eureka/

④ 测试

首先启动三个服务中心7001 7002 7003,然后启动服务提供者/microservicecloud-provider-dept-8001,最后启动服务消费者/microservicecloud-consumer-dept-feign。

正常访问:http://localhost/consumer/dept/get/1


将服务提供者关掉,然后再测试消费者:

可以看到,此时我的服务提供者provider已经down了,但是我们做了服务降级处理,让客户端在服务不可用的时候也会获得信息提示而不会挂起耗死服务器。


【3】服务监控Hystrix Dashboard

除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续的记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户。其中包括每秒执行多少请求,多少成功,多少失败等。


Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。SpringCloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。


① 新建module microservicecloud-consumer-hystrix-dashboard

pom文件如下:

   <dependencies>
  <!-- 自己定义的api -->
  <dependency>
    <groupId>com.web.springcloud</groupId>
    <artifactId>microservicecloud-api</artifactId>
    <version>${project.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <!-- 修改后立即生效,热部署 -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>springloaded</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
  </dependency>
  <!-- Ribbon相关 -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
  </dependency>
  <!-- feign相关 -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
  </dependency>
  <!-- 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>
   </dependencies>

② yml文件如下

server:
  port: 9001

③ 主启动类如下:

@SpringBootApplication
@EnableHystrixDashboard
public class DeptConsumer_DashBoard_App
{
  public static void main(String[] args)
  {
    SpringApplication.run(DeptConsumer_DashBoard_App.class, args);
  }
}

④ 服务提供者8001 8002 8003添加监控依赖

    <!-- actuator监控信息完善 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

⑤ 启动/microservicecloud-consumer-hystrix-dashboard


别乱猜,这货不是熊,这是豪猪!!!


⑥ 启动三个服务中心和/microservicecloud-provider-dept-hystrix-8001

访问http://localhost:8001/hystrix.stream:

可以看到该页面在不断获取数据!!!


⑦ 使用Hystrix Dashboard监控

页面如下填写,分别是监控URL,延迟时间和自定义名字。


⑧ 如何查看?

一圈–实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。

实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化。流量越大该实心圆也就越大。所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压力实例。


曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。



多次刷新http://localhost:8001/dept/get/1请求,然后查看Hystrix Dashboard如下:

连续发出六次请求http://localhost:8001/dept/get/1,如下图:

参数详细说明如下:



相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
5月前
|
设计模式 监控 Java
解析Spring Cloud中的断路器模式原理
解析Spring Cloud中的断路器模式原理
|
3月前
|
XML 监控 Java
Spring Cloud全解析:熔断之Hystrix简介
Hystrix 是由 Netflix 开源的延迟和容错库,用于提高分布式系统的弹性。它通过断路器模式、资源隔离、服务降级及限流等机制防止服务雪崩。Hystrix 基于命令模式,通过 `HystrixCommand` 封装对外部依赖的调用逻辑。断路器能在依赖服务故障时快速返回备选响应,避免长时间等待。此外,Hystrix 还提供了监控功能,能够实时监控运行指标和配置变化。依赖管理方面,可通过 `@EnableHystrix` 启用 Hystrix 支持,并配置全局或局部的降级策略。结合 Feign 可实现客户端的服务降级。
182 23
|
3月前
|
Java 对象存储 开发者
故障隔离与容错处理:Hystrix在Spring Cloud和Netflix OSS中的应用
故障隔离与容错处理:Hystrix在Spring Cloud和Netflix OSS中的应用
56 3
|
3月前
|
Java 微服务 Spring
微服务(九)-Hystrix(断路器)
微服务(九)-Hystrix(断路器)
|
5月前
|
监控 Java 开发者
Spring Cloud中的服务熔断与降级
Spring Cloud中的服务熔断与降级
|
5月前
|
监控 Java 数据中心
通用快照方案问题之服务雪崩问题如何解决
通用快照方案问题之服务雪崩问题如何解决
23 0
|
5月前
|
设计模式 监控 Java
深入理解Spring Cloud中的断路器模式
深入理解Spring Cloud中的断路器模式
|
6月前
|
监控 Java 微服务
Spring Cloud 之 Hystrix
Spring Cloud Hystrix 是一个用于处理分布式系统延迟和容错的库,防止雪崩效应。它作为断路器,当服务故障时通过监控短路,返回备用响应,保持系统弹性。主要功能包括服务降级和熔断:
|
6月前
|
自然语言处理 监控 开发者
springCloud之Sentinel流量路由、流量控制、流量整形、熔断降级
springCloud之Sentinel流量路由、流量控制、流量整形、熔断降级
127 0
|
6月前
|
监控
springCloud之Hystrix监控
springCloud之Hystrix监控