1. 简介

1.1 分布式系统面临的问题

复杂分布式体系结构中的应用程序:有数10个依赖关系,每个依赖关系在某些时候将不可避免地失败

image.png
image.png

1.2 HyStrix简介

image.png

1.3 HyStrix功能

1.3.1 服务降级 fallback

服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback

1.3.1.1 降级场景

  • 程序运行异常
    • 出错(宕机或程序运行出错)———出错要有兜底
  • 超时
    • 超时导致服务器变慢(转圈)———超时不再等待
  • 服务熔断触发服务降级
  • 线程池/信号量也会导致服务降级(Tomcat)

    1.3.2 服务熔断 break

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

1.3.3 服务限流 flowlimit

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

1.3.4 接近实时的监控

2. 依赖

  1. <!--hystrix-->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  5. </dependency>

3. 服务降级

3.1 降级配置

注解:@HystrixCommand,加在服务提供方和服务消费方具有相同的作用但配置不一样,一般使用服务消费方

3.1.1 服务提供方

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

3.1.1.1 业务类

image.png
image.png
如上图,一旦调用服务方法失败并抛出了错误信息后,会自动调用 @HystrixCommand 标注好的fallbckMethod调用类中的指定方法

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

3.1.1.2 主启动类

// 提供方声明启用Hystrix
@EnableCircuitBreaker

3.1.2 服务消费方

3.1.2.1 YAML配置

feign:
  hystrix:
    enabled: true

3.1.2.2 主启动类

// 消费方声明启用Hystrix
@EnableHystrix

3.1.2.3 业务类

image.png

@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})

3.1.3 服务消费方的全局服务降级处理

自定义的服务消费方服务降级,每个业务方法对应一个兜底的方法,代码膨胀,可以定义一个统一的和自定义的分开 image.png

3.1.3.1 @DefaultProperties(defaultFallback=””)

image.png

3.1.3.2 业务类

/**
 * @author JShawn 2021/3/24 18:03
 */
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHyrixController {

    @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")
    })*/
    @HystrixCommand // 没有特别指明处理方法就用全局的
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
    {
        int age = 10/0;
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

    /**
     * 自定义fallback降级处理
     * @param id
     * @return
     */
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id)
    {
        return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
    }

    /**
     * 全局fallback降级处理
     * @param id
     * @return
     */
    public String payment_Global_FallbackMethod()
    {
        return "Global,我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
    }
}

3.1.4 通配(解耦合)服务降级

以上三种服务降级都与业务代码存在耦合,逻辑混乱。因此可以只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦 image.png

3.1.4.1 Feign接口的实现类

实现Feign接口,重写每一个方法,针对每一个方法做fallback处理,统一为接口里面的方法进行异常处理

/**
 * 实现Feign接口,重写每一个方法,针对每一个方法做fallback处理,统一为接口里面的方法进行异常处理
 * @author JShawn 2021/3/24 19:41
 */
@Component
public class PaymentFallbackService implements PaymentHystrixService {
    @Override
    public String paymentInfo_OK(Integer id) {
        return "------PaymentFallbackService fall back-paymentInfo_OK";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
        return "------PaymentFallbackService fall back-paymentInfo_TimeOut";
    }
}

3.1.4.2 Feign接口类

@Component
// 声明fallback实现类,如果某个接口遭遇服务降级,就会去调用相应的实现
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",
             fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {

    /**
     * 正常访问
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/ok/{id}")
    String paymentInfo_OK(@PathVariable("id") Integer id);

    /**
     * 超时访问
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/timeout/{id}")
    String paymentInfo_TimeOut(@PathVariable("id") Integer id);

3.1.4.3 主启动类

// 声明启用Hystrix
@EnableHystrix

3.1.4.4 YAML配置

feign:
  hystrix:
    enabled: true

4. 服务熔断

服务熔断一般应用在服务提供方,一句话就是家里的保险丝 默认:**5s****20次**调用失败启用熔断机制 image.png

4.1 业务类

//=====服务熔断
@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 不能负数");
    }
    String serialNumber = IdUtil.simpleUUID();

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

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

4.2 熔断类型

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

4.3 断路器原理

4.3.1 断路器何时起作用

image.png

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

十一、HyStrix------熔断器 - 图13

4.3.3 断路器作用

image.png

5. HyStrix所有配置

5.1 代码

//========================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";

}

5.2 图片

image.png
image.png
image.png
image.png

6. 服务限流

参考:十九、(Alibaba)Sentinel———熔断与限流 5. 流量控制

7. HyStrix工作流程

image.png
image.png

8. 服务监控hystrixDashboard

8.1 概述

新建cloud-consumer-hystrix-dashboard9001

image.png

8.2 依赖

必须要有actuator

<!--hystrix dashboard-->
<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>

8.3 DashBoard主启动类

// 声明启用Dashboard
@EnableHystrixDashboard

8.4 被监控的微服务主启动类

注意:新版本Hystrix需要在主启动MainAppHystrix8001中指定监控路径

/**
 * 此配置是为了服务监控而配置,与服务容错本身无观,springCloud 升级之后的坑
 * ServletRegistrationBean因为springboot的默认路径不是/hystrix.stream
 * 只要在自己的项目中配置上下面的servlet即可
 * @return
 */
@Bean
public ServletRegistrationBean getServlet(){
    HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
    ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(streamServlet);
    registrationBean.setLoadOnStartup(1);
    registrationBean.addUrlMappings("/hystrix.stream");
    registrationBean.setName("HystrixMetricsStreamServlet");
    return registrationBean;
}

8.5 监控

所有Provider微服务提供类(8001/8002/8003)都需要监控依赖部署 image.png 启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001 image.png image.png

8.5.1 断路器是否工作

8.5.1.1 关闭

image.png

8.5.1.2 打开

image.png

8.5.2 图形解释

image.png
image.png
image.png
image.png
image.png
image.png
image.png