【SpringCloud11】Hystrix断路器

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

Hystrix断路器

1.概述

1.1分布式系统面临的问题

复杂分布式体系结构中的应用程序有数十个依赖关系每个依赖关系在某些时候将不可避免地失败
在这里插入图片描述
服务雪崩
多个微服务之间调用的时候假设微服务A调用微服务B和微服务C微服务B和微服务C又调用其它的微服务这就是所谓的“扇出”如果扇出的链路上某个微服务的调用响应时间过长或者不可用对微服务A的调用就会占用越来越多的系统资源进而引起系统崩溃所谓的“雪崩效应”

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

所以通常当你发现一个模块下的某个实例失败后这时候这个模块依然还会接收流量然后这个有问题的模块还调用了其他的模块这样就会发生级联故障或者叫雪崩

1.2Hystrix 是什么

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

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

1.3Hystrix 的作用

  • 服务降级
  • 服务熔断
  • 接近实时的监控
  • 其他限流隔离等

1.4官网资料

官网

1.5Hystrix官宣停更进维

官方宣布停更进维
在这里插入图片描述

2.Hystrix重要概念

2.1服务降级fallback

  • 服务器忙请稍后再试不让客户端等待并立刻返回一个友好的提示fallback服务没停
  • 哪些情况会触发降级
    • 程序运行异常
    • 超时
    • 服务熔断触发服务降级
    • 线程池/信号量打满也会导致服务降级

2.2服务熔断break

  • 类比保险丝达到最大服务访问后直接拒绝访问拉闸限电然后调用服务降级的方法并返回友好提示
  • 就是保险丝服务的降级->进而熔断->恢复调用链路

2.3服务限流flowlimit

  • 秒杀高并发等操作严禁一窝蜂过来拥挤大家排队一秒钟N个有序进行

3.Hystrix案例

3.1构建

将7001先恢复成单机版集群版本演示启动太慢

1.新建cloud-provider-hystrix-payment8001

2.POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.atguigu.springcloud</groupId>
        <artifactId>cloud2022</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-provider-hystrix-payment8001</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--web-->
        <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><!-- 引入自己定义的api通用包可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </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>
</project>

3.YML

server:
  port: 8001

spring:
  application:
    name: cloud-provider-hystrix-payment

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
      defaultZone: http://eureka7001.com:7001/eureka

4.主启动

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 简要描述
 *
 * @Author: ASuLe
 * @Date: ${DATE} ${TIME}
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
@SpringBootApplication
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class, args);
    }
}

5.Controller

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * 简要描述
 *
 * @Author: ASuLe
 * @Date: 2023/1/13 20:56
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfoOk(@PathVariable("id") Integer id) {
        String result = paymentService.paymentInfo_OK(id);
        log.info("****result: " + result);
        return result;
    }

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfoTimeOut(@PathVariable("id") Integer id) throws InterruptedException {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("****result: " + result);
        return result;
    }
}

6.业务类

PaymentService

package com.atguigu.springcloud.service;

/**
 * 简要描述
 *
 * @Author: ASuLe
 * @Date: 2023/1/13 20:56
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
public interface PaymentService {

    String paymentInfo_OK(Integer id);

    String paymentInfo_TimeOut(Integer id);

}

PaymentServiceImpl

package com.atguigu.springcloud.service.impl;

import com.atguigu.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 简要描述
 *
 * @Author: ASuLe
 * @Date: 2023/1/13 20:57
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
@Service
public class PaymentServiceImpl implements PaymentService {

    /**
     * 正常访问
     * @param id
     * @return
     */
    @Override
    public String paymentInfo_OK(Integer id) {
        return "线程池" + Thread.currentThread().getName() + "paymentInfo_OK" + id + "\t" + "(●'◡'●)";
    }

    /**
     * 延迟超时
     * @param id
     * @return
     */
    @Override
    public String paymentInfo_TimeOut(Integer id) {
        int timeNumber = 3;
        try {
            TimeUnit.SECONDS.sleep(timeNumber);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return "线程池" + Thread.currentThread().getName() + "paymentInfo_TimeOut" + id + "\t" + "(●'◡'●)" + "耗时秒" + timeNumber;
    }
}

7.正常测试

  1. 启动eureka7001

  2. 启动cloud-provider-hystrix-payment8001

  3. 访问

    • success的方法http://localhost:8001/payment/hystrix/ok/1
      在这里插入图片描述

    • 每次调用耗费5秒钟http://localhost:8001/payment/hystrix/timeout/1
      在这里插入图片描述

  4. 上述均可成功以上述根基为平台从正确->错误->降级熔断->恢复

3.2高并发测试

3.2.1上述再非高并发情况下还能勉强满足但在高并发条件呢

Jmeter压力测试

  1. 开启Jmeter来20000个并发压死800120000个请求都去访问paymentInfo_TimeOut服务
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  2. 再来一个访问

    之前这个是不会转圈圈的现在也要转圈圈了
    在这里插入图片描述
    后台还未执行完在这里插入图片描述

  3. 看演示结果
    两个都在自己转圈圈
    为什么会被卡死
    tomcat的默认的工作线程数被打满了没有多余的线程来分解压力和处理。

    假设只有10个线程 你现在高并发2w个所有的线程都去处理TimeOut了没有多余的线程来分解压力和处理就导致即便是没有转圈圈的ok也变慢了被拖累了

3.2.2Jmeter压力结论

上面还是服务提供者8001自己测试假如此时外部的消费者80也来访问那消费者只能干等最终导致消费端80不满意服务端8001直接被拖死

3.2.3新建消费端80加入压力测试

1.新建cloud-consumer-feign-hystrix-order80

2.POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.atguigu.springcloud</groupId>
        <artifactId>cloud2022</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-consumer-feign-hystrix-order80</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--web-->
        <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.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>

</project>

3.YML

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * 简要描述
 *
 * @Author: ASuLe
 * @Date: ${DATE} ${TIME}
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class, args);
    }
}

4.主启动

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * 简要描述
 *
 * @Author: ASuLe
 * @Date: 2023/1/13 21:52
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
@RestController
@Slf4j
public class OrderHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

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

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

}

5.业务类

底层的接口调用的是payment所以是PaymentHystrixService

package com.atguigu.springcloud.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * 简要描述
 *
 * @Author: ASuLe
 * @Date: 2023/1/13 21:50
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {

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

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

6.正常测试

测试网址http://localhost/consumer/payment/hystrix/ok/1
在这里插入图片描述

7.高并发测试

  1. 2W线程压力测试8001
    在这里插入图片描述

  2. 消费端80微服务再去访问正常的ok微服务8001地址
    在这里插入图片描述

    消费者80要么转圈圈等待要么消费端报超时错误

    在这里插入图片描述

3.3故障现象和导致原因

8001同一层次的其他接口服务被困死因为tomcat线程池里面的工作线程已经被机战完毕

80此时调用8001客户端访问响应缓慢转圈圈

3.4上诉结论

因为存在上述故障表现或表现不佳所以才有降级/容错/限流等技术诞生

3.5如何解决解决的要求

1.超时导致服务器变慢转圈

超时就不再等待例如显示页面正在繁忙请稍后再试

2.出错宕机或程序运行出错

出错要有兜底

3.解决

  • 对方服务8001超时了调用者80不能一直卡斯等待必须有服务降级
  • 对方服务8001宕机了调用者80不能一直卡斯等待必须有服务降级
  • 对方服务8001OK调用者80自己故障或有自我要求自己的等待时间小于服务提供者自己处理降级

3.6服务降级

3.6.1降级配置

注解@HystrixCommand

官网用的是继承
在这里插入图片描述

3.6.2服务提供端8001从自身找问题

设置自身调用超时时间的峰值峰值内可以正常运行超过了需要有兜底的方法处理做服务降级fallback

将延迟超时时间3s改成5s
在这里插入图片描述

3.6.3服务提供端8001fallback

  • 业务类启用
    新增paymentInfo_TimeOutHandler方法

    public String paymentInfo_TimeOutHandler(Integer id){
            return "线程池" + Thread.currentThread().getName() + "paymentInfo_TimeOutHandler" + id + "\t" + "┭┮﹏┭┮" ;
    }
    

    在这里插入图片描述

    问题 加上@HystrixCommand注解后报异常如何处理

    一旦调用服务方法失败后抛出了错误信息后会自动调用@HystrixCommand标注好的fallbackMethod调用类中的方法

    继续在注解中添加内容规定execution.isolation.thread.timeoutInMilliseconds这个线程的超时时间是3秒钟即3秒钟以内是正常的业务逻辑但是我们方法内设置了5s的睡眠超过了峰值3秒上限就可以引发fallbackMethod的方法

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    

    在这里插入图片描述

  • 主启动类激活
    添加注解@EnableCircuitBreaker
    在这里插入图片描述

  • 测试生效
    在这里插入图片描述

  • 故意制造两个异常

    • int age = 10 / 0
    • 我们可以接受3s延迟让它睡5s超时异常

    在这里插入图片描述
    再次测试
    在这里插入图片描述

    结论当前服务不可用了做服务降级兜底的方案都是paymentInfo_TimeOutHandler

    结束上述操作还原异常
    在这里插入图片描述
    再次测试还原成功
    在这里插入图片描述

3.6.4消费端80fallback

80调用8001时8001设置了5s都是正常的但是80端认为2s是正常的那80马上就要降级保护

注意 我们自己配置过热部署方式对Java代码的改动是明显的但是对于@HystrixCommand内属性的修改建议重启微服务

  • YML
    添加配置

    feign:
      hystrix:
        enabled: true
    

    在这里插入图片描述

  • 启动类

    启动类上加上@EnableHystrix
    在这里插入图片描述

  • controller
    添加代码

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
        @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
        })
    public String paymentInfoTimeOut(@PathVariable("id") Integer id) {
        String result = paymentHystrixService.paymentInfoTimeOut(id);
        return result;
    }
    
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
        return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
    }
    

    在这里插入图片描述

    此时8001的业务层
    在这里插入图片描述
    此时80端的只等1.5s但是8001端要睡3s所以80客户端不等了直接降级保护了

    测试超时异常
    在这里插入图片描述

    测试报错
    在这里插入图片描述
    在这里插入图片描述

3.6.5目前问题

  • 每个业务方法对应一个兜底的方法会导致代码膨胀
  • 处理业务逻辑方法和处理异常服务降级的方法耦合在一起
  • 统一的全局fallback不需要一对一配置的和自定义的需要一一配置的分开

3.6.6解决问题

Ⅰ.解决每个方法配置一个服务降级方法

Feign接口系列
添加方法

/**
    * 全局fallback方法
    *
    * @return
    */
   public String paymentGlobalFallbackMethod() {
       return "Global异常处理信息请稍后再试/(ㄒoㄒ)/~~";
   }

在这里插入图片描述
添加注解 @DefaultProperties(defaultFallback="")

@DefaultProperties(defaultFallback = "paymentGlobalFallbackMethod")

在这里插入图片描述
根据就近原则会与具体指明的fallback产生冲突先注释掉突出全局fallback
在这里插入图片描述
重启80服务测试
在这里插入图片描述即没有配置过fallback的就找defaultFallback

对于@DefaultProperties(defaultFallback=“”)而言
11每个方法配置一个服务降级技术上可以实现但是很傻逼
1 : N 除了个别重要核心业务有专属其它普通的方法的fallback可以通过@DefaultProperties(defaultFallback=“”)统一跳转到统一处理服务降级页面
通用和独享的各自分开避免了代码膨胀合理减少了代码量

Ⅱ.解决业务逻辑方法和处理异常服务方法耦合混乱的问题

客户端80能够调用服务提供端8001全部业务逻辑方法是因为有PaymentHytrixService接口在controller为每个方法制定一个fallback方法导致高耦合、难以解耦不如在PaymentHytrixService接口中进行统一处理和降级就可以解决耦合问题

1.服务降级客户端去调用服务端碰上服务端宕机或关闭
2.案例
  • 本次案例服务降级处理是在客户端80实现完成的与服务端8001没有关系。

  • 只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦

3.未来要面对的异常
  • 运行
  • 超时
  • 宕机
4.修改cloud-consumer-feign-hytrix-order80
  • PaymentFallbackService类实现PaymentHystrixService接口

  • 根据cloud-consumer-feign-hytrix-order80已经有的PaymentHystrixService接口重新新建一个类(PaymentFallbackService)实现该接口统一为接口里面的方法进行异常处理

package com.atguigu.springcloud.service;

import org.springframework.stereotype.Component;

/**
 * 简要描述
 *
 * @Author: ASuLe
 * @Date: 2023/1/14 19:45
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
@Component
public class PaymentFallbackService implements PaymentHystrixService{
    @Override
    public String paymentInfoOk(Integer id) {
        return "----- PaymentFallbackService fall back paymentInfoOk,┭┮﹏┭┮";
    }

    @Override
    public String paymentInfoTimeOut(Integer id) {
        return "----- PaymentFallbackService fall back paymentInfoTimeOut,┭┮﹏┭┮";
    }
}

在这里插入图片描述

5.YML

这里之前已经打开了

feign:
  hystrix:
    enabled: true

在这里插入图片描述

6.PaymentHystrixService接口

PaymentHystrixService接口的
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")注解中添加fallback = PaymentFallbackService.class
在这里插入图片描述

7.测试
  1. 单个eureka先启动7001

  2. PaymentHystrixMain8001启动

  3. 正常访问测试
    在这里插入图片描述

  4. 故意关闭微服务8001模拟宕机

  5. 客户端自己调用提示
    在这里插入图片描述
    此时服务端provider已经宕机了但是我们做了服务降级处理让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器。

3.7服务熔断

3.7.1断路器

类比保险丝

3.7.2熔断是什么

熔断机制概述

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

在Spring Cloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况当失败的调用到一定阈值缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand

大神论文
在这里插入图片描述

正常的时候保险丝是关闭的但是电器太多了就跳闸了保险丝打开了那什么是半开假设现在限电了熔断器已经是open了假设跳闸了所有电器都不能用假设有少量的请求过来了尝试着能不能恢复例如只能承受1s承受100次的并发调用现在有500次请求服务接口就g了过了一会儿没有那么高的并发量了1s钟72次调用可以先尝试响应一波请求能不能成功慢慢的发现能够响应100

总结 降解是思想熔断是对降解的具体实现但是降解的实现并不止熔断这一种

  • 调用失败会触发降级而降级会调用fallback方法但无论如何降级的流程一定会先调用正常方法再调用fallback方法
  • 假如单位时间内调用失败次数过多也就是降级次数过多则触发熔断
  • 熔断以后就会跳过正常方法直接调用fallback方法所谓“熔断后服务不可用”就是因为跳过了正常方法直接执行fallback

3.7.3实操

1.修改cloud-provider-hytrix-payment8001

  • PaymentService

    String paymentCircuitBreaker(Integer id);
    

    在这里插入图片描述

  • PaymentServiceImpl

    // =====服务熔断=====
    @Override
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", 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 paymentCircuitBreaker(@PathVariable("id") Integer id) {
        if (id < 0) {
            throw new RuntimeException("******id 不能负数");
        }
        //hutool工具包依赖在公共模块
        String serialNumber = IdUtil.simpleUUID();
    
        return Thread.currentThread().getName() + "\t" + "调用成功流水号: " + serialNumber;
    }
    
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
        return "id 不能负数请稍后再试/(ㄒoㄒ)/~~   id: " + id;
    }
    

    在这里插入图片描述

    假设在时间窗口期10s中内10次请求里面超过百分之606次以上都是失败的这个断路器就是失败的

  • 为什么要配置上述参数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • PaymentController
    新增代码

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

    在这里插入图片描述

2.测试

正数测试正确调用
在这里插入图片描述
负数测试异常调用
在这里插入图片描述
测试多次负数断路器会跳闸这时再次输入正数也不会访问正确的方法需要隔一段时间发现正确方法访问率上升了错误方法访问率下降了慢慢的恢复相当于从open到halfopen再到close

  • 正确访问一次
    在这里插入图片描述

  • 错误访问一次
    在这里插入图片描述

  • 多次访问错误方法操作之后正确访问发现访问正确方法也错误了
    在这里插入图片描述

  • 过一段时间再去访问正确方法

    在这里插入图片描述

3.7.4原理小总结

1.大神结论

在这里插入图片描述

2.熔断类型

  • 熔断打开
    请求不再进行调用当前服务内部设置时钟一般为MTTR平均故障处理时间)当打开时长达到所设时钟则进入半熔断状态
  • 熔断关闭
    熔断关闭不会对服务进行熔断
  • 熔断半开
    部分请求根据规则调用当前服务如果请求成功且符合规则则认为当前服务恢复正常关闭熔断

3.官网断路器流程图

在这里插入图片描述

  1. 官网步骤
    在这里插入图片描述
  2. 断路器在什么情况下开始起作用
    在这里插入图片描述
    涉及到断路器的三个重要参数请求总数阈值、快照时间窗、错误百分比阈值
  • 请求总数阈值在快照时间窗内必须满足请求总数阀值才有资格熔断。默认为20意味着在10秒内如果该hystrix命令的调用次数不足20次即使所有的请求都超时或其他原因失败断路器都不会打开

  • 快照时间窗断路器确定是否打开需要统计一些请求和错误数据而统计的时间范围就是快照时间窗默认为最近的10秒【这个时间窗口期是打开短路器之后到尝试恢复期间拒绝请求的时间】

  • 错误百分比阈值当请求总数在快照时间窗内超过了阀值比如发生了30次调用如果在这30次调用中有15次发生了超时异常也就是超过50%的错误百分比在默认设定50%阀值情况下这时候就会将断路器打开

  1. 断路器开启或者关闭的条件

    1. 当满足一定的阀值的时候默认10秒内超过20个请求次数
    2. 当失败率达到一定的时候默认10秒内超过50%的请求失败
    3. 到达以上阀值断路器将会开启
    4. 当开启的时候所有请求都不会进行转发
    5. 一段时间之后默认是5秒这个时候断路器是半开状态会让其中一个请求进行转发如果成功断路器会关闭若失败继续开启。重复4和5
  2. 断路器打开之后

    • 再有请求调用的时候将不会调用主逻辑而是直接调用降级fallback通过断路器实现了自动地发现错误并将降级逻辑切换为主逻辑减少响应延迟的效果

    • 原来的主逻辑如何恢复
      对于这一问题hystrix也为我们实现了自动恢复功能。

      当断路器打开对主逻辑进行熔断之后hystrix会启动一个休眠时间窗在这个时间窗内降级逻辑是临时的成为主逻辑当休眠时间窗到期断路器将进入半开状态释放一次请求到原来的主逻辑上如果此次请求正常返回那么断路器将继续闭合主逻辑恢复如果这次请求依然有问题断路器继续进入打开状态休眠时间窗重新计时。

  3. All配置

    //========================All
    @HystrixCommand(fallbackMethod = "str_fallbackMethod",
            groupKey = "strGroupCommand",
            commandKey = "strCommand",
            threadPoolKey = "strThreadPool",
    
            commandProperties = {
                    // 设置隔离策略THREAD 表示线程池 SEMAPHORE信号池隔离
                    @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
                    // 当隔离策略选择信号池隔离的时候用来设置信号池的大小最大并发数
                    @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
                    // 配置命令执行的超时时间
                    @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
                    // 是否启用超时时间
                    @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                    // 执行超时的时候是否中断
                    @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
                    // 执行被取消的时候是否中断
                    @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
                    // 允许回调方法执行的最大并发数
                    @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
                    // 服务降级是否启用是否执行回调函数
                    @HystrixProperty(name = "fallback.enabled", value = "true"),
                    // 是否启用断路器
                    @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
                    // 该属性用来设置在滚动时间窗中断路器熔断的最小请求数。例如默认该值为 20 的时候
                    // 如果滚动时间窗默认10秒内仅收到了19个请求 即使这19个请求都失败了断路器也不会打开。
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
                    // 该属性用来设置在滚动时间窗中表示在滚动时间窗中在请求数量超过
                    // circuitBreaker.requestVolumeThreshold 的情况下如果错误请求数的百分比超过50,
                    // 就把断路器设置为 "打开" 状态否则就设置为 "关闭" 状态。
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                    // 该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后
                    // 会将断路器置为 "半开" 状态尝试熔断的请求命令如果依然失败就将断路器继续设置为 "打开" 状态
                    // 如果成功就设置为 "关闭" 状态。
                    @HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
                    // 断路器强制打开
                    @HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
                    // 断路器强制关闭
                    @HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
                    // 滚动时间窗设置该时间用于断路器判断健康度时需要收集信息的持续时间
                    @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
                    // 该属性用来设置滚动时间窗统计指标信息时划分"桶"的数量断路器在收集指标信息的时候会根据
                    // 设置的时间窗长度拆分成多个 "桶" 来累计各度量值每个"桶"记录了一段时间内的采集指标。
                    // 比如 10 秒内拆分成 10 个"桶"收集这样所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
                    // 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。
                    @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
                    // 该属性用来设置百分位统计的滚动窗口的持续时间单位为毫秒。
                    @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
                    // 该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。
                    @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
                    // 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数
                    // 就从最初的位置开始重写。例如将该值设置为100, 滚动窗口为10秒若在10秒内一个 “桶 ”中发生了500次执行
                    // 那么该 “桶” 中只保留 最后的100次执行的统计。另外增加该值的大小将会增加内存量的消耗并增加排序百分位数所需的计算时间。
                    @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
                    // 该属性用来设置采集影响断路器状态的健康快照请求的成功、 错误百分比的间隔等待时间。
                    @HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
                    // 是否开启请求缓存
                    @HystrixProperty(name = "requestCache.enabled", value = "true"),
                    // HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中
                    @HystrixProperty(name = "requestLog.enabled", value = "true"),
            },
            threadPoolProperties = {
                    // 该参数用来设置执行命令线程池的核心线程数该值也就是命令执行的最大并发量
                    @HystrixProperty(name = "coreSize", value = "10"),
                    // 该参数用来设置线程池的最大队列大小。当设置为 -1 时线程池将使用 SynchronousQueue 实现的队列
                    // 否则将使用 LinkedBlockingQueue 实现的队列。
                    @HystrixProperty(name = "maxQueueSize", value = "-1"),
                    // 该参数用来为队列设置拒绝阈值。 通过该参数 即使队列没有达到最大值也能拒绝请求。
                    // 该参数主要是对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue
                    // 队列不能动态修改它的对象大小而通过该属性就可以调整拒绝请求的队列大小了。
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
            }
    )
    public String strConsumer() {
        return "hello 2020";
    }
    public String str_fallbackMethod()
    {
        return "*****fall back str_fallbackMethod";
    }
    
    

3.8服务限流

在讲解alibaba的Sentinel的时候说明

4.Hystrix工作流程

工作流程图
工作流程官网
在这里插入图片描述

在这里插入图片描述
步骤说明中文

  1. 创建 HystrixCommand用在依赖的服务返回单个操作结果的时候 或 HystrixObserableCommand用在依赖的服务返回多个操作结果的时候 对象
  2. 命令执行。其中 HystrixComand 实现了下面前两种执行方式而 HystrixObservableCommand 实现了后两种执行方式execute()同步执行从依赖的服务返回一个单一的结果对象 或是在发生错误的时候抛出异常。queue()异步执行 直接返回 一个Future对象 其中包含了服务执行结束时要返回的单一结果对象。observe()返回 Observable 对象它代表了操作的多个结果它是一个 Hot Obserable不论 “事件源” 是否有 “订阅者”都会在创建后对事件进行发布所以对于 Hot Observable 的每一个 “订阅者” 都有可能是从 “事件源” 的中途开始的并可能只是看到了整个操作的局部过程。toObservable() 同样会返回 Observable 对象也代表了操作的多个结果但它返回的是一个Cold Observable没有 “订阅者” 的时候并不会发布事件而是进行等待直到有 “订阅者” 之后才发布事件所以对于 Cold Observable 的订阅者它可以保证从一开始看到整个操作的全部过程
  3. 若当前命令的请求缓存功能是被启用的 并且该命令缓存命中 那么缓存的结果会立即以 Observable 对象的形式返回
  4. 检查断路器是否为打开状态。如果断路器是打开的那么Hystrix不会执行命令而是转接到 fallback 处理逻辑第 8 步如果断路器是关闭的检查是否有可用资源来执行命令第 5 步
  5. 线程池/请求队列/信号量是否占满。如果命令依赖服务的专有线程池和请求队列或者信号量不使用线程池的时候已经被占满 那么 Hystrix 也不会执行命令 而是转接到 fallback 处理逻辑第8步
  6. Hystrix 会根据我们编写的方法来决定采取什么样的方式去请求依赖服务。HystrixCommand.run() 返回一个单一的结果或者抛出异常。HystrixObservableCommand.construct() 返回一个Observable 对象来发射多个结果或通过 onError 发送错误通知
  7. Hystrix会将 “成功”、“失败”、“拒绝”、“超时” 等信息报告给断路器 而断路器会维护一组计数器来统计这些数据。断路器会使用这些统计数据来决定是否要将断路器打开来对某个依赖服务的请求进行 “熔断/短路”
  8. 当命令执行失败的时候 Hystrix 会进入 fallback 尝试回退处理 我们通常也称该操作为 “服务降级”。而能够引起服务降级处理的情况有下面几种第4步 当前命令处于"熔断/短路"状态断路器是打开的时候。第5步 当前命令的线程池、 请求队列或 者信号量被占满的时候。第6步HystrixObservableCommand.construct() 或 HystrixCommand.run() 抛出异常的时候
  9. 当Hystrix命令执行成功之后 它会将处理结果直接返回或是以Observable 的形式返回

5.启用服务监控HystrixDashboard

5.1概述

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

5.2仪表盘9001

1.新建cloud-consumer-hystrix-dashboard9001

2.POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.atguigu.springcloud</groupId>
        <artifactId>cloud2022</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-consumer-hystrix-dashboard9001</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <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>
</project>

3.YML

server:
  port: 9001

4.启动类

package com.atguigu.springcloud;

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

/**
 * 简要描述
 *
 * @Author: ASuLe
 * @Date: ${DATE} ${TIME}
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain9001.class, args);
    }
}

5.所有Provider微服务提供类8001/8002/8003都需要监控依赖配置

所有Provider微服务提供类添加POM这里之前已经添加了

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

6.启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001

测试网址 http://localhost:9001/hystrix
在这里插入图片描述

6.Hystrix图形化Dashboard监控实战

6.1修改cloud-provider-hystrix-payment8001

6.1.1这两个依赖必须导入这里之前已经导入

<!--web-->
<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>

6.1.2在被监控服务的启动类添加

如果不添加代码会报错Unable to connect to Command Metric Stream

@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;
}

在这里插入图片描述

6.2监控测试

6.2.1启动eureka或者eureka集群均可

6.2.2监控测试

  1. 9001监控8001
    在这里插入图片描述

  2. 测试地址

  • 正确地址http://localhost:8001/payment/circuit/1
  • 错误地址http://localhost:8001/payment/circuit/-1
    多次正确地址访问
    在这里插入图片描述
    多次错误地址访问
    在这里插入图片描述

    先访问正确地址在访问错误地址在正确地址会发现图示断路器都是慢慢放开的。

  1. 如何看
    7色

    1圈
    实心圆共有两种含义它通过颜色的变化代表了实例的健康程度它的健康度从绿色<黄色<橙色<红色递减。该实心圆除了颜色的变化之外它的大小也会根据实例的请求流量发生变化流量越大该实心圆就越大。所以通过该实心圆的展示就可以在大量的实例中快速的发现故障实例和高压力实例

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

    整图说明
    在这里插入图片描述

    整图一个的说明
    在这里插入图片描述
    监控多个
    在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring