所有文章

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. Apache Rewrite 静态配置

    1.mod_rewrite 简介和配置 Rewirte主要的功能就是实现URL的跳转和隐藏真实地址,基于Perl语言的正则表达式规范.平时帮助我们实现拟静态,拟目录,域名跳转,防止盗链等如一个普通访问 ...

  2. mac PHP安装imageMagic扩展

    1. 安装:ImageMagick:命令:brew install ImageMagick这种方式安装下来的imageMagic,不缺少东西,报错较少.安装之后的位置:/usr/local/Cella ...

  3. c++ 网络编程基础

    目录 c++ 网络编程 建立socket 绑定socket 建立连接 监听 服务器端接收 数据发送和接收 面向连接的数据发送 面向连接的数据接收 无连接的数据发送 无连接的数据接收 关闭socket ...

  4. flutter 路由动画

    import 'package:flutter/material.dart'; import 'package:flutter_app/pages/FirstPage.dart'; void main ...

  5. ISO/IEC 9899:2011 条款6.7——声明

    6.7 声明 语法 1.declaration: declaration-specifiers    init-declarator-listopt    ; static_assert-declar ...

  6. Python3基础 函数 多值参数 元组与字典形式(使用星号对列表与字典进行拆包)

             Python : 3.7.3          OS : Ubuntu 18.04.2 LTS         IDE : pycharm-community-2019.1.3    ...

  7. c++调用动态库失败解决办法

    c++调用动态库失败解决办法 之前写好的程序今天早上过来发现在服务器上出错了,于是就各种查问题,整整一个早上外加下午两个小时都在查这个问题,最终被我找到了问题: 在程序中我发现LoadLibrary( ...

  8. QWidget中结束QThread线程

    QThread安全结束 protected: void closeEvent(QCloseEvent *event); void closeEvent(QCloseEvent *event) { th ...

  9. DDos攻击解决办法

    (1).DDos概念 分布式拒绝服务攻击(英文意思是Distributed Denial of Service,简称DDoS)是指处于不同位置的多个攻击者同时向一个或数个目标发动攻击,或者一个攻击者控 ...

  10. VS2010/VS2012/VS2015下openGL环境配置(转)

    按:按照下述博文,三个例子均成功. https://blog.csdn.net/so_geili/article/details/51685005 请仔细阅读每一个字. 为了学习<OpenGL超 ...