在深入研究熔断器之前,我们需要先看一下Hystrix的几个重要的默认配置,这几个配置在HystrixCommandProperties

//时间窗(ms)
static final Integer default_metricsRollingStatisticalWindow = 10000;
//最少请求次数
private static final Integer default_circuitBreakerRequestVolumeThreshold = 20;
//熔断器打开后开始尝试半开的时间间隔
private static final Integer default_circuitBreakerSleepWindowInMilliseconds = 5000;
//错误比例
private static final Integer default_circuitBreakerErrorThresholdPercentage = 50;

这几个属性共同组成了熔断器的核心逻辑,即:

  1. 每10秒的窗口期内,当请求次数超过20次,且出错比例超过50%,则触发熔断器打开
  2. 当熔断器5秒后,会尝试放过去一部分流量进行试探
熔断器初始化

熔断器的初始化是在HystrixCircuitBreaker.FactorygetInstance方法

        private static ConcurrentHashMap<String, HystrixCircuitBreaker> circuitBreakersByCommand = new ConcurrentHashMap<String, HystrixCircuitBreaker>();

        public static HystrixCircuitBreaker getInstance(HystrixCommandKey key, HystrixCommandGroupKey group, HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
// this should find it for all but the first time
HystrixCircuitBreaker previouslyCached = circuitBreakersByCommand.get(key.name());
if (previouslyCached != null) {
return previouslyCached;
} // if we get here this is the first time so we need to initialize // Create and add to the map ... use putIfAbsent to atomically handle the possible race-condition of
// 2 threads hitting this point at the same time and let ConcurrentHashMap provide us our thread-safety
// If 2 threads hit here only one will get added and the other will get a non-null response instead.
HystrixCircuitBreaker cbForCommand = circuitBreakersByCommand.putIfAbsent(key.name(), new HystrixCircuitBreakerImpl(key, group, properties, metrics));
if (cbForCommand == null) {
// this means the putIfAbsent step just created a new one so let's retrieve and return it
return circuitBreakersByCommand.get(key.name());
} else {
// this means a race occurred and while attempting to 'put' another one got there before
// and we instead retrieved it and will now return it
return cbForCommand;
}
}

由上方代码可知,每一个熔断器都是由HystrixCircuitBreakerImpl实现的,而所有的熔断器都维护在circuitBreakersByCommand这个ConcurrentHashMap

熔断器实现
构造方法
class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
private final HystrixCommandProperties properties;
private final HystrixCommandMetrics metrics; enum Status {
CLOSED, OPEN, HALF_OPEN
} private final AtomicReference<Status> status = new AtomicReference<Status>(Status.CLOSED);
private final AtomicLong circuitOpened = new AtomicLong(-1);
private final AtomicReference<Subscription> activeSubscription = new AtomicReference<Subscription>(null); protected HystrixCircuitBreakerImpl(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, final HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
this.properties = properties;
this.metrics = metrics; //On a timer, this will set the circuit between OPEN/CLOSED as command executions occur
Subscription s = subscribeToStream();
activeSubscription.set(s);
}
}

先介绍一下几个比较基础的属性:

  1. HystrixCommandProperties:当前熔断器的配置
  2. HystrixCommandMetrics: 请求统计组件
  3. Status:熔断器状态枚举,一共包含三种,关闭、打开和半开
  4. status:当前熔断器的状态
  5. circuitOpened:当前熔断器的打开时间
  6. activeSubscription:订阅请求统计的处理函数
请求统计处理
private Subscription subscribeToStream() {
/*
* This stream will recalculate the OPEN/CLOSED status on every onNext from the health stream
*/
return metrics.getHealthCountsStream()
.observe()
.subscribe(new Subscriber<HealthCounts>() {
@Override
public void onCompleted() { } @Override
public void onError(Throwable e) { } @Override
public void onNext(HealthCounts hc) {
// check if we are past the statisticalWindowVolumeThreshold
if (hc.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
// we are not past the minimum volume threshold for the stat window,
// so no change to circuit status.
// if it was CLOSED, it stays CLOSED
// if it was half-open, we need to wait for a successful command execution
// if it was open, we need to wait for sleep window to elapse
} else {
if (hc.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
//we are not past the minimum error threshold for the stat window,
// so no change to circuit status.
// if it was CLOSED, it stays CLOSED
// if it was half-open, we need to wait for a successful command execution
// if it was open, we need to wait for sleep window to elapse
} else {
// our failure rate is too high, we need to set the state to OPEN
if (status.compareAndSet(Status.CLOSED, Status.OPEN)) {
circuitOpened.set(System.currentTimeMillis());
}
}
}
}
});
}

直接看onNext方法里的处理方式:

  1. 时间窗内的请求数量是否达标,按默认配置就是10秒钟的请求数是否超过20次,如果不达标不能开启熔断器
  2. else中首先判断错误比例是否达到比例,按默认就是50%
  3. 满足打开条件,使用CAS修改状态为打开,并记录打开时间circuitOpened为当前时间

当记录了当前应用的统计数据之后,在每次请求的时候就可以根据这些数据来判断是否应该打开熔断器了

请求过滤

不知你是否还记得在系列文章第一篇中曾经提到了一个方法applyHystrixSemantics,在这个方法中就包含了判断是否应该熔断的逻辑,如果熔断器打开的情况下会直接进入降级逻辑。这个判断的方法如下:

        public boolean attemptExecution() {
if (properties.circuitBreakerForceOpen().get()) {
return false;
}
if (properties.circuitBreakerForceClosed().get()) {
return true;
}
if (circuitOpened.get() == -1) {
return true;
} else {
if (isAfterSleepWindow()) {
if (status.compareAndSet(Status.OPEN, Status.HALF_OPEN)) {
//only the first request after sleep window should execute
return true;
} else {
return false;
}
} else {
return false;
}
}
}
  1. 第一个if,如果配置强制熔断则返回false表示开启熔断器进入降级逻辑
  2. 第二个,如果配置强制关闭则返回正常不进行后续的判断
  3. 第三个,打开时间为空则肯定没打开过
  4. 第四个,判断是否满足尝试时间,默认是5秒钟。时间计算方式如下:
private boolean isAfterSleepWindow() {
final long circuitOpenTime = circuitOpened.get();
final long currentTime = System.currentTimeMillis();
final long sleepWindowTime = properties.circuitBreakerSleepWindowInMilliseconds().get();
return currentTime > circuitOpenTime + sleepWindowTime;
}
  1. 当满足尝试时则使用CAS方式修改熔断器为半开状态

而当请求成功的时候则会调用如下方法清除统计数据,更改熔断器状态为关闭

 public void markSuccess() {
if (status.compareAndSet(Status.HALF_OPEN, Status.CLOSED)) {
//This thread wins the race to close the circuit - it resets the stream to start it over from 0
metrics.resetStream();
Subscription previousSubscription = activeSubscription.get();
if (previousSubscription != null) {
previousSubscription.unsubscribe();
}
Subscription newSubscription = subscribeToStream();
activeSubscription.set(newSubscription);
circuitOpened.set(-1L);
}
}

请求失败则再次打开熔断器,并更新打开时间

        public void markNonSuccess() {
if (status.compareAndSet(Status.HALF_OPEN, Status.OPEN)) {
//This thread wins the race to re-open the circuit - it resets the start time for the sleep window
circuitOpened.set(System.currentTimeMillis());
}
}

原文地址

Hystrix核心熔断器的更多相关文章

  1. 分布式RPC框架Dubbo实现服务治理:集成Kryo实现高速序列化,集成Hystrix实现熔断器

    Dubbo+Kryo实现高速序列化 Dubbo RPC是Dubbo体系中最核心的一种高性能,高吞吐量的远程调用方式,是一种多路复用的TCP长连接调用: 长连接: 避免每次调用新建TCP连接,提高调用的 ...

  2. 【一起学源码-微服务】Hystrix 源码三:Hystrix核心流程:Hystix降级、熔断等原理剖析

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一讲我们讲解了Hystrix在配合feign的过程中,一 ...

  3. Hystrix核心基础 - 滑动窗口创建过程及demo

    前言 RxJava可能有些小伙伴没有听过是什么东西,可能是因为大家平时在做业务需求的时候对异步编程了解得比较少,而RxJava就是这么一个响应式编程框架,RxJava在安卓上面用得非常多,做安卓的朋友 ...

  4. Spring Cloud Hystrix Dashboard熔断器-Turbine集群监控(六)

    序言 上一篇说啦hystrix的使用方法与配置还有工作流程及为何存在,我去,上一篇这么屌,去看看吧,没这么屌的话,我贴的有官方文档,好好仔细看看 hystrix除啦基本的熔断器功能之外,还可以对接口的 ...

  5. 【一起学源码-微服务】Hystrix 源码二:Hystrix核心流程:Hystix非降级逻辑流程梳理

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一讲我们讲了配置了feign.hystrix.enabl ...

  6. hystrix(3) 熔断器

    讲完metrics我们就来了解一下熔断器的执行情况,熔断器的判断取决metrics数据. hystrix在执行命令前需要经过熔断器判断,如果服务被熔断,则执行fallback流程,熔断判断逻辑如下: ...

  7. SpringCloud学习笔记:熔断器Hystrix(5)

    1. Hystrix简介 在分布式系统中,服务与服务之间相互依赖,一种不可避免的情况是某些服务会出现故障,导致依赖于它们的其他服务出现远程调度的线程阻塞. Hystrix提供熔断器功能,能够阻止分布式 ...

  8. 熔断器---Hystrix

    Hystrix:熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力. 说到熔断器,先要引入另外一个词,雪崩效应. 雪崩效应,百度百科的解释是这样的: ...

  9. 6、Spring Cloud -熔断器Hystrix

    6.1.什么是Hystrix 在分布式系统中.服务与服务之间的依赖错综复杂,一种不可避免的情况就是某些服务 出现故障,导致依赖于它们的其他服务出现远程调度的线程阻塞.   Hystrix是Netfli ...

随机推荐

  1. 微信小程序和支付宝小程序富文本使用

    微信小程序使用的是 1. wxml 页面元素的最简单使用 <rich-text nodes="{{这是你的数据}}"></rich-text>   2. j ...

  2. FastJSON使用例子

    FastjsonTest.java package demo; import java.util.ArrayList; import java.util.Date; import java.util. ...

  3. Vue.js+cube-ui(Scroll组件)实现类似头条效果的横向滚动导航条

    本博主在一次个人移动端项目中,遇到这么一个需求:希望自己的项目中,头部导航条的效果可以像今日头条那样,横向滚动! 对于这样的效果,在各大移动端项目中几乎是随处可见,为什么呢? 我们都知道,对于移动端也 ...

  4. bay——安装_RAC11g_LC_ASM方式_测试环境.txt

    ★★★____★☆★〓〓〓〓→VMware vSphere Client6.0 https://10.20.4.200/ 下载Vwmare IP:10.20.4.200-------账号:root-- ...

  5. hi3559v100 sdk中双系统AMP架构的初步了解

    hi3559v100是海思推出的camera soc处理器.采用的是双核处理器.一个是a7,运行的linux3.18内核.一个是a17使用的是huaweiliteos操作系统,Hi3559V100 系 ...

  6. HDU 4729 An Easy Problem for Elfness(树链剖分边权+二分)

    题意 链接:https://cn.vjudge.net/problem/HDU-4729 给你n个点,然你求两个点s和t之间的最大流.而且你有一定的钱k,可以进行两种操作 1.在任意连个点之间建立一个 ...

  7. selenium的web自动化实战

    selenium自动化原理: 1.通过各种语言(python,java,ruby等)调用接口库 2.通过浏览器驱动(web driver)来驱动浏览器 利用Python自动化的环境安装: 1.pyth ...

  8. ARM 寻址方式

    寻址方式有 9种 1.寄存器 2.立即数 3.寄存器位移 4.寄存器间接 5.基址 6.多寄存器 7.堆栈 8.块拷贝 9.相对 1. MOV R1,R2 R1 = R2 2. MOV R0,#0x1 ...

  9. (四)Amazon Lightsail 部署LAMP应用程序之扩展PHP前端

    扩展PHP前端 既然PHP前端和数据库是分开的,您将为Web层添加可伸缩性和容错性: 在以下步骤,您将获取Web前端实例的快照,并从该快照部署另外2个Web层实例.最终,您将在三个Web实例前面添加一 ...

  10. matplotlib画3D图修改X,Y,Z,colorbar的刻度值

    修改X,Y,Z轴的刻度值 from matplotlib.ticker import MultipleLocator,FuncFormatter from mpl_toolkits.mplot3d i ...