转 Hystrix超时实现机制
HystrixCommand在执行的过程中如何探测超时,本篇主要对此进行介绍说明。
1.主入口:executeCommandAndObserve
#com.netflix.hystrix.AbstractCommand#executeCommandAndObserve
private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) {
···省略部分代码···
Observable<R> execution;
//判断是否开启超时监测
if (properties.executionTimeoutEnabled().get()) {
execution = executeCommandWithSpecifiedIsolation(_cmd)
.lift(new HystrixObservableTimeoutOperator<R>(_cmd));
} else {
execution = executeCommandWithSpecifiedIsolation(_cmd);
}
return execution.doOnNext(markEmits)
.doOnCompleted(markOnCompleted)
.onErrorResumeNext(handleFallback)
.doOnEach(setRequestContext);
}
executeCommandWithSpecifiedIsolation(_cmd) .lift(new HystrixObservableTimeoutOperator<R>(_cmd));
可以简单的认为lift 里面的对前面的Observable包含,类似装饰者,后面的parent就是指上层的Observable。其中 HystrixObservableTimeoutOperator 就是关键的部分。
2.关键点: HystrixObservableTimeoutOperator
先看下HystrixObservableTimeoutOperator.call(),TimerListener的实现
TimerListener listener = new TimerListener() {
@Override
public void tick() {
if (originalCommand.isCommandTimedOut.compareAndSet(TimedOutStatus.NOT_EXECUTED, TimedOutStatus.TIMED_OUT)) {
// 标记事件,可以认为是开的hook,这里暂忽略
originalCommand.eventNotifier.markEvent(HystrixEventType.TIMEOUT, originalCommand.commandKey);
//取消原Obserable的订阅
s.unsubscribe();
final HystrixContextRunnable timeoutRunnable = new HystrixContextRunnable(originalCommand.concurrencyStrategy, hystrixRequestContext, new Runnable() {
@Override
public void run() {
child.onError(new HystrixTimeoutException());
}
});
timeoutRunnable.run();
}
}
//获取配置的超时时间配置
@Override
public int getIntervalTimeInMilliseconds() {
return originalCommand.properties.executionTimeoutInMilliseconds().get();
}
};
这段代码的意思就是,给当前command的超时状态置为超时,如果设置成功就抛出HystrixTimeoutException
异常,紧接着被command的 doOnErron接收走 fallback逻辑
fallback
private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) {
final HystrixRequestContext currentRequestContext = HystrixRequestContext.getContextForCurrentThread();
.................................
final Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() {
@Override
public Observable<R> call(Throwable t) {
circuitBreaker.markNonSuccess();
Exception e = getExceptionFromThrowable(t);
executionResult = executionResult.setExecutionException(e);
if (e instanceof RejectedExecutionException) {
return handleThreadPoolRejectionViaFallback(e);
} else if (t instanceof HystrixTimeoutException) {
//此处catch到超时异常
return handleTimeoutViaFallback();
} else if (t instanceof HystrixBadRequestException) {
return handleBadRequestByEmittingError(e);
} else {
/*
* Treat HystrixBadRequestException from ExecutionHook like a plain HystrixBadRequestException.
*/
if (e instanceof HystrixBadRequestException) {
eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey);
return Observable.error(e);
}
return handleFailureViaFallback(e);
}
}
};
.................................
return execution.doOnNext(markEmits)
.doOnCompleted(markOnCompleted)
.onErrorResumeNext(handleFallback)
.doOnEach(setRequestContext);
}
同时s.unsubscribe()
通知正在执行的线程,终止任务。如何终止呢?
executeCommandWithSpecifiedIsolation.subscribeOn()
subscribeOne的参数就是HystrixContextScheduler
, Rxjava里 scheduler具体干活的是 worker
,我们先看下Hystrix自定义scheduler的结构示意图
那么我们直奔主题,直接看 ThreadPoolWorker
//ThreadPoolWorker.schedule
@Override
public Subscription schedule(final Action0 action) {
if (subscription.isUnsubscribed()) {
return Subscriptions.unsubscribed();
}
ScheduledAction sa = new ScheduledAction(action);
subscription.add(sa);
sa.addParent(subscription);
ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor();
FutureTask<?> f = (FutureTask<?>) executor.submit(sa);
sa.add(new FutureCompleterWithConfigurableInterrupt(f, shouldInterruptThread, executor));
return sa;
}
1.开始的时候判断observable是否被订阅
2.被订阅后,将任务 submit到线程池
3.FutureCompleterWithConfigurableInterrupt
scheduler在执行的时候,增加了observable的中断探测
private static class FutureCompleterWithConfigurableInterrupt implements Subscription {
private final FutureTask<?> f;
private final Func0<Boolean> shouldInterruptThread;
private final ThreadPoolExecutor executor;
private FutureCompleterWithConfigurableInterrupt(FutureTask<?> f, Func0<Boolean> shouldInterruptThread, ThreadPoolExecutor executor) {
this.f = f;
this.shouldInterruptThread = shouldInterruptThread;
this.executor = executor;
}
@Override
public void unsubscribe() {
executor.remove(f);
if (shouldInterruptThread.call()) {
f.cancel(true);
} else {
f.cancel(false);
}
}
.....省略代码.......
}
当observable 取消订阅时,就会把当前任务移除,并中断任务
到这里只是讲说了超时后的处理,如何认定执行超时呢?
3.匠心之巧
这里有个很巧妙的设计,再探HystrixObservableTimeoutOperator
final Reference<TimerListener> tl = HystrixTimer.getInstance().addTimerListener(listener);
#com.netflix.hystrix.util.HystrixTimer#addTimerListener
public Reference<TimerListener> addTimerListener(final TimerListener listener) {
startThreadIfNeeded();
// add the listener
Runnable r = new Runnable() {
@Override
public void run() {
try {
listener.tick();
} catch (Exception e) {
logger.error("Failed while ticking TimerListener", e);
}
}
};
ScheduledFuture<?> f = executor.get().getThreadPool().scheduleAtFixedRate(r, listener.getIntervalTimeInMilliseconds(), listener.getIntervalTimeInMilliseconds(), TimeUnit.MILLISECONDS);
return new TimerReference(listener, f);
}
利用了ScheduledThreadPoolExecutor
,延迟执行,延迟时间就是我们设定的超时时间,我们再看下
#HystrixObservableTimeoutOperator
Subscriber<R> parent = new Subscriber<R>() {
@Override
public void onCompleted() {
if (isNotTimedOut()) {
// stop timer and pass notification through
tl.clear();
child.onCompleted();
}
}
@Override
public void onError(Throwable e) {
if (isNotTimedOut()) {
// stop timer and pass notification through
tl.clear();
child.onError(e);
}
}
..... ..... ..... ..... ..... ..... ..... ..... .....
private boolean isNotTimedOut() {
// if already marked COMPLETED (by onNext) or succeeds in setting to COMPLETED
return originalCommand.isCommandTimedOut.get() == TimedOutStatus.COMPLETED ||
originalCommand.isCommandTimedOut.compareAndSet(TimedOutStatus.NOT_EXECUTED, TimedOutStatus.COMPLETED);
}
};
这里parent就是指上层的obserable,这里可以抽象的认为是我们的HystrixCommand执行线程, 当command执行线程执行完成的时候或异常的时候,会执行 tl.clear(), 也就是Future.cancel()会中断 TimerListener
的ScheduledFuture 线程,迫使超时机制失效。
// tl.clear()
private static class TimerReference extends SoftReference<TimerListener> {
private final ScheduledFuture<?> f;
.... .... .... .... ....
@Override
public void clear() {
super.clear();
// stop this ScheduledFuture from any further executions
f.cancel(false);
}
}
4.回归文字
HystrixCommand里有个 TimedOutStatus
超时状态
现在可以认为有两个线程,一个是hystrixCommand任务执行线程,一个是等着给hystrixCommand判定超时的线程,现在两个线程看谁能先把hystrixCommand的状态置换,只要任何一个线程对hystrixCommand打上标就意味着超时判定结束。
系列文章推荐
作者:青芒v5
链接:https://www.jianshu.com/p/60074fe1bd86
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
转 Hystrix超时实现机制的更多相关文章
- 使用Hystrix的插件机制,解决在使用线程隔离时,threadlocal的传递问题
背景 在我们的项目中,比较广泛地使用了ThreadLocal,比如,在filter层,根据token,取到用户信息后,就会放到一个ThreadLocal变量中:在后续的业务处理中,就会直接从当前线程, ...
- 使用druid连接池的超时回收机制排查连接泄露问题
在工程中使用了druid连接池,运行一段时间后系统出现异常: Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: ...
- 不同浏览器的DNS超时重发机制(一)
一.Chrome浏览器(37.0.2062.124 m) 1.在Win7环境下,DNS超时重发的时间间隔为:2s.2s.2s.2s(在这个时刻重复发2个DNS请求).2s.4s,再经过大约14s左右, ...
- Volley超时重试机制
基础用法 Volley为开发者提供了可配置的超时重试机制,我们在使用时只需要为我们的Request设置自定义的RetryPolicy即可. 参考设置代码如下: int DEFAULT_TIMEOUT_ ...
- Dubbo超时重试机制带来的数据重复问题
Dubbo的超时重试机制为服务容错.服务稳定提供了比较好的框架支持,但是在一些比较特殊的网络环境下(网络传输慢,并发多)可能 由于服务响应慢,Dubbo自身的超时重试机制(服务端的处理时间超过了设定的 ...
- 【原创】TCP超时重传机制探索
TCP超时重传机制探索 作者:tll (360电商技术) 1)通信模型 TCP(Transmission Control Protocol)是一种可靠传输协议.在传输过程中当发送方(sender)向接 ...
- Hystrix超时测试
package com.cookie.test; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.Hystr ...
- springcloud超时重试机制的先后顺序
https://blog.csdn.net/zzzgd_666/article/details/83314833
- C++服务器设计(四):超时管理机制设计
前四章介绍了系统层的设计,从这一章开始进入服务层的设计. 连接断开 在常见的服务器场景中,客户端断开连接的方式为被动关闭.即作为客户端请求完服务器的服务后,选择主动关闭同服务器的连接.在服务器的角度看 ...
随机推荐
- 跨域漏洞丨JSONP和CORS跨域资源共享
进入正文之前,我们先来解决个小问题,什么是跨域? 跨域:指的是浏览器不能执行其它网站的脚本,它是由浏览器的同源策略造成的,是浏览器的安全限制! 跨域常见的两种方式,分别是JSONP和CORS. 今天i ...
- 隐马尔科夫模型(Hidden Markov Models) 系列之二
转自:http://blog.csdn.net/eaglex/article/details/6385204 隐含模式(Hidden Patterns) 当马尔科夫过程不够强大的时候,我们又该怎么办呢 ...
- Bugku 代码审计
0x01.extract变量覆盖 代码: <?php$flag='xxx';extract($_GET);if(isset($shiyan)){$content=trim(file_get_co ...
- metrics-server 安装问题解决
参考: https://www.qikqiak.com/post/install-metrics-server/ git clone https://github.com/kubernetes- ...
- angular6 监听url查询参数变化刷新页面
快照snapshot取到的参数是组件第一次渲染时候的参数,当我们在页面中需要根据不同的url查询参数显示不同的内容时,快照就不能满足我们的需要了,这时候就要用ActivatedRoute服务的quer ...
- Java的自动拆装箱与Integer的缓存机制
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10832303.html 一:基本类型与包装类型 我们知道,Java有8大基本数据类型,4整2浮1符1 ...
- Removing Stones(2019年牛客多校第三场G+启发式分治)
目录 题目链接 题意 思路 代码 题目链接 传送门 题意 初始时有\(n\)堆石子,每堆石子的石子个数为\(a_i\),然后进行游戏. 游戏规则为你可以选择任意两堆石子,然后从这两堆中移除一个石子,最 ...
- Spring Cloud微服务安全实战_3-1_API安全 常见的安全机制
本章讲解,在不考虑微服务,只考虑一个简单的API ,如何保证这个API的安全? 三个问题: 1,什么是API ? 2,API安全的要素有哪些? 3,API安全基本机制 一.什么是API 百度百科:AP ...
- Python——IO多路复用之select模块poll方法
Python——IO多路复用之select模块poll方法 使用poll方法实现IO多路复用 .├── poll_client.py├── poll_server.py└── settings.py ...
- windows下如何制作和应用数字签名证书 全流程
目前我们在发布应用程序时,有时用户下载后会被360杀毒当做木马直接隔离.为应用程序可执行文件打上数字签名可以让360杀毒放宽检测规则.下文是讲述如何制作数字签名证书的过程. 需要准备的工具:makec ...