5-Hystrix熔断器


一、分布式系统面临的问题:服务雪崩

复杂分布式系统中的应用程序有数十个依赖关系,每个依赖关系在某些时候不可避免的失败。

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

二、Hystrix概念

1、是什么

​ Hystrix 是处理分布式系统的延迟和容错的开源库,保证一个依赖出现问题时不会导致整体服务失败,避免级联故障,以提高分布式系统弹性。
​ 断路器本身是一种开关装置,当某个服务单元发生故障后,通过断路器的故障监控,向调用方返回一个符合预期的可处理的备选响应,而不是长时间的等待或抛出调用方法无法处理的异常 。这样就保证了服务调用方的线程不会被长时间、不必要的占用,从而避免故障在分布式中蔓延,乃至雪崩。

官网:https://github.com/Netflix/Hystrix

2、服务降级

  1. 服务器忙,请稍后重试,不让客户端等待并立即返回一个友好的提示。
  2. 哪些情况会导致服务降级
    1. 程序运行异常
    2. 超时
    3. 服务熔断触发服务降级
    4. 线程池/信号量打满

3、服务熔断

  1. 类比保险丝达到最大服务访问时,直接拒绝访问,拉闸限电,然后调用服务降级的方法返回友好提示。就是访问的人太多,后面的人无法访问服务,只能访问降级的方法。
  2. 服务降级->进而熔断->恢复调用链路

4、服务限流

  1. 秒杀高并发等操作,严禁一窝蜂过来拥挤,一秒N个有序进行。

三、服务降级

服务降级既可以在服务提供方,也可以在服务消费方

但一般在服务消费方

1、服务提供方的服务降级

(1)PaymentService

@Service
public class PaymentService {

    /**
     * 正常访问
     * @return
     */
    public String paymentInfo_Ok(Integer id){
        return "线程池" +Thread.currentThread().getName() + "paymentInfo_ok,id:" + id +"\t";

    }


    //@HystrixCommand报异常后如何处理
    //一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbckMethod调用类中的指定方法
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler" ,commandProperties = {    
            //表示调用此方法的线程最多等待3秒,如果3秒没有响应,除法降级,调用指定方法
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds" ,value = "3000")
    })
    public String paymentInfo_timeOut(Integer id){
        long l = 3000L;

        //也可以通过触发异常,导致服务降级
        int i = 1/0;

        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "线程池" +Thread.currentThread().getName() + "paymentInfo_timeOut,id:" + id +"\t" +"耗时(s)"+l;
    }

    public String paymentInfo_TimeOutHandler(Integer id){
        return "线程池" +Thread.currentThread().getName() + "paymentInfo_timeOutHandler,id:" + id +"\t";
    }
}

(2)主启动类

添加注解@EnableCircuitBreaker激活

@SpringBootApplication
@EnableEurekaClient
//激活Hystrix
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}

2、服务消费方的服务降级

(1)application添加配置

feign:
  hystrix:
    enabled: true

(2)Controller

基本同服务提供方的Service

@RestController
@Slf4j
//配置全局服务fallback的方法,没有指定具体的fallback方法的降级时会调用这个方法,
//有指定具体的fallback方法的方法,直接调用指定的方法
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController
{
    @Resource
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id)
    {
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    /*@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
    })*/
    //该注解的属性为空时,fallback调用的是全局fallback方法
    @HystrixCommand
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
    {
        int age = 10/0;
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id)
    {
        return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
    }

    // 下面是全局fallback方法
    public String payment_Global_FallbackMethod()
    {
        return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
    }
}

3、代码优化(解耦)

(1)application.yml添加

feign:
  hystrix:
    enabled: true

(2)Service

服务降级,客户端去调用服务端,碰上服务端宕机或关闭

本次案例服务降级处理是在客户端80实现完成,与服务端8001没有关系 只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦

PaymentHystrixService接口是远程调用pay模块的,我们这里创建一个类实现service接口,在实现类中统一处理异常

@Component
//以fallback类中的`同名`的方法,作为降级方法
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT" ,fallback = PaymentFallbackService.class)
public interface PaymentHystrixService
{
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}

实现类

@Component
public class PaymentFallbackService implements PaymentHystrixService
{
    @Override
    public String paymentInfo_OK(Integer id)
    {
        return "-----PaymentFallbackService fall back-paymentInfo_OK ,o(╥﹏╥)o";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id)
    {
        return "-----PaymentFallbackService fall back-paymentInfo_TimeOut ,o(╥﹏╥)o";
    }
}
它的运行逻辑是:
        当请求过来,首先还是通过Feign远程调用pay模块对应的方法
    但是如果pay模块报错,调用失败,那么就会调用PaymentFallbackService类的
    当前`同名`的方法,作为降级方法

(3)Controller

就近原则:

如果Controller中的方法含有@HystrixCommand注解,则调用用该注解的fallback降级

否则调用Service的的fallback

四、服务熔断

熔断机制是应对服务雪崩效应的一种微服务链路保护机制,当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。当检测到该节点微服务响应正常后恢复调用链路,在SpringCloud框架机制通过Hystrix实现,Hystrix会监控微服务见调用的状况,当失败的调用到一个阈值,缺省是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是@HystrixCommand

当检测到在circuitBreaker.sleepWindowInMilliseconds的时间内,该节点微服务响应正常后,恢复调用链路。

服务降级->进而熔断->恢复链路

0、依赖以及开启熔断

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.5.RELEASE</version>
</dependency>

启动类上加注解

@EnableCircuitBreaker

1、服务提供方的Service

配置@HystrixCommand,及其属性如下

此处https://www.bilibili.com/video/BV18E411x7eT?p=61阳哥对属性的说明有误

@Service
public class PaymentServiceImpl implements PaymentService {
    /*===================服务熔断=========================*/
    //降级调用的方法
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",
        // 忽略的异常类型
        ignoreExceptions = {IOException.class},
        commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
            //一个统计窗口内(默认10s)请求数量达到此值(默认20)才会进行熔断与否的判断,不是要失败那么多个才熔断
            //即指定时间内达到该值才会对失败率进行判断,从而判断是否熔断
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20"),
            // 时间窗口期(短路多久以后开始尝试是否恢复,默认5s)(休眠时间)(单位毫秒)
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
            // 失败率(%)达到多少后熔断,出错百分比阈值,当达到此阈值后,开始短路。默认50%)
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),
        }
    )
    @Override
    public String paymentCircuitBreaker(Integer id){
        if(id < 0){
            throw new RuntimeException("******id 不能负数");
        }
        String serialNumber = IdUtil.simpleUUID();

        return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
    }


    // 降级方法
    public String paymentCircuitBreaker_fallback(Integer id){
        return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~   id: " +id;
    }
}

fallback方法的参数要跟你注解的方法参数保持一致,否则会报错。而且两个方法必须要在同一个类中,由于在同一个类中,所以fallback的修饰符没有限制

2、服务提供方的Controller

/*===================服务熔断=========================*/
    @GetMapping("/payment/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id)
    {
        String result = paymentService.paymentCircuitBreaker(id);
        log.info("****result: "+result);
        return result;
    }

3、测试结果

  1. localhost:8001/payment/circuit/{id}中的id为正时,正常显示
  2. 当id为负时,服务降级,显示id不能为负
  3. 当id多次为负,达到Service的注解@HystrixCommand中的限定值之一时,服务熔断,此时即使访问id为正也无法正常访问。必须等一段时间后恢复链路方可正常访问。

4、内部执行原理

断路器的打开和关闭,是按照一下5步决定的
      1,并发此时是否达到我们指定的阈值
      2,错误百分比,比如我们配置了60%,那么如果并发请求中,10次有6次是失败的,就开启断路器
      3,上面的条件符合,断路器改变状态为open(开启)
      4,这个服务的断路器开启,所有请求无法访问
      5,在我们的时间窗口期,期间,尝试让一些请求通过(半开状态),
          如果请求还是失败,证明断路器还是开启状态,服务没有恢复,重复执行5;
          如果请求成功了,证明服务已经恢复,断路器状态变为close关闭状态.

5、Hystrix可配置的其他属性

可查看HystrixCommandProperties.java类即可

五、Hystrix图形化Dashboard监控

1、Dashboard模块构建

1、新建模块

2、pom

 <dependencies>
        <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-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3、application.yml

server:
  port: 9001

4、主启动类,激活Dashboard

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

5、测试

访问localhost:9001/hystrix即可访问hystrix的图形化监控界面

6、坑

  1. 被监控的微服务(8001)必须包含以下两个依赖

    org.springframework.boot
     spring-boot-starter-web



     org.springframework.boot
    spring-boot-starter-actuator
  1. 注意:新版本Hystrix需要在主启动类MainAppHystrix8001中指定监控路径

被监控的微服务主启动类添加如下配置

否则报错Unable to connect to Command Metric Stream

@SpringBootApplication
@EnableEurekaClient
//激活Hystrix
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {

        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }



    /**
     *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
     *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
     *只要在自己的项目里配置上下面的servlet就可以了
     */
    @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;
    }
}

2、图形化界面使用

指定要监控的端口或集群:http://localhost:8001

参数详解

实心圆大小代表压力大小


  目录