Spring Cloud(十一):Hystrix服务熔断-工作流程

1. Hystrix服务熔断

1.1 断路器

类似保险丝

1.2 熔断是什么

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

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

大神论文:https://martinfowler.com/bliki/CircuitBreaker.html

1.3 修改cloud-provider-hystrix-payment8001

  • PaymentService
package com.lele.springcloud.service;

import cn.hutool.core.util.IdUtil;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.ribbon.proxy.annotation.Hystrix;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.concurrent.TimeUnit;

/**
 * @author: lele
 * @date: 2021/3/20 13:53
 * @description:
 */
@Service
public class PaymentService {
    
    

    /**
     * 正常访问:肯定OK
     * @param id
     * @return
     */
    public String paymentInfo_OK(Integer id) {
    
    
        return "线程池:" + Thread.currentThread().getName()+"  paymentInfo_OK,id:" + id+"\t"+"哈哈~";
    }

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
    
    
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="5000")
    })
    public String paymentInfo_TimeOut(Integer id) {
    
    
//        int timeNumber = 5;
//         int age = 10/0;
        try {
    
    
            TimeUnit.MILLISECONDS.sleep(3000);
        } catch(InterruptedException e) {
    
    
            e.printStackTrace();
        }
//        return "线程池:"+ Thread.currentThread().getName()+"  paymentInfo_OK,id" + id+"\t"+"~~"+"耗时(秒)"+timeNumber;
        return "线程池:"+ Thread.currentThread().getName()+"  paymentInfo_OK,id" + id+"\t"+"哈哈~~"+"耗时(秒)";
    }

    public String paymentInfo_TimeOutHandler(Integer id) {
    
    
        return "线程池:"+Thread.currentThread().getName()+"  8001系统繁忙或者运行报错,请稍候再试,id: "+id+"\t"+"(╥╯^╰╥)";
    }

    //======服务熔断
    @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 不能为负数,请稍候再试,id:"+id;
    }
}

  • PaymentController 添加
    在这里插入图片描述

1.4 测试

正确访问:http://localhost:8001/payment/circuit/1
在这里插入图片描述

错误访问:http://localhost:8001/payment/circuit/-1
在这里插入图片描述

多次错误,然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行访问,需要慢慢的恢复链路

1.5 总结

大神结论:
在这里插入图片描述
熔断类型

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

官网断路器流程图
在这里插入图片描述
断路器在什么情况下开始起作用:
在这里插入图片描述
涉及到断路器的三个重要参数:快照时间窗、请求总数阈值、错误百分比阈值
1、快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒

2、请求总数阈值:在快照时间窗内,必须满足请求总数阈值才有资格熔断,默认为20,意味着在10秒内,如果该Hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。

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

断路器开启或者关闭的条件
1、当满足一定阀值的时候(默认10秒内超过20个请求次数)

2、当失败率达到一定的时候(默认10秒内超过50%请求失败)

3、到达以上阀值,断路器将会开启

4、当开启的时候,所有请求都不会进行转发

5、一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5

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

2:、原来的主逻辑要如何恢复呢?
对于这一问题,Hystrix也为我们实现了自动恢复功能。
当断路器打开,对主逻辑进行熔断之后,Hystrix会启动一个休眠时间窗,在这个时间窗内如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。

ALL配置

package com.liang.cloud;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;

public class AllHystrixCommand {
    
    


    @HystrixCommand(fallbackMethod = "str. fallbackMethod",

            groupKey = "strGroupCommand",

            commandKey = "strCommarld",

            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"),

                    //熔动时间窗没置,该时间用于断路器判断健康度时需要收集信息的持续时间

                    @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "1000"),

                    //该属性用来没置熔动时间窗统计指标信息时划分”桶"的数量,断路器在收集指标信息的时候会根据

                    //设置的时间窗长度拆分成多个"桶”来累计各度量值,每个”桶" 记录了- -段时间内的来集指标。

                    //比如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"),

                    //HystrixProperty的执行和时间是否打印日志到HystrixRequestLog中

                    @HystrixProperty(name = "requestLog.enabled", value = "true"),

                    @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "10"),

                    //该属性用来汝置采集影响断路器状态的健康快照(请求的成功、错误百分 比)的间隔等待时间。

                    @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 All() {
    
    
        return "zhang";
    }
}

2. hystrix工作流程

官网:https://github.com/Netflix/Hystrix/wiki/How-it-Works

1 创建HystrixCommand(用在依赖的服务返回单个操作结果的时候)或HystrixObserableCommand(用在依赖的服务返回多个操作结果的时候)对象。
2 命令执行。其中HystrixCommand实现了下面前两种执行方式:而HystrixObserableCommand实现了后两种执行方式:execute():同步执行,从依赖的服务返回一个单一的结果对象,或是在发生错误的时候跑出异常。queue():异步执行,直接返回一个Future对象,其中包含了服务之行结束时要返回的单一结果对象。obserce():返回Observable对象,他代表了操作的多个结果,它是一个Hot Obserable(不论“事件源”是否有“订阅者”,都会在创建后对事件进行发布,所以对于Hot Observable 的每一个订阅者都有可能是从事件源的中途开始的,并可能只是看到了整个操作的局部过程。)toObserable():同样返回Obserable对象,也代表了操作的多个结果,但他返回的是一个Cold Observale(没有“订阅者”的时候并不会发布事件,而是进行等等待,直到有订阅者之后才发布事件,所以对于Cold Obserable的订阅者,他可以保证从一开始看到整个操作的全部过程)
3 若当前命令的请求缓存功能是被启用的,并且该命令缓存命中,那么缓存的结果会立即以Observable对象的形式返回。
4 检查断路器是否为打开状态,如果断路器是打开的,那么Hys不会执行命令,而是转接到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的形式返回

如果我们没有为命令实现降级逻辑或者在降级处理逻辑中抛出了异常,Hystrix依然会返回一个Observable对象,但是他不会发射任何结果数据,而是通过onError方法通知命令立即中断请求,并通过onError()方法将因其命令失败的异常发送给调用者。

猜你喜欢

转载自blog.csdn.net/houwanle/article/details/115042115