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 ...
随机推荐
- 【python实现卷积神经网络】Dropout层实现
代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...
- undefined 和 not defined
概念上的解释: undefined是javascript语言中定义的五个原始类中的一个,换句话说,undefined并不是程序报错,而是程序允许的一个值. not defined是javascript ...
- FastAPI框架
目录 FastAPI框架 安装 基本使用 模版渲染 安装jinja2 基本使用 form表单数据交互 基本数据 文件交互 静态文件配置 FastAPI框架 该框架的速度(天然支持异步)比一般的djan ...
- 试验使用t检验
官方解释 Excel中使用T.TEST函数 T.TEST(array1,array2,tails,type) Array1 必需.第一个数据集. Array2 必需.第二个数据集. ...
- 作为python开发者,这几个PyCharm 技巧你必须掌握!
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取htt ...
- [Abp vNext 入坑分享] - 1.创建初始的项目
一.简要说明 本篇文章主要是跟着官方的文档把项目安装好先,同时了解一下大概的项目结构. 二.具体步骤 2.1全局安装ABP CLI,直接在cmd中安装即可.如果你之前安装过,这里可以略过: dotne ...
- Scrapy学习-(1)
Scrapy框架介绍 Scrapy是一个非常优秀的爬虫框架,基于python. 只需要在cmd运行pip install scrapy就可以自动安装.用scrapy-h检验是否成功安装 Scrapy部 ...
- CAS原理解析
CAS底层原理 概念 CAS的全称是Compare-And-Swap,它是CPU并发原语 它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的 CAS并发原语体现在Jav ...
- mysql 使用记录
修改 mysql 数据库密码 mysqladmin -u username -h host_name password -P <port> "new_password" ...
- Java 多线程 -- 协作模型:生产消费者实现方式二:信号灯法
使用信号灯法实现生产消费者模式需要借助标志位. 下面以演员表演,观众观看电视为列,写一个demo 同一资源 电视: //同一资源 电视 class Tv { String voice; // 信号灯 ...