熔断
概念:
概述
前面我们学过:
Eureka实现了服务注册与发现
服务间调用。
Ribbon实现了客户端负载均衡
Feign实现了声明式 API调用
这节学习 微服务间的容错
在分布式系统下,微服务之间不可避免地会发生相互调用,但每个系统都无法百分之百保证自身运行不出问题。在服务调用中,很可能面临依赖服务失效的问题(网络延时,服务异常,负载过大无法及时响应)。因此需要一个组件,能提供强大的容错能力,为服务间调用提供保护和控制。
我们的目的:当我自身 依赖的服务不可用时,服务自身不会被拖垮。防止微服务级联异常。
图。
本质:就是隔离坏的服务,不让坏服务拖垮其他服务(调用坏服务的服务)。
比如:武汉发生疫情,隔离它,不让依赖于武汉的地方感染。
和我们课程中熔断降级更贴切一点:北京从武汉招聘大学生,武汉有疫情了,当北京去武汉请求大学生来的时候,武汉熔断,然后北京启动自身的备用逻辑:去上海找大学生(降级)。
舱壁模式
舱壁模式(Bulkhead)隔离了每个工作负载或服务的关键资源,如连接池、内存和CPU,硬盘。每个工作单元都有独立的 连接池,内存,CPU。
使用舱壁避免了单个服务消耗掉所有资源,从而导致其他服务出现故障的场景。
这种模式主要是通过防止由一个服务引起的级联故障来增加系统的弹性。
据说泰坦尼克原因:泰坦尼克号上有16个防水舱,设计可以保障如果只有4个舱进水,密闭和隔离可以阻止水继续进入下一个防水舱,从而保证船的基本浮力。
但是当时冰山从侧面划破了船体,从而导致有5个防水舱同时进水,而为了建造豪华的头等舱大厅,也就是电影里杰克和罗斯约会的地方,5号舱的顶部并未达到密闭所需要的高度,水就一层层进入了船体,隔离的失败导致了泰坦尼克的沉没。
舱壁模式
给我们的思路:可以对每个请求设置,单独的连接池,配置连接数,不要影响 别的请求。就像一个一个的防水舱。
对在公司中的管理也一样:给每个独立的 小组,分配独立的资源,比如产品,开发,测试。在小公司,大多数情况 这些资源都是共享的,有一个好处是充分利用资源,坏处是,如果一个项目延期,会影响别的项目推进。自己权衡利弊。
最近比较火的一句话: 真正的知识,是 产品提高一个等级和成本提高0.2元的 痛苦抉择。
雪崩效应
每个服务 发出一个HTTP请求都会 在 服务中 开启一个新线程。而下游服务挂了或者网络不可达,通常线程会阻塞住,直到Timeout。如果并发量多一点,这些阻塞的线程就会占用大量的资源,很有可能把自己本身这个微服务所在的机器资源耗尽,导致自己也挂掉。
如果服务提供者响应非常缓慢,那么服务消费者调用此提供者就会一直等待,直到提供者响应或超时。在高并发场景下,此种情况,如果不做任何处理,就会导致服务消费者的资源耗竭甚至整个系统的崩溃。一层一层的崩溃,导致所有的系统崩溃。
《雪崩示意图》
雪崩:由基础服务故障导致级联故障的现象。描述的是:提供者不可用 导致消费者不可用,并将不可用逐渐放大的过程。像滚雪球一样,不可用的服务越来越多。影响越来越恶劣。
雪崩三个流程:
服务提供者不可用
重试会导致网络流量加大,更影响服务提供者。
导致服务调用者不可用,由于服务调用者 一直等待返回,一直占用系统资源。
(不可用的范围 被逐步放大)
服务不可用原因:
服务器宕机
网络故障
宕机
程序异常
负载过大,导致服务提供者响应慢
缓存击穿导致服务超负荷运行
总之 : 基础服务故障 导致 级联故障 就是 雪崩。
容错机制
-
为网络请求设置超时。
必须为网络请求设置超时。一般的调用一般在几十毫秒内响应。如果服务不可用,或者网络有问题,那么响应时间会变很长。长到几十秒。
每一次调用,对应一个线程或进程,如果响应时间长,那么线程就长时间得不到释放,而线程对应着系统资源,包括CPU,内存,得不到释放的线程越多,资源被消耗的越多,最终导致系统崩溃。
因此必须设置超时时间,让资源尽快释放。
-
使用断路器模式。
想一下家里的保险丝,跳闸。如果家里有短路或者大功率电器使用,超过电路负载时,就会跳闸,如果不跳闸,电路烧毁,波及到其他家庭,导致其他家庭也不可用。通过跳闸保护电路安全,当短路问题,或者大功率问题被解决,在合闸。
自己家里电路,不影响整个小区每家每户的电路。
断路器
如果对某个微服务请求有大量超时(说明该服务不可用),再让新的请求访问该服务就没有意义,只会无谓的消耗资源。例如设置了超时时间1s,如果短时间内有大量的请求无法在1s内响应,就没有必要去请求依赖的服务了。
-
断路器是对容易导致错误的操作的代理。这种代理能统计一段时间内的失败次数,并依据次数决定是正常请求依赖的服务还是直接返回。
-
断路器可以实现快速失败,如果它在一段时间内检测到许多类似的错误(超时),就会在之后的一段时间,强迫对该服务的调用快速失败,即不再请求所调用的服务。这样对于消费者就无须再浪费CPU去等待长时间的超时。
-
断路器也可自动诊断依赖的服务是否恢复正常。如果发现依赖的服务已经恢复正常,那么就会恢复请求该服务。通过重置时间来决定断路器的重新闭合。
这样就实现了微服务的“自我修复”:当依赖的服务不可用时,打开断路器,让服务快速失败,从而防止雪崩。当依赖的服务恢复正常时,又恢复请求。
断路器开关时序图
第一次正常
第二次提供者异常
提供者多次异常后,断路器打开
后续请求,则直接降级,走备用逻辑。
断路器状态转换的逻辑:
关闭状态:正常情况下,断路器关闭,可以正常请求依赖的服务。
打开状态:当一段时间内,请求失败率达到一定阈值,断路器就会打开。服务请求不会去请求依赖的服务。调用方直接返回。不发生真正的调用。重置时间过后,进入半开模式。
半开状态:断路器打开一段时间后,会自动进入“半开模式”,此时,断路器允许一个服务请求访问依赖的服务。如果此请求成功(或者成功达到一定比例),则关闭断路器,恢复正常访问。否则,则继续保持打开状态。
断路器的打开,能保证服务调用者在调用异常服务时,快速返回结果,避免大量的同步等待,减少服务调用者的资源消耗。并且断路器能在打开一段时间后继续侦测请求执行结果,判断断路器是否能关闭,恢复服务的正常调用。
《熔断.doc》《断路器开关时序图》《状态转换》
降级
为了在整体资源不够的时候,适当放弃部分服务,将主要的资源投放到核心服务中,待渡过难关之后,再重启已关闭的服务,保证了系统核心服务的稳定。当服务停掉后,自动进入fallback替换主方法。
用fallback方法代替主方法执行并返回结果,对失败的服务进行降级。当调用服务失败次数在一段时间内超过了断路器的阈值时,断路器将打开,不再进行真正的调用,而是快速失败,直接执行fallback逻辑。服务降级保护了服务调用者的逻辑。
熔断和降级:
共同点:
1、为了防止系统崩溃,保证主要功能的可用性和可靠性。
2、用户体验到某些功能不能用。
不同点:
1、熔断由下级故障触发,主动惹祸。
2、降级由调用方从负荷角度触发,无辜被抛弃。
19年春晚 百度 红包,凤巢的5万台机器熄火4小时,让给了红包。
Hystrix
spring cloud 用的是 hystrix,是一个容错组件。
Hystrix实现了 超时机制和断路器模式。
Hystrix是Netflix开源的一个类库,用于隔离远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。主要有以下几点功能:
- 为系统提供保护机制。在依赖的服务出现高延迟或失败时,为系统提供保护和控制。
- 防止雪崩。
- 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中运行。
- 跳闸机制:当某服务失败率达到一定的阈值时,Hystrix可以自动跳闸,停止请求该服务一段时间。
- 资源隔离:Hystrix为每个请求都的依赖都维护了一个小型线程池,如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。防止级联失败。
- 快速失败:Fail Fast。同时能快速恢复。侧重点是:(不去真正的请求服务,发生异常再返回),而是直接失败。
- 监控:Hystrix可以实时监控运行指标和配置的变化,提供近实时的监控、报警、运维控制。
- 回退机制:fallback,当请求失败、超时、被拒绝,或当断路器被打开时,执行回退逻辑。回退逻辑我们自定义,提供优雅的服务降级。
- 自我修复:断路器打开一段时间后,会自动进入“半开”状态,可以进行打开,关闭,半开状态的转换。前面有介绍。
提问
15.2 Hystrix 使用
hystrix独立使用脱离spring cloud
代码:study-hystrix项目,HelloWorldHystrixCommand类。看着类讲解。
关注点:
继承hystrixCommand
重写run
fallback(程序发生非HystrixBadRequestException异常,运行超时,熔断开关打开,线程池/信号量满了)
熔断(熔断机制相当于电路的跳闸功能,我们可以配置熔断策略为当请求错误比例在10s内>50%时,该服务将进入熔断状态,后续请求都会进入fallback。)
结果缓存(支持将一个请求结果缓存起来,下一个具有相同key的请求将直接从缓存中取出结果,减少请求开销。)
这个例子,只是独立使用hystrix, 通过这个例子,了解 hystrix 的运行逻辑。
原理
了解前面一些概念:舱壁模式,命令模式(下面),雪崩,容错,断路器,降级。
熔断降级:北京去武汉招大学生的例子。
资源隔离:类似于高铁高架桥,并不是一个整体,而是一块一块的拼装的,一段路坏了,不会影响整条路。
隔离策略
概念中的舱壁模式。想一下货船上,每个货仓中间的隔离。两个好处:
- 服务提供者高延迟或异常,不会影响到整个系统的失败。
- 能够控制每个调用者的并发度。因为有独立的线程池。
两种线程隔离策略:线程池(默认)、信号量。
《Hystrix隔离策略》
@HystrixCommand注释修饰一个服务时,HystrixCommand的运行逻辑有可能是在该请求的主线程上一并执行,也有可能是单独起一个线程来执行,这取决于我们如何设置Hystrix线程的隔离策略。
execution.isolation.strategy属性就是用来设置HystrixCommand.run()执行的隔离策略的。(回忆上面讲过的配置,设置线程策略的)
两种隔离策略:线程隔离和信号量隔离,即“THREAD”和“SEMAPHORE”,系统默认为“THREAD”。
它们的含义是:
THREAD(线程隔离):使用该方式,HystrixCommand将会在单独的线程上执行,并发请求受线程池中线程数量的限制。不同服务通过使用不同线程池,彼此间将不受影响,达到隔离效果。
此种隔离方式:将调用服务线程与服务访问的执行线程分割开来,调用线程能够空出来去做其他工作,而不至于因为服务调用的执行,阻塞过长时间。
hystrix将使用独立的线程池对应每一个服务提供者,用于隔离和限制这些服务。于是某个服务提供者的高延迟或者资源受限只会发生在该服务提供者对应的线程池中。
SEMAPHORE(信号量隔离):其实就是个计数器,使用该方式,HystrixCommand将会在调用线程上执行,通过信号量限制单个服务提供者的并发量,开销相对较小(因为不用那么多线程池),并发请求受到信号量个数的限制。 线程隔离会带来线程开销,有些场景(比如无网络请求场景)可能会因为用开销换隔离得不偿失,为此hystrix提供了信号量隔离,当服务的并发数大于信号量阈值时将进入fallback。
Hystrix中默认并且推荐使用线程隔离(THREAD),
一般来说,只有当调用负载异常高时(例如每个实例每秒调用数百次)才需要信号量隔离,因为这种场景下使用THREAD开销会比较高。信号量隔离一般仅适用于非网络调用的隔离。
正常情况下,默认为线程隔离, 保持默认即可。
取舍:
线程池和信号量都支持熔断和限流。相比线程池,信号量不需要线程切换,因此避免了不必要的开销。但是信号量不支持异步,也不支持超时,也就是说当所请求的服务不可用时,信号量会控制超过限制的请求立即返回,但是已经持有信号量的线程只能等待服务响应或从超时中返回,即可能出现长时间等待。线程池模式下,当超过指定时间未响应的服务,Hystrix会通过响应中断的方式通知线程立即结束并返回。
Hystrix实现思路
-
请求过来时,将请求的远程调用逻辑,封装到HystrixCommand或者HystrixObservableCommand对象(并在构造方法配置请求被执行需要的参数)中,这些远程调用将会在独立的线程中执行。(资源隔离、命令模式)。
https://www.runoob.com/design-pattern/command-pattern.html 介绍 意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。 主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。 何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。 如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。 关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口 应用实例:struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。 优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。 缺点:使用命令模式可能会导致某些系统有过多的具体命令类。 使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。 注意事项:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。
-
Hystrix对访问耗时超过设置阈值的请求采用自动超时的策略。该策略对所有的命令都有效。(如果是信号量隔离方式,则此特性失效),超时的阈值可以通过命令配置进行自定义。
-
为每个服务提供者维护一个线程池(信号量),当线程池(信号量)被占满时,对于该服务提供者的请求将会被直接拒绝(快速失败,走回滚)而不是排队等待,减少系统等待资源。
-
针对请求服务提供者划分出成功、失效、超时和线程池被占满等情况。
-
断路器将在请求服务提供者失败次数超过一定阈值后手动或自动切断服务一段时间。
-
当请求服务提供者出现服务拒绝、超时和 短路(多个服务提供者依次顺序请求,前面的服务提供者请求失败,后面的请求将不再发出)等情况,执行器fallback方法,服务降级。
-
提供近乎实时的监控和配置变更服务。
hystrix实现流程
-
构建HystrixCommand或者HystrixObservableCommand对象,用于封装请求,并在构造方法配置请求被执行需要的参数。
-
执行命令,Hystrix提供了4种执行命令的方法。
-
检查是否有相同命令执行的缓存,若启用了缓存,且缓存可用,直接使用缓存响应请求。Hystrix支持请求缓存,但需要用户自定义启动。
-
检查断路器是否打开,如果打开走 第8步。
-
检查线程池或者信号量是否被消耗完,如果已满,走第8步。
-
调用HystrixCommand的run 或者 HystrixObservableCommand的construct 执行被封装的调用逻辑,如果执行失败或超时,走第8步。
-
计算链路的健康情况
-
在命令执行失败时获取fallback逻辑。
-
返回响应。
《断路器整体流程》
源码
debug时,注意上面类名的变化。
包裹请求
@HystrixCommand,用此注解来包装需要保护的远程调用方法。
public @interface HystrixCommand {
/**
* The command group key is used for grouping together commands such as for reporting,
* alerting, dashboards or team/library ownership.
* <p/>
* default => the runtime class name of annotated method
*
* @return group key
*/
命令分组键:被此注解修饰的命令被归为一组,默认组名:类名。用于报告,预警,面板展示
String groupKey() default "";
/**
* Hystrix command key.
* <p/>
* default => the name of annotated method. for example:
* <code>
* ...
* @HystrixCommand
* public User getUserById(...)
* ...
* the command name will be: 'getUserById'
* </code>
*
* @return command key
*/
命令键:默认为注解的方法名,用于区分不同的方法。
String commandKey() default "";
/**
* The thread-pool key is used to represent a
* HystrixThreadPool for monitoring, metrics publishing, caching and other such uses.
*
* @return thread pool key
*/
线程池键,用来指定执行命令的 hystrixThreadPool
String threadPoolKey() default "";
/**
* Specifies a method to process fallback logic.
* A fallback method should be defined in the same class where is HystrixCommand.
* Also a fallback method should have same signature to a method which was invoked as hystrix command.
* for example:
* <code>
* @HystrixCommand(fallbackMethod = "getByIdFallback")
* public String getById(String id) {...}
*
* private String getByIdFallback(String id) {...}
* </code>
* Also a fallback method can be annotated with {@link HystrixCommand}
* <p/>
* default => see {@link com.netflix.hystrix.contrib.javanica.command.GenericCommand#getFallback()}
*
* @return method name
*/
回调方法名
String fallbackMethod() default "";
/**
* Specifies command properties.
*
* @return command properties
*/
自定义命令相关配置。我们前面讲过有例子
HystrixProperty[] commandProperties() default {};
/**
* Specifies thread pool properties.
*
* @return thread pool properties
*/
自定义线程池相关配置,
HystrixProperty[] threadPoolProperties() default {};
/**
* Defines exceptions which should be ignored.
* Optionally these can be wrapped in HystrixRuntimeException if raiseHystrixExceptions contains RUNTIME_EXCEPTION.
*
* @return exceptions to ignore
*/
自定义忽略的异常
Class<? extends Throwable>[] ignoreExceptions() default {};
/**
* Specifies the mode that should be used to execute hystrix observable command.
* For more information see {@link ObservableExecutionMode}.
*
* @return observable execution mode
*/
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
/**
* When includes RUNTIME_EXCEPTION, any exceptions that are not ignored are wrapped in HystrixRuntimeException.
*
* @return exceptions to wrap
*/
HystrixException[] raiseHystrixExceptions() default {};
/**
* Specifies default fallback method for the command. If both {@link #fallbackMethod} and {@link #defaultFallback}
* methods are specified then specific one is used.
* note: default fallback method cannot have parameters, return type should be compatible with command return type.
*
* @return the name of default fallback method
*/
String defaultFallback() default "";
}
上面的配置,我们大部分情况仅需要关注fallbackMethod,看注释中关于fallback方法的说明,如果需要对线程池和和命令有特殊要求,可进行额外配置,其实99%不需要配置。
HystrixCommandAspect切面
被注解@HystrixCommand修饰的方法,会被HystrixCommand包装执行,通过切面来实现。
com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect
定义切面
@Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()")
public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
主要地方:
备注:
(
@Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)")
public void hystrixCommandAnnotationPointcut() {
}
)
此方法主要:构建了MetaHolder(请求必要的信息),在此方法第一行(Method method = getMethodFromTarget(joinPoint);)打断点。
鼠标放到joinPoint上面看内容:execution(ResponseResult com.online.taxi.driver.service.impl.RestTemplateRequestServiceImpl.smsSend(SmsSendRequest))
鼠标放上去,查看metaHolder
观察hystrixCommand。
构建MetaHolder
根据MetaHolder构建合适的HystrixCommand
委托CommandExecutor执行HystrixCommand
得到结果
此方法中:
Object result;
try {
if (!metaHolder.isObservable()) {
result = CommandExecutor.execute(invokable, executionType, metaHolder);
} else {
result = executeObservable(invokable, executionType, metaHolder);
}
} catch (HystrixBadRequestException e) {
throw e.getCause();
} catch (HystrixRuntimeException e) {
throw hystrixRuntimeExceptionToThrowable(metaHolder, e);
}
此处判断是用HystrixCommand还是HystrixObservableCommand,执行HystrixCommand命令执行。
HystrixCommand:同步,异步执行。
HystrixObservableCommand: 异步回调执行(响应式)。
MetaHolder 持有用于构建HystrixCommand和与被包装方法相关的必要信息,如被注解的方法,失败回滚执行的方法等
com.netflix.hystrix.contrib.javanica.command.MetaHolder
private final HystrixCollapser hystrixCollapser;
private final HystrixCommand hystrixCommand;
private final DefaultProperties defaultProperties;
private final Method method;被注解的方法。
private final Method cacheKeyMethod;
private final Method ajcMethod;
private final Method fallbackMethod;失败回滚执行的方法。
private final Object obj;
private final Object proxyObj;
private final Object[] args;
private final Closure closure;
private final String defaultGroupKey;默认的group键
private final String defaultCommandKey;默认的命令键
private final String defaultCollapserKey;合并请求键
private final String defaultThreadPoolKey;线程池 键
private final ExecutionType executionType;执行类型
private final boolean extendedFallback;
private final ExecutionType collapserExecutionType;
private final ExecutionType fallbackExecutionType;
private final boolean fallback;
private boolean extendedParentFallback;
private final boolean defaultFallback;
private final JoinPoint joinPoint;
private final boolean observable;
private final ObservableExecutionMode observableExecutionMode;
创建HystrixCommand方法如下
com.netflix.hystrix.contrib.javanica.command.HystrixCommandFactory
public HystrixInvokable create(MetaHolder metaHolder) {
HystrixInvokable executable;
if (metaHolder.isCollapserAnnotationPresent()) {
executable = new CommandCollapser(metaHolder);
根据metaHolder.isObservable()来判断,是生成HystrixCommand还是HystrixObservableCommand。
} else if (metaHolder.isObservable()) {
executable = new GenericObservableCommand(HystrixCommandBuilderFactory.getInstance().create(metaHolder));
} else {
executable = new GenericCommand(HystrixCommandBuilderFactory.getInstance().create(metaHolder));
}
return executable;
}
点击GenericObservableCommand(异步回调执行,也就是响应式)和GenericCommand(同步,异步执行)进去,查看父类发现HystrixObservableCommand和HystrixCommand。
ExecutionType
/**
* Used for asynchronous execution of command.
*/
ASYNCHRONOUS,异步
/**
* Used for synchronous execution of command.
*/
SYNCHRONOUS,同步
/**
* Reactive execution (asynchronous callback).
*/
OBSERVABLE;异步回调
/**
* Gets execution type for specified class type.
* @param type the type
* @return the execution type {@link ExecutionType}
*/
public static ExecutionType getExecutionType(Class<?> type) {
if (Future.class.isAssignableFrom(type)) {
return ExecutionType.ASYNCHRONOUS;
} else if (Observable.class.isAssignableFrom(type)) {
return ExecutionType.OBSERVABLE;
} else {
return ExecutionType.SYNCHRONOUS;
}
}
根据被包装方法的返回值类型觉得命令执行的ExecutionType,从而(通过上面代码块中的一步)决定构建HystrixCommand 还是 HystrixObservableCommand。
方法的返回值为Future:异步执行,rx类型:异步回调,其他类型:同步执行。
@HystrixCommand
public Future<T> find(){}
debug到:
HystrixCommandAspect类中。
create方法。
HystrixCommand hystrixCommand = method.getAnnotation(HystrixCommand.class);
ExecutionType executionType = ExecutionType.getExecutionType(method.getReturnType());
可以看到命令是同步还是异步,又方法的返回值决定。
命令模式在此的应用
HystrixInvokable是被HystrixCommand标记的接口,继承了它的类,都是可以被执行的HystrixCommand。提供具体方法的为HystrixExecutable。
主要的2个类
public abstract class HystrixCommand<R> extends AbstractCommand<R>
public abstract class HystrixObservableCommand<R> extends AbstractCommand<R>
queue和execute
public abstract class HystrixCommand<R> extends AbstractCommand<R>的下面的方法,
public Future<R> queue() {
回想study-hystrix中queue的说明,异步执行。execute同步执行。
断路器
断路器核心接口:
com.netflix.hystrix.HystrixCircuitBreaker
一个Command key (也就是method)对应一个HystrixCircuitBreaker。
public boolean allowRequest();//是否允许命令执行
public boolean isOpen();//断路器是否打开(开关)
void markSuccess();//在半开状态时,执行成功反馈。将半开转为关闭。
void markNonSuccess();//在半开状态时,执行失败反馈。将半开转为打开。
实现类:HystrixCircuitBreakerImpl
@Override
public boolean allowRequest() {
if (properties.circuitBreakerForceOpen().get()) {
return false;
}
if (properties.circuitBreakerForceClosed().get()) {
return true;
}
if (circuitOpened.get() == -1) {
return true;
} else {
if (status.get().equals(Status.HALF_OPEN)) {
return false;
} else {
return isAfterSleepWindow();
}
}
}
此处有强制打开,强制关闭,可以通过配置更改。
上面有测试例子(断路器开关强制配置)。
统计命令
com.netflix.hystrix.HystrixMetrics
HystrixCommandMetrics是上面的子类
在断路器的isOpen等方法中,均有对HealthCount的数量的计算,来判断断路器状态:
public boolean isOpen() {
if (circuitOpen.get()) {
// if we're open we immediately return true and don't bother attempting to 'close' ourself as that is left to allowSingleTest and a subsequent successful test to close
return true;
}
// we're closed, so let's see if errors have made us so we should trip the circuit open
HealthCounts health = metrics.getHealthCounts();
// check if we are past the statisticalWindowVolumeThreshold
if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
// we are not past the minimum volume threshold for the statisticalWindow so we'll return false immediately and not calculate anything
return false;
}
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
circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
return true;
} else {
// How could previousValue be true? If another thread was going through this code at the same time a race-condition could have
// caused another thread to set it to true already even though we were in the process of doing the same
// In this case, we know the circuit is open, so let the other thread set the currentTime and report back that the circuit is open
return true;
}
}
}
统计数据:
public static class HealthCounts {
private final long totalCount;执行总数
private final long errorCount;失败数
private final int errorPercentage;失败百分比
用滑动窗口的方式统计,一个滑动窗口又划分为几个bucket(滑动窗口时间和bucket成整数倍关系),滑动窗口的移动,以bucket为单位,每个bucket仅统计该时间间隔内的请求数据。,最后将所有窗口中的bucket进行聚合。
失败回滚
AbstractCommand的方法executeCommandAndObserve的局部变量:handleFallback(final Func1<Throwable, Observable<R>> handleFallback)
如果失败,走失败逻辑。