所有文章

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. 人性化的HTTP命令行工具——HTTPie

    Httpie 是什么 Httpie (aych-tee-tee-pie)是一个 HTTP 的命令行客户端.其目标是让 CLI 和 web 服务之间的交互尽可能的人性化.你可以用它很方便的用 http ...

  2. 0.9.0.RELEASE版本的spring cloud alibaba sentinel+feign降级处理实例

    既然用到了feign,那么主要是针对服务消费方的降级处理.我们基于0.9.0.RELEASE版本的spring cloud alibaba nacos+feign实例添油加醋,把sentinel功能加 ...

  3. 0.9.0.RELEASE版本的spring cloud alibaba sentinel实例

    sentinel即哨兵,相比hystrix断路器而言,它的功能更丰富.hystrix仅支持熔断,当服务消费方调用提供方发现异常后,进入熔断:sentinel不仅支持异常熔断,也支持响应超时熔断,另外还 ...

  4. 解决:error: Cannot find libmysqlclient_r under /usr/local/mysql.

    libodb-mysql-2.4.0.tar.gz 解压完安装libodb-mysql时,执行完./cofigure后,出现如下错误: checking for libmysqlclient_r... ...

  5. 【Mybatis】MyBatis之缓存(七)

    MyBatis缓存介绍 Mybatis 使用到了两种缓存:一级缓存(本地缓存.local cache)和二级缓存(second level cache). 一级缓存:基于PerpetualCache ...

  6. linux记录-docker配置mysql

    docker部署mysql 1.拉取镜像 docker pull mysql 2.docker  rm   containerID  删除镜像iD 3.创建镜像 docker run --name=m ...

  7. matlab学习笔记4--MAT文件的保存和读取

    一起来学matlab-matlab学习笔记4 数据导入和导出_1 MAT文件的保存和读取 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用&g ...

  8. Linux系统调优——磁盘I/O(三)

    (1).查看I/O运行状态相关工具 1)查看文件系统块大小 对于ext4文件系统,查看文件系统块大小 [root@CentOS6 ~]# tune2fs -l /dev/sda1 | grep siz ...

  9. DEBUG技巧里的问题1 双击某个变量不能显示

    DEBUG模式  双击 ls_return-type 变量不能显示,提示警告消息 好像说明的不是这个问题, 把字段复制到右边的变量框里可以显示 这个确实有点奇怪了

  10. spring 使用Spring表达式(Spring EL)

    Spring还提供了更灵活的注入方式,那就是Spring表达式,实际上Spring EL远比以上注入方式强大,我们需要学习它.Spring EL拥有很多功能. 使用Bean的id来引用Bean. •调 ...