所有文章

https://www.cnblogs.com/lay2017/p/11908715.html

正文

HystrixInvocationHandler

hystrix是开源的一个熔断组件,springcloud将其集成并默认与openfeign组合使用。而openfeign又是基于jdk动态代理生成接口的代理对象的,hystrix肯定是集成在feign的接口调用过程当中的。

所以,hystrix的熔断集成到openfeign当中就落到了jdk动态代理的InvocationHandler上。那么hystrix对它的实现又是什么呢?

那么,我们接着跟进HystrixInvocationHandler的invoke方法

public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
// ... HystrixCommand<Object> hystrixCommand =
new HystrixCommand<Object>(setterMethodMap.get(method)) {
@Override
protected Object run() throws Exception {
try {
// 获取并调用MethodHandler,MethodHandler封装了Http请求,ribbon也在这里被集成
return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
} catch (Exception e) {
throw e;
} catch (Throwable t) {
throw (Error) t;
}
} @Override
protected Object getFallback() {
// ... fallback降级的处理
}
}; // ... return hystrixCommand.execute();
}

invoke的代码很长,删除后显得清晰一点。hystrix由于采用了rxjava,代码逻辑阅读起来有点恶心。

核心正向调用流程依旧是通过Method来获取对应的MethodHandler,MethodHandler负责执行http请求,并返回结果。

那么hystrix在这里干了啥呢?

比较显而易见的是fallback部分,fallback含义很明显了,达到一定的失败条件就会触发降级处理。所以,我们可以自定义fallback实现,保证高可用。

而熔断的核心逻辑就落到了hystrixCommand的execute方法里面

hystrixCommand

接下来,跟进hystrixCommand的execute方法

public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}

queue返回一个future,get方法将获取异步结果,跟进queue

public Future<R> queue() {
final Future<R> delegate = toObservable().toBlocking().toFuture(); final Future<R> f = new Future<R>() {
// ... future实现,调用delegate的对应实现
}; // ... return f;
}

可以看到,核心逻辑落到了toObservable上,跟进它

public Observable<R> toObservable() {
final AbstractCommand<R> _cmd = this; // ... final Func0<Observable<R>> applyHystrixSemantics = new Func0<Observable<R>>() {
@Override
public Observable<R> call() {
if (commandState.get().equals(CommandState.UNSUBSCRIBED)) {
return Observable.never();
}
return applyHystrixSemantics(_cmd);
}
}; // ...
}

方法非常长,这里只关注一下applyHystrixSemantics方法

private Observable<R> applyHystrixSemantics(final AbstractCommand<R> _cmd) {
// ... // 是否允许请求
if (circuitBreaker.allowRequest()) {
final TryableSemaphore executionSemaphore = getExecutionSemaphore(); // ... if (executionSemaphore.tryAcquire()) {
try {
executionResult = executionResult.setInvocationStartTime(System.currentTimeMillis());
// 执行业务
return executeCommandAndObserve(_cmd)
.doOnError(markExceptionThrown)
.doOnTerminate(singleSemaphoreRelease)
.doOnUnsubscribe(singleSemaphoreRelease);
} catch (RuntimeException e) {
return Observable.error(e);
}
} else {
// 信号量获取失败,走fallback
return handleSemaphoreRejectionViaFallback();
}
} else {
// 快速熔断,走fallback
return handleShortCircuitViaFallback();
}
}

applyHystrixSemantics通过熔断器的allowRequest方法判断是否需要快速失败走fallback,如果允许执行那么又会经过一层信号量的控制,都通过才会走execute。

所以,核心逻辑就落到了allowRequest上,跟进它

@Override
public boolean allowRequest() {
// 强制开启熔断
if (properties.circuitBreakerForceOpen().get()) {
return false;
}
// 强制关闭熔断
if (properties.circuitBreakerForceClosed().get()) {
isOpen();
return true;
}
// 未开启熔断 或者 允许单个测试
return !isOpen() || allowSingleTest();
}

hystrix允许强制开启或者关闭熔断,如果不想有请求执行就开启,如果觉得可以忽略所有错误就关闭。

在没有强制开关的情况下,主要就是判断当前熔断是否开启。另外,我们不免注意到这里还有一个allowSingleTest方法。在熔断器开启的情况下,会在一定时间后允许发出一个测试的请求,来判断是否开启熔断器。

我们先进入isOpen方法,看看如果判断当前熔断器的开启

public boolean isOpen() {
// 开关是开启的,直接返回
if (circuitOpen.get()) {
return true;
} // 开关未开启,获取健康统计
HealthCounts health = metrics.getHealthCounts(); // 总请求数太小的情况,不开启熔断
if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
return false;
} // 总请求数够了,失败率比较小的情况,不开启熔断
if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
return false;
} else {
// 总请求数和失败率都比较大的时候,设置开关为开启,进行熔断
if (circuitOpen.compareAndSet(false, true)) {
circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
return true;
} else {
return true;
}
}
}

总体逻辑就是判断一个失败次数是否达到开启熔断的条件,如果达到那么设置开启的开关。

在熔断一直开启的情况下,偶尔会放过一个测试请求来判断是否关闭。那么我们就跟进allowSingleTest看看

public boolean allowSingleTest() {
// 获取熔断开启时间,或者上一次的测试时间
long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get(); // 如果熔断处于开启状态,且当前时间距离熔断开启时间或者上一次执行测试请求时间已经到了
if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) {
// cas控制熔断开启
if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) {
return true;
}
}
// 否则不允许
return false;
}

其实就是在一定时间窗口下释放一个测试请求出去,这里还使用了cas机制来控制一定时间窗口只会释放一个请求出去。

总结

到这里,本文就结束了。hystrix其实就是在feign的调用过程插了一脚,通过对请求的成功失败的统计数据来开关是否进行熔断。又在每个时间窗口内发送一个测试请求出去,来判断是否关闭熔断。总得来说还是很清晰实用的。不过最后还是要说一句,rxjava的代码太恶心了,哈哈

一、hystrix如何集成在openfeign中使用的更多相关文章

  1. 一、ribbon如何集成在openfeign中使用

    所有文章 https://www.cnblogs.com/lay2017/p/11908715.html 正文 ribbon是springcloud封装的一个基于http客户端负载均衡的组件.spri ...

  2. 【笔记】android sdk集成的eclipse中导入项目

    android sdk集成的eclipse中导入项目 想要把旧的ADT项目,一模一样的导入进来,需要: 1.把项目放到,非当前ADT的workspace目录下: 2.从Project中Import,选 ...

  3. Spark Streaming揭秘 Day28 在集成开发环境中详解Spark Streaming的运行日志内幕

    Spark Streaming揭秘 Day28 在集成开发环境中详解Spark Streaming的运行日志内幕 今天会逐行解析一下SparkStreaming运行的日志,运行的是WordCountO ...

  4. 持续集成:TestNG中case之间的关系

    持续集成:TestNG中case之间的关系   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq: ...

  5. 第三百五十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中

    第三百五十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中,判断URL是否重复 布隆过滤器(Bloom Filter)详 ...

  6. 第三百五十一节,Python分布式爬虫打造搜索引擎Scrapy精讲—将selenium操作谷歌浏览器集成到scrapy中

    第三百五十一节,Python分布式爬虫打造搜索引擎Scrapy精讲—将selenium操作谷歌浏览器集成到scrapy中 1.爬虫文件 dispatcher.connect()信号分发器,第一个参数信 ...

  7. 如何将SLIC集成到ESXi中

    如何将SLIC集成到ESXi中 参考 http://forums.mydigitallife.info/threads/12982-ESX-ESXi-Bios-Tools/page34?p=72183 ...

  8. 解决duilib使用zip换肤卡顿的问题(附将资源集成到程序中的操作方法)

    转载请说明原出处,谢谢~~ 今天在做单子是.客户要求做换肤功能,为此我专门写了一个换肤函数,而且把各种皮肤资源压缩为各个zip文件来换肤.可是客户反映程序执行缓慢,我測试后发现的确明显能够看出慢了不少 ...

  9. 三十七 Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中

    Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中,判断URL是否重复 布隆过滤器(Bloom Filter)详解 基本概念 如 ...

随机推荐

  1. Andorid SQLite数据库开发基础教程(3)

    Andorid SQLite数据库开发基础教程(3) 数据库生成方式 数据库的生成有两种方式,一种是使用数据库管理工具生成的数据库,我们将此类数据库称为预设数据库,另一种是使用代码生成的数据库. 使用 ...

  2. svn: E205007: None of the environment variables SVN_EDITOR

    svn propedit svn:ignore .svn: E205007: None of the environment variables SVN_EDITOR, VISUAL or EDITO ...

  3. IDEA分配内存无效

    idea改启动内存分配, 改 C:/Users/xxx/.IntelliJIdea2018.1/confing/idea64.exe.vmoptions 或 C:/Users/xxx/.Intelli ...

  4. Glide图片加载过程(简)

    iceIC 关注 2018.10.25 20:53* 字数 906 阅读 529评论 0喜欢 1 调研版本为4.7.1为了更加简单的理解,会将函数代码简化,详细代码请自行照源码对比 Glide用法 G ...

  5. 转 检查rac服务时,发现ons服务offline

    检查rac服务时,发现ons服务offline 时间:2014-12-23 11:17:37  作者:solgle  来源:www.solgle.com  查看:4075  评论:0         ...

  6. Apache Flink 开发环境搭建和应用的配置、部署及运行

    https://mp.weixin.qq.com/s/noD2Jv6m-somEMtjWTJh3w 本文是根据 Apache Flink 系列直播课程整理而成,由阿里巴巴高级开发工程师沙晟阳分享,主要 ...

  7. 使用idea创建webservice客户端

     new project: Generate Java Code From Wsdl: 导入junit.jar,编写测试类:

  8. Apache2.4+PHP7.2配置站点访问变下载

    问题描述:Apache正常工作,php-fpm正常工作,访问网址就变下载,显然是站点无法正常解析php造成的.php-fpm与apache的通信出现了问题. 解决方案: 如果php-fpm使用的是TC ...

  9. [计算机视觉][ARM-Linux开发]OpenCV 3.1下载 ippicv_linux_20151201失败

    安装OpenCV 3.1的过程中要下载ippicv_linux_20151201,由于网络的原因,这个文件经常会下载失败. 解决的办法是手动下载: 先下载 OpenCV 3.1 Download MD ...

  10. 【GStreamer开发】GStreamer基础教程13——播放速度

    目标 快进,倒放和慢放是trick模式的共同技巧,它们有一个共同点就是它们都修改了播放的速度.本教程会展示如何来获得这些效果和如何进行逐帧的跳跃.主要内容是: 如何来变换播放的速度,变快或者变慢,前进 ...