Hystrix是怎么实现熔断的

1.为什么需要熔断

分布式架构中,一个应用依赖很多服务非常常见,如果其中依赖的一个服务由于延迟过高产生阻塞,导致依赖该服务的应用也产生阻塞,如果该应用QPS较高,就会有大量的请求被阻塞,每个请求都占用了系统的CPU、内存、网络等资源,从而导致该应用由于服务器资源耗尽而拖垮。
如果这个应用还有上游服务,就会产生连锁反应,造成雪崩效果。
在服务延迟过高或者失败的时候,为系统提供保护机制快速返回失败,就可以缩短应用的等待时间,避免产生大面积请求阻塞和雪崩效应。

2.限流、降级、熔断的区别

限流:对并发访问的流量进行限制。主要行为有:限制并发数量、服务降级、分级请求(部分用户可以请求)、延时处理(削峰填谷),主要方式有:计数器(简单的流量技术+1)、漏斗模式(使用队列,队列长度限制)、令牌桶(先从令牌桶获取令牌才能访问)等。
降级:在流量高峰的时候,为了保全重要的服务,停掉一些不重要的服务,从而释放出更多资源。比如退款系统在流量高峰期,保障退款功能可用,停掉退款查询功能。
熔断:下游系统故障或者阻塞的时候,自动降级服务,快速返回错误。
熔断是降级的一种方式,降级又是限流的一种方式。

3.hystrix常用参数介绍

3.1 HystrixCommand和HystrixObservableCommand接口

HystrixCommand: 用在依赖的服务返回单个操作结果的时候。
HystrixObservableCommand:用在依赖的服务返回多个操作结果的时候,HystrixObservableCommand使用观察者模式

3.2 execute()、queue()、observe()、toObservable()命令

HystrixCommand实现了4个方法、HystrixObservableCommand实现了后面两个方法。
execute (): 同步执行,从依赖的服务返回一个单一的结果对象,或是在发生错误的时候抛出异常。
queue (): 异步执行,直接返回一个Future对象,其中包含了服务执行结束时要返回的单一结果对象。
observe () :返回Observable对象,它代表了操作的多个结果,它是一个Hot Observable。
toObservable(): 同样会返回Observable对象,也代表了操作的多个结果,但它返回的是 一个Cold Observable。

3.3 hystrix的一些默认值

circuitBreaker.sleepWindowInMilliseconds:断路器的快照时间窗,也叫做窗口期。可以理解为一个触发断路器的周期时间值,默认为5秒(5000)。
这个时间内如果发生断路的请求数超过了设置数则会熔断;如果发生了熔断,那么在这个时间后会成为半开状态。
circuitBreaker.requestVolumeThreshold:断路器的窗口期内触发断路的请求阈值,默认为20。
窗口期内的请求总数超过这个阈值才会判断错误率是否超过,如果超过就熔断。不超过这个阈值,不会发生熔断。
circuitBreaker.errorThresholdPercentage:断路器的窗口期内能够容忍的错误百分比阈值,默认为50(也就是说默认容忍50%的错误率)。
一个窗口期内,发生了100次服务请求,其中50次出现了错误。在这样的情况下,断路器将会被打开。

4.hystrix处理流程

在这里插入图片描述

4.1 创建HystrixCommand或HystrixObservableCommand对象
4.2 执行execute()、queue()、observe()、toObservable()命令
4.3 如果请求缓存功能被起用,并且命中缓存,缓存的结果会立即以Observable对象的形式返回
4.4 检查断路器是不是打开的状态,如果是打开的状态,直接执行getFallback()
4.5 如果和命令相关的线程池/请求队列/信号量 已经被占满,hystrix不会执行这个命令,直接执行getFallback()
4.6 执行HystrixCommand.run()或HystrixObservableCommand.construct(),如果这两个方法执行失败或者超时,则执行getFallback()
4.7 Hystrix会将“成功”、“失败”、“拒绝”、“超时” 等信息报告给断路器,而断路器会维护一组计数器来统计这些数据。断路器会使用这些统计数据来决定是否要将断路器打开,来对某个依赖服务的请求进行“熔断/短路”,直到恢复期结束。若在恢复期结束后,根据统计数据判断如果还是未达到健康指标,就再次 “熔断/短路”。
4.8 当Hystrix命令执行成功之后,它会将处理结果直接返回或是以Observable 的形式返回。

5.hystrix源码分析

5.1 HystrixCircuitBreaker

public interface HystrixCircuitBreaker {
    //每个Hystrix命令的请求都通过它判断是否被执行
    public boolean allowRequest();
    //返回当前断路器是否打开
    public boolean isOpen();
    //用来闭合断路器 
    void markSuccess();
 
    public static class Factory {...}
 
    static class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {...}
 
    static class NoOpCircuitBreaker implements HystrixCircuitBreaker {...}
}

5.2 HystrixCircuitBreakerImpl

//断路器对应HystrixCommand实例的属性对象
private final HystrixCommandProperties properties;
//用于让HystrixCommand记录各类度量指标的对象
private final HystrixCommandMetrics metrics;
//断路器是否打开的标志,默认为false 
private AtomicBoolean circuitOpen = new AtomicBoolean(false);
//断路器打开或上一次测试的时间戳
private AtomicLong circuitOpenedOrLastTestedTime = new AtomicLong();

5.3 isOpen()

public boolean isOpen() {
    //判断断路器的打开/关闭状态
    //如果断路器打开标识为true, 则直接返回true
    if (circuitOpen.get()) {
        return true;
    }
    //就从度量指标对象 metrics 中获取 HealthCounts 统计对象做进一步判断(该对象记录了 一个滚动时间窗内的请求信息快照,默认时间窗为10秒)
    HealthCounts health = metrics.getHealthCounts();
    
    //如果它的请求总数(QPS)在预设的阈值范围内就返回 false
    //阈值的配置参数为circuitBreakerRequestVolumeThreshold,默认值为20 
    if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
        return false;
    }
    
    //如果错误百分比在阈值范围内就返回 false
    //该阈值的配置参数为 circuitBreakerErrorThresholdPercentage, 默认值为50
    if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
        return false;
    } else {
        // our failure rate is too high, trip the circuit
        //如果失败率超过阈值,就进行熔断
        if (circuitOpen.compareAndSet(false, true)) {
            // if the previousValue was false then we want to set the currentTime
            //如果之前是false,设置熔断的当前时间
            circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
            return true;
        } else {
            return true;
        }
    }
}

5.4 allowRequest()

public boolean allowRequest() {
    //根据配置对象properties中断路器判断强制打开或关闭属性是否被设置。如果强制打开,就直接返回false,拒绝请求
    if (properties.circuitBreakerForceOpen().get()) {
        return false;
    }
    //如果强制关闭,它会允许所有请求
    if (properties.circuitBreakerForceClosed().get()) {
        //调用 isOpen ()来执行断路器的计算逻辑,用来模拟断路器打开/关闭的行为
        isOpen();
        return true;
    }
    //!isOpen () I I allowSingleTest ()来判断是否允许请求访问
    return !isOpen() || allowSingleTest();
}
public boolean allowSingleTest() {
    //获取断路器开启的时间
    long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get();
    //如果断路器开启,并且过了休眠期(窗口期)本次请求允许访问
    if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) {
        if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) {
            return true;
        }
    }
    return false;
}

5.5 markSuccess()

该函数用来在 “半开路” 状态时使用。若Hystrix 命令调用成功,通过调用它将打开的断路器关闭, 并重置度量指标对象。

public void markSuccess() {
    if (circuitOpen.get()) {
        if (circuitOpen.compareAndSet(true, false)) {
            metrics.resetStream();
        }
    }
}

参考文档:
1.降级-熔断-限流-傻傻分不清楚 https://zhuanlan.zhihu.com/p/61363959
2.一文带大家彻底弄懂Hystrix原理机制! https://zhuanlan.zhihu.com/p/363518269
3.Hystrix熔断器使用时的三个参数介绍 https://blog.csdn.net/weixin_54707282/article/details/123406787
4.hystrix实现原理 https://www.jianshu.com/p/fc19f6ed6d0d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
5.Hystrix实现原理 https://blog.csdn.net/skh2015java/article/details/120750565

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值