Spring Cloud学习 之 Spring Cloud Hystrix(流程分析)
Spring Boot版本:2.1.4.RELEASE
Spring Cloud版本:Greenwich.SR1
我们还是从流程图入手:

创建HystrixCommand或者HystrixObservableCommand对象,在上篇文章我们已经讲过了,这里主要用的是命令模式
命令执行
从图中我们可以看到一共有4种命令的执行方式,而Hystrix在执行时会根据创建的Command对象以及具体的情况来选择一个执行,其中HystrixCommand实现了下面的执行方式
- execute():同步执行,从依赖的服务返回一个单一的结果对象,或是在发生错误的时候抛出异常
- queue():异步执行,直接返回一个Future对象,其中包含了服务执行结束时要返回的单一结果对象
而HystrixObservableCommand实现了另外两种执行方式
- observe():返回Observable对象,它代表了操作的多个结果,它是一个Hot Observable
- toObserveable():同样会返回Observable对象,也代表了操作的多个结果,但它返回的是一个Cold Observable
RxJava在上篇文章中也做了简单的介绍,这里也不多讲了。
在这里我们对于事件源observable提到了两个不同的概念:Hot Observable和Cold Observable,分别对应了上面command.observe()和command.toObserveable()的返回对象。其中Hot Observable,它不论“事件源”是否有“订阅者”,都会在创建后对事件进行发布,所以对于Hot Observable的每一个订阅者都有可能是从事件源的中途开始的,并可能只是看到了整个操作的局部过程。而Cold Observable在没有订阅者的时候并不会发布事件,而是进行等待,直到有订阅者之后才发布事件,所以对于Cold Observable的订阅者。它可以保证从一开始看到整个操作的全部过程。
大家从表面上可能只会认为只是在HystrixObservableCommand中使用了RxJava,然后实际上execute(),queue()也都使用了RxJava来实现,从下面的源码中,我们可以看到。
public R execute() {
try {
// 是通过queue()返回的异步对象Future<R>的get方法来实现同步执行的
// 改方法会等待任务执行结束,然后获得R类型的结果进行返回
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}
public Future<R> queue() {
// queue()则是通过toObservable()方法来获得一个Cold Observable,并且通过toBlocking()将
// Observable转换成BlockingObservable,它可以把数据以阻塞的方式发射出来,而toFuture方法
// 则是把BlockingObservable转换为一个Future,该方法只是创建一个Future返回,并不会阻塞,这使得
// 消费者可以自行决定如何处理异步操作,而execute方法是通过queue()返回的异步对象Future<R>的get方 // 法来实现同步执行的,通过这种方式转换的future要求Observable只发射一个数据,所以这两个实现都只能
// 返回单一结果
final Future<R> delegate = toObservable().toBlocking().toFuture();
final Future<R> f = new Future<R>() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (delegate.isCancelled()) {
return false;
}
if (HystrixCommand.this.getProperties().executionIsolationThreadInterruptOnFutureCancel().get()) {
/*
* The only valid transition here is false -> true. If there are two futures, say f1 and f2, created by this command
* (which is super-weird, but has never been prohibited), and calls to f1.cancel(true) and to f2.cancel(false) are
* issued by different threads, it's unclear about what value would be used by the time mayInterruptOnCancel is checked.
* The most consistent way to deal with this scenario is to say that if *any* cancellation is invoked with interruption,
* than that interruption request cannot be taken back.
*/
interruptOnFutureCancel.compareAndSet(false, mayInterruptIfRunning);
}
final boolean res = delegate.cancel(interruptOnFutureCancel.get());
if (!isExecutionComplete() && interruptOnFutureCancel.get()) {
final Thread t = executionThread.get();
if (t != null && !t.equals(Thread.currentThread())) {
t.interrupt();
}
}
return res;
}
@Override
public boolean isCancelled() {
return delegate.isCancelled();
}
@Override
public boolean isDone() {
return delegate.isDone();
}
@Override
public R get() throws InterruptedException, ExecutionException {
return delegate.get();
}
@Override
public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return delegate.get(timeout, unit);
}
};
/* special handling of error states that throw immediately */
if (f.isDone()) {
try {
f.get();
return f;
} catch (Exception e) {
Throwable t = decomposeException(e);
if (t instanceof HystrixBadRequestException) {
return f;
} else if (t instanceof HystrixRuntimeException) {
HystrixRuntimeException hre = (HystrixRuntimeException) t;
switch (hre.getFailureType()) {
case COMMAND_EXCEPTION:
case TIMEOUT:
// we don't throw these types from queue() only from queue().get() as they are execution errors
return f;
default:
// these are errors we throw from queue() as they as rejection type errors
throw hre;
}
} else {
throw Exceptions.sneakyThrow(t);
}
}
}
return f;
}
若当前命令的请求缓存功能被启用的,并且该命令缓存命中,那么缓存的结果会立即以Observable对象的形式返回
断路器是否打开
在命令结果没有缓存命中的时候,Hystrix在执行命令前需要检查断路器是否为打开状态:
- 如果断路器是打开的,那么Hystrix不会执行命令,而是转接到fallback处理逻辑
- 如果断路器是关闭的,那么Hystrix跳转到第五步,检查是否有可用资源来执行命令
线程池/请求队列/信号量是否占满
如果与命令相关的线程池和请求队列,或者信号量已经被占满,那么Hystrix也不会执行命令,而是转接到fallback处理逻辑
需要注意的是,这里Hystrix所判断的线程池并非容器的线程池,而是每个依赖服务的专有线程池。Hystrix为了保证不会因为某个依赖服务的问题影响到其他依赖服务而采用了“舱壁模式”来隔离每个依赖的服务
HystrixObservableCommand.construct()或HystrixCommand.run()
Hystrix会根据我们编写的方法来决定采取什么样的方式去请求依赖服务。
HystrixCommand.run():返回一个单一结果,或者抛出异常
HystrixObservableCommand.construct():返回一个Observable对象来发射多个结果,或通过onError发送错误通知
如果run()或construct()方法的执行事件超过了命令设置的超时阀值,当前处理线程将会抛出一个TimeOutException。在这种情况下,Hystrix会转接到fallback处理逻辑。同时,如果当前命令没有被取消或中断,那么它最终会忽略run()或者construct()方法的返回
如果命令没有抛出异常并返回了结果,那么Hystrix在记录一些日志并采集监控报告之后将结果返回。在使用run()的情况下,Hystrix会返回一个Observable,它发射单个结果并产生onCompleted的结束通知:而在使用construct()的情况下,Hystrix会直接返回该方法产生的Observable对象
计算断路器的健康度
Hystrix会将成功,失败,拒绝,超时等信息报告给断路器,而断路器会维护一组计数器来统计这些数据。
断路器会使用这些统计数据来决定是否要将断路器打开,来对某个依赖服务的请求进行熔断,直到恢复期结束。若在恢复期结束后,根据统计数据判断如果还是未达到健康指标,就再次熔断
fallback的处理
当命令执行失败的时候,Hystrix会进入fallback尝试回退处理,我们通常也称该操作为服务降级。而能引起服务降级的有下面几种情况
- 第四步:当前命令处于“熔断”状态,断路器是打开的时候
- 第五步:当前命令的线程池,请求队列或者信号量被占满的时候
- 第六步:HystrixObservableCommand.construct()或HystrixCommand.run()抛出异常的时候
在服务降级逻辑中,我们需要实现一个通用的响应结果,并且该结果的处理逻辑应当是从缓存或者根据一些静态逻辑来获取,而不是依赖网络请求实现。如果一定要在降级逻辑中包含网络请求,那么该请求也必须被包装在HystrixObservableCommand或HystrixCommand中,从而实现级联的降级策略,而最终的降级逻辑一定不是一个依赖网络请求的处理,而是一个能够稳定地返回结果的处理逻辑
在HystrixObservableCommand跟HystrixCommand实现降级逻辑时还略有不同
- 当使用HystrixCommand的时候,通过实现HystrixCommand.getFallback来实现服务降级逻辑
- 当使用HystrixObservableCommand的时候,通过HystrixObservableCommand.resumeWithFallback()实现服务降级逻辑,该方法会返回一个Observable对象来发射一个或多个降级结果
当命令的降级逻辑返回结果之后,Hystrix就将该结果返回给调用者。当使用HystrixCommand.getFallback的时候,它返回一个Observable对象,该对象会发射HystrixCommand.getFallback的处理结果,而使用HystrixObservableCommand.resumeWithFallback()的时候,它会直接将Observable对象返回
如果我们没有为命令实现降级逻辑或者降级处理了逻辑中抛出了异常,Hystrix依然会返回一个Observable对象,但是它不会发射任何结果数据,而是通过onError方法通知命令立即中断请求,并通过onError方法将引起命令失败的异常发送给调用者。实现一个有可能失败的降级逻辑是一种非常糟糕的做法,我们应该在实现降级策略时尽可能避免失败的情况
当然完全不可能出现失败的完美策略是不存在的,如果降级执行发现失败的时候,Hystrix会根据不同的执行方法做出不同的处理。
- execute():抛出异常
- queue():正常返回future对象,但是当调用get方法时会抛出异常
- observe():正常返回Observable对象,当订阅它的时候,将立即通过调用订阅者的onError方法来通知中止请求
- toObservable():正常返回Observable对象,当订阅它的时候,将立即通过调用订阅者的onError方法来通知中止请求
返回成功的响应
当Hystrix命令执行成功之后,他会将处理结果直接返回或是以Observable的形式返回。而具体以哪种方式返回取决于之前第二步我们所提到的对命令的4中不同的执行方式,下图总结了这4中调用方式之间的依赖关系。我们可以将此图与在第二步中对前两者的源码分析联系起来,并且从源头toObservable()来开始分析

- toObservable():返回最原始的Observable,必须通过订阅它才会真正触发命令的执行流程
- observe():在toObservable()产生原始Observable之后立即订阅它,让命令能够马上开始异步执行,并返回一个Observable对象,当调用它的subscribe时,将重新产生结果和通知给订阅者
- queue():将toObservable()产生的原始Observable通过toBlocking()方法转换成BlockingObservable对象,并调用它的toFuture()方法返回异步的Future对象
- execute():在queue()产生异步结果Future对象之后,通过调用get()方法阻塞并等待结果的返回
Spring Cloud学习 之 Spring Cloud Hystrix(流程分析)的更多相关文章
- Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)
Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...
- Spring Cloud 学习 之 Spring Cloud Eureka(搭建)
Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 文章目录 搭建服务注册中心: 注册服务提供者: 高可用注册中心: 搭建服务注册中心: ...
- Spring Cloud学习笔记--Spring Boot初次搭建
1. Spring Boot简介 初次接触Spring的时候,我感觉这是一个很难接触的框架,因为其庞杂的配置文件,我最不喜欢的就是xml文件,这种文件的可读性很不好.所以很久以来我的Spring学习都 ...
- spring cloud学习(六)Spring Cloud Config
Spring Cloud Config 参考个人项目 参考个人项目 : (希望大家能给个star~) https://github.com/FunriLy/springcloud-study/tree ...
- Spring Cloud学习 之 Spring Cloud Ribbon(执行流程源码分析)
Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 文章目录 分析: 总结: 分析: 在上篇文章中,我们着重分析了RestTempla ...
- Spring Cloud 学习 (九) Spring Security, OAuth2
Spring Security Spring Security 是 Spring Resource 社区的一个安全组件.在安全方面,有两个主要的领域,一是"认证",即你是谁:二是& ...
- Spring 框架学习(1)--Spring、Spring MVC扫盲
纸上得来终觉浅,绝知此事要躬行 文章大纲 什么是spring 传统Java web应用架构 更强的Java Web应用架构--MVC框架 Spring--粘合式框架 spring的内涵 spring核 ...
- Spring框架学习03——Spring Bean 的详解
1.Bean 的配置 Spring可以看做一个大型工厂,用于生产和管理Spring容器中的Bean,Spring框架支持XML和Properties两种格式的配置文件,在实际开发中常用XML格式的配置 ...
- Spring框架学习02——Spring IOC 详解
1.Spring IOC的基本概念 IOC(Inverse of Control)反转控制的概念,就是将原本在程序中手动创建对象的控制权,交由Spring框架管理.当某个Java对象(调用者)需要调用 ...
- Spring Boot学习笔记——Spring Boot与MyBatis的集成(项目示例)
1.准备数据库环境 # 创建数据库 CREATE DATABASE IF NOT EXISTS zifeiydb DEFAULT CHARSET utf8 COLLATE utf8_general_c ...
随机推荐
- 深度解密 Go 语言之 sync.Pool
最近在工作中碰到了 GC 的问题:项目中大量重复地创建许多对象,造成 GC 的工作量巨大,CPU 频繁掉底.准备使用 sync.Pool 来缓存对象,减轻 GC 的消耗.为了用起来更顺畅,我特地研究了 ...
- 【three.js第三课】鼠标事件,移动、旋转物体
1.下载three.js的源码包后,文件夹结构如下: 2.在[three.js第一课]的代码基础上,引入OrbitControls.js文件,此文件主要用于 对鼠标的操作. 该文件位置:在文件结构中 ...
- \r\n的意思
\n是换行,英文是New line.\r是回车,英文是Carriage return. 1.换行符(line break),是一种计算机语言表达方式,它的作用是跳到下一个新行.在不同的语言中,代码也有 ...
- 学习Salesforce | Einstein业务机会评分怎么玩
Einstein 业务机会评分(Opportunity Scoring)是销售团队的得力助手,通过分数以及研究影响分数的因素,确定业务机会的优先级,赢得更多交易. Einstein 业务机会评分可以给 ...
- Salesforce 开发 | Salesforce与微信集成实操指南
配置前须知 Salesforce通过试点对特定客户提供Lightning WeChat Messaging,该试点需要同意特定的条款.除非Salesforce宣布WeChat Messaging全面可 ...
- Python的深浅copy详解
Python的深浅copy详解 目录 Python的深浅copy详解 一.浅copy的原理 1.1 浅copy的定义 1.2 浅copy的方法 二.深copy的原理 2.1 深copy的定义 2.2 ...
- O - Navigation System CodeForces - 1321D
题目大意:有一个导航系统,会根据你当前的位置,规划到目的地的最短路线,给你一个有向图,和一条行驶路径,问你导航重新规划路径的最大次数和最小次数. 读题的时候题意特别不理解,何为最大次数,何为最小次数? ...
- 今天开始让我们一起来学JavaScript吧!(今天先扯先别的)
1.为什么要学习JavaScript? 首先它是web开发人员必须学习的3门语言之一: ①HTML定义了网页的内容 ②CSS描述了网页的布局: ③JavaScript网页的行为 首先JavaScrip ...
- 乱 七 八 糟 $(n.)$
\(2020/4/22\) 今天常规作业还是太慢了,白天似乎已经抓紧了,但总还能挤出时间来的.八点钟了还有物理和英语作业,回去又得很晚睡. 还是容易开小差,不过回忆了一下,今天化学课还是太懒散,其余的 ...
- 简单了解下CAP定理与BASE定理
分布式环境下的各种问题 通信异常 网络不可用风险高,消息丢失.消息延迟非常普遍 网络分区(脑裂) 网络发生异常情况,延迟增加,导致所有组成分布式系统的节点中,只有部分节点之间能够正常通信,而另一些 ...