SpringCloud搭建微服务之Hystrix熔断器

1. 概述

1.1. 分布式系统需要解决的问题

复杂分布式体系结构中的应用程序有数十个依赖服务,每个依赖服务在某时将不可避免发生异常或失败。多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他微服务,这就叫扇出,如果扇出的链路上某个微服务的调用响应时间过长或者不可使用,对微服务A的调用就会占用越来越多的系统资源,进而引发整个系统的崩溃,称为雪崩效应,因此就需要一个组件来解决这个问题

1.2. 什么是Hystrix

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个系统服务失败,避免级联故障发生,以提高分布式系统的弹性。当某个服务发生故障后,通过断路器的故障监控向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩

1.3. 重要概念

服务降级:当程序运行异常、超时、服务熔断触发、线程池/信号量打满等情况下会让客户端不再等待并返回一个友好提示
服务熔断:请求量达到最大服务访问后,直接拒绝访问,然后调用服务降级的方法并返回友好提示
服务限流:高并发操作时,严禁一瞬间过来海量请求,要求一秒钟多少个请求有序进行

2. 服务提供端集成Hystrix

2.1. 引入核心依赖

在pom.xml文件中引入Hystrix依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2.2. 业务方法配置服务降级

@HystrixCommand(fallbackMethod = "timeOutHandler", commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
@GetMapping(value = "/getProviderInfoForHystrix/{message}")
public String getProviderInfoForHystrix(@PathVariable("message") String message) {
    try {
        TimeUnit.SECONDS.sleep(6);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Hello" + message + ", This is provider hystrix, The current time is " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}

public String timeOutHandler(String message) {
    return "Hello " + message + "调用服务接口超时或异常!";
}

2.3. 主启动类引入Hystrix配置

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class ProviderApplication {

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

3. 服务消费端集成Hystrix

3.1. 引入核心依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<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-openfeign</artifactId>
</dependency>

3.2. 配置application.yml文件

server:
  port: 8800
spring:
  application:
    name: cloud-hystrix
eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka

3.3. 编写主启动类

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class HystrixApplication {

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

3.4. 编写业务接口

@RestController
@RequestMapping("/hystrix")
public class HystrixController {
    
    @Autowired
    private ProviderClient providerClient;

    @GetMapping(value = "/getHystrixInfoTimeOut/{message}")
    @HystrixCommand(fallbackMethod = "timeOutFallback", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
    })
    public String getHystrixInfoTimeOut(@PathVariable("message") String message) {
        return providerClient.getProviderInfoForHystrix(message);
    }

    public String timeOutFallback(String message) {
        return "This is Hystrix consumer, The request Provider time out, The message is " + message;
    }
}

3.5. 业务类配置默认返回类

由于每个方法都配置一个异常处理类,会导致代码膨胀,可以在类上配置默认的异常处理类,在需要特殊处理的方法上再配置单独的异常处理方法

@RestController
@RequestMapping("/hystrix")
@DefaultProperties(defaultFallback = "providerDefaultFallback")
public class HystrixController {

    @Autowired
    private ProviderClient providerClient;
    
    @HystrixCommand
    @GetMapping(value = "/getHystrixInfo/{message}")
    public String getHystrixInfo(@PathVariable("message") String message) {
        return providerClient.getProviderInfoForHystrix(message);
    }

    public String providerDefaultFallback() {
        return "This is Hystrix consumer The global error fallback";
    }
}

3.6. 编写异常请求类

上面两种方法都是在业务代码中处理异常业务,这样容易导致异常代码和业务代码混淆在一起,可以单独将异常处理拧出来,在Feign接口上引用该异常类即可

@Component
public class ProviderClientFallback implements ProviderClient {

    @Override
    public String getProviderInfoForHystrix(String message) {
        return "failBack provider info rest service call failed!";
    }
}

在Feign接口上引用该异常类

@Component
@FeignClient(value = "CLOUD-PROVIDER", configuration = ProviderClientFallback.class)
public interface ProviderClient {

    @GetMapping(value = "/provider/getProviderInfoForHystrix/{message}")
    String getProviderInfoForHystrix(@PathVariable("message") String message);
}

在yml配置中开启Feign的hystrix支持

feign:
  hystrix:
    enabled: true

3.7. 编写异常回退工厂类

定义和使用一个Fallback回退处理工厂类

@Component
public class ProviderClientFallbackFactory implements FallbackFactory<ProviderClient> {
    
    @Override
    public ProviderClient create(Throwable throwable) {
        return new ProviderClient() {
            @Override
            public String getProviderInfoForHystrix(String message) {
                return "FallbackFactory fallback: provider rest service call failed!";
            }
        };
    }
}

在Feign接口引入该工厂类

@Component
@FeignClient(value = "CLOUD-PROVIDER", configuration = FeignConfiguration.class, fallbackFactory = ProviderClientFallbackFactory.class)
public interface ProviderClient {

    @GetMapping(value = "/provider/getProviderInfoForHystrix/{message}")
    String getProviderInfoForHystrix(@PathVariable("message") String message);
}

回退类和回退工厂类区别

  • 使用回退类时,远程调用RPC过程中所引发的异常已经被回退逻辑彻底屏蔽掉了,应用程序不方便干预,也看不到RPC过程中的具体异常
  • 使用回退工厂类时,应用程序可以通过Java代码对RPC异常进行拦截和处理

4. 服务熔断

当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制

4.1. 注解配置

在服务端新增测试熔断方法

@GetMapping(value = "/getProviderInfoForCircuitBreaker/{id}")
@HystrixCommand(fallbackMethod = "circuitBreakerFallback", commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")
})
public String getProviderInfoForCircuitBreaker(@PathVariable("id") Integer id) {
    if (id < 0) {
        throw new RuntimeException("id不能为负数!");
    }
    return "The id is " + id;
}

public String circuitBreakerFallback(@PathVariable("id") Integer id) {
    return "id 不能为负数,请稍后再试! id is " + id;
}

4.2. 三个重要参数

快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近10秒
请求总数阈值:在快照时间窗内,必须满足请求总数阈值才有资格熔断,默认20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开
错误百分比阈值:当请求总数在快照时间窗内超过了阈值,比如发生了30次调用,如果在这30次调用中有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阈值情况下,这时就会将断路器打开
断路器开启或关闭的条件
当满足一定的阈值的时候(默认10秒内超过20个请求次数)
当失败率达到一定的时候(默认10秒内超过50%的请求失败)
到达以上阈值,断路器将会开启
当开启的时候,所有请求都不会进行转发
一段时间后(默认5秒),此时断路器是半开状态,会让其中一个请求进行转发,如果成功,断路器会关闭,若失败,继续开启

4.3. yml文件配置

在yml文件中也可以进行服务熔断相关配置

hystrix:
  command:
    default:
      circuitBreaker:
        enabled: true #是否开启熔断器
        requestVolumeThreshold: 20 #窗口时间内最小请求数
        sleepWindowInMilliseconds: 5000 #打开后允许一次尝试的睡眠时间,默认5秒
        errorThresholdPercentage: 50 #窗口时间内熔断器开启的错误比例,默认50
      metrics:
        rollingStats:
          timeInMilliseconds: 1000 #滑动窗口时间
          numBuckets: 10 #滑动窗口的时间桶数

5. RPC保护之舱壁模式

对于不同的服务提供者可以设置不同的RPC调用线程池,让不同RPC通过专门的线程池请求到各自的Provider服务提供者,像舱壁一样对Provider进行隔离,对于不同的服务提供者设置不同的RPC调用线程池,这种模式被称为舱壁模式
优点
避免对单个Provider的RPC的消耗掉所有资源,从而防止由于某一个服务性能低而引起的级联故障和雪崩效应
Hystrix提供两种RPC隔离方式:线程池隔离和信号量隔离

5.1. 线程池隔离

每一个线程池都有一个Key,名为Thread Pool Key(线程池名)。如果没有为HystrixCommand指定线程池,Hystrix就会为HystrixCommand创建一个与Group Key(命令组Key)同名的线程池,当然,如果与Group Key同名的线程池已经存在,就直接进行关联。
线程池隔离配置如下:

hystrix:
  threadpool:
    default:
      coreSize: 10 #线程池核心线程数
      maximumSize: 20 线程池最大线程数
      allowMaximumSizeToDivergeFromCoreSize: true #线程池maximumSize最大线程数是否生效
      keepAliveTimeMinutes: 10 #可空闲时间,分钟
  command:
    default:
      execution:
        isolation:
          strategy: THREAD #配置请求隔离方式为线程池
          thread:
            timeoutInMilliseconds: 100000 #RPC执行超时时间,默认1000毫秒
            interruptOnTimeout: true #超时后是否中断方法的执行,默认true

5.2. 信号量隔离

信号量所起到的作用就像一个开关,而信号量的值就是每个命令的并发执行数量,当并发数高于信号量的值时就不再执行命令。信号量可以细分为run执行信号量和fallback回退信号量
IO线程在执行HystrixCommand命令之前需要抢到run执行信号量,成功之后才允许执行HystrixCommand.run()方法。如果争抢失败,就准备回退,但是在执行HystrixCommand.getFallback()回退方法之前,还需要争抢fallback回退信号量,成功之后才允许执行HystrixCommand.getFallback()回退方法。如果都获取失败,操作就会直接终止。
信号量隔离配置如下

hystrix:
  command:
      fallback:
        isolation:
          semaphore:
            maxConcurrentRequests: 10 #回退信号量大小,默认为10,信号量大小不能大于容器线程池大小

5.3. 线程池隔离与信号量隔离区别

线程池隔离信号量隔离
调用线程RPC线程与Web容器IO线程相互隔离RPC线程与Web容器IO线程相同
开销存在请求排队、线程调度、线程上下文切换无线程切换,开销低
异步支持不支持
并发量最大线程池大小最大信号量上限,且最大信号量需要小于IO线程数

6. RPC保护之熔断器模式

统计最近RPC调用发生错误的次数,然后根据统计值中的失败比例等信息决定是否允许后面的RPC调用继续,或者快速地失败回退
熔断器的3种状态如下:
关闭(closed):熔断器初始状态,RPC调用正常放行
开启(open):失败比例到一定的阈值后,熔断器进入开启状态,RPC将会快速失败,然后执行失败回退逻辑
半开启(half-open):打开一定时间后(睡眠窗口结束),熔断器进入半开启状态,小流量尝试进行RPC调用放行,如果尝试成功,熔断器就变为关闭状态,RPC调用正常,如果尝试失败,熔断器就变为开启状态,RPC调用快速失败

6.1. 熔断器配置

包含滑动窗口配置和熔断器自身配置

hystrix:
  command:
    default:
      circuitBreaker:
        enabled: true #是否开启熔断器
        requestVolumeThreshold: 20 #窗口时间内最小请求数
        sleepWindowInMilliseconds: 5000 #打开后允许一次尝试的睡眠时间,默认5秒
        errorThresholdPercentage: 50 #窗口时间内熔断器开启的错误比例,默认50
      metrics:
        rollingStats:
          timeInMilliseconds: 1000 #滑动窗口时间
          numBuckets: 10 #滑动窗口的时间桶数

6.2. HystrixCommand工作流程

  1. 判断是否使用缓存响应请求,若启用了缓存,且缓存可用,则直接使用缓存响应请求,Hystrix支持请求缓存,但需要用户自定义启动
  2. 判断熔断器是否开启,如果熔断器处于open状态,则跳至第5步
  3. 若使用线程池进行请求隔离,则判断线程池是否已占满,若已满则跳至第5步;若使用信号量进行请求隔离,则判断信号量是否耗尽,若耗尽则跳至第5步
  4. 使用HystrixCommand.run()方法执行具体业务逻辑,如果执行失败或者超时,就跳至第5步,否则跳至第6步
  5. 执行HystrixCommand.getFallback()服务降级处理逻辑
  6. 返回请求响应

7. 服务监控

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

7.1. 引入核心依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

7.2. 编写application.yml文件

server:
  port: 8801
spring:
  application:
    name: cloud-hystrix-dashboard
eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka
hystrix:
  dashboard:
    proxy-stream-allow-list: '*'

7.3. 编写主启动类

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {

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

主要新增注解@EnableHystrixDashboard

7.4. 验证

依次启动Eureka Server、Provider和HystrixDashboard consumer微服务
浏览器地址输入http://localhost:8801/hystrix
hystrix dashboard
Delay:用来控制服务器上轮询监控信息的延迟时间,默认2000毫秒,可以通过配置该属性来降低客户端的网络和CPU消耗
Title:对应头部标题Hystrix Stream之后的内容,默认会使用具体监控实例的URL,可以通过配置该信息来展示更合适的标题
填写监控地址http://localhost:8770/actuator/hystrix.stream、延迟时间和应用名称
微服务监控
点击Monitor Stream
监控页面
浏览器输入地址http://localhost:8801/hystrix/dashboard/getProviderInfoForCircuitBreaker/100,多次访问
访问正常监控
再次在浏览器地址输入http://localhost:8801/hystrix/dashboard/getProviderInfoForCircuitBreaker/-100,多次访问
错误监控
实心圆:通过颜色的变化代表了实例的健康程度,健康度从绿色<黄色<橙色<红色递减。其大小会根据实例的请求流量发生变化,流量越大该实心圆就越大
曲线:用来记录2分钟内流量的相对变化,通过其来观察到流量的上升和下降趋势
错误解析

8. 聚合监控

dashboard用于监控单个微服务,如果需要监控多个微服务,就需要用到turbine

8.1. 引入核心依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>

8.2. 编写application.yml文件

server:
  port: 8802
spring:
  application:
    name: cloud-turbine
eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka
hystrix:
  dashboard:
    proxy-stream-allow-list: '*'
turbine:
  aggregator:
    cluster-config: default
  app-config: cloud-provider8771,cloud-provider8772 #服务列表
  cluster-name-expression: new String('default')

8.3. 编写主启动类

@EnableTurbine
@SpringBootApplication
@EnableHystrixDashboard
public class TurbineApplication {

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

8.4. 验证

启动两个微服务,在浏览器输入地址http://localhost:8802/hystrix
turbine监控面板
多次访问两个微服务接口,在面板地址栏输入http://localhost:8802/turbine.stream,点击Monitor Stream查看监控界面信息
turbine监控详情

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值