所处环境
spring boot
: 1.5.9.RELEASEspring cloud
: Edgware.RELEASE
问题描述
根据netflix hystrix官方的描述,可以通过hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
这个全局配置为所有服务配置默认超时时间。然而在实践中却发现该配置并没有生效(一直是2000ms),但针对各服务进行单独配置却是可以生效的。
问题定位
经google发现,hystrix的timeout机制是通过HystrixTimer
来处理的。
HystrixTimer是一个单例,开发人员可以在执行HystrixCommand
前通过调用HystrixTimer.getInstance().addTimerListener()方法来添加一个定时的listener,然后在command on completed的时候移除它。相关的代码已经由netflix实现了,可以查看源码AbstractCommand.java
TimerListener listener = new TimerListener() {
@Override
public int getIntervalTimeInMilliseconds() {
return originalCommand.properties.executionTimeoutInMilliseconds().get();
}
};
final Reference<TimerListener> tl = HystrixTimer.getInstance().addTimerListener(listener);
// set externally so execute/queue can see this
originalCommand.timeoutTimer.set(tl);
/**
* If this subscriber receives values it means the parent succeeded/completed
*/
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);
}
}
};
从以上代码可以很容易追溯到hystrix超时时间是从originalCommand.properties(HystrixCommandProperties
)这个对象中获取的,而在spring cloud提供的AbstractRibbonCommand
中存在以下代码
final HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(zuulProperties.getRibbonIsolationStrategy()).withExecutionTimeoutInMilliseconds(
RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT + RibbonClientConfiguration.DEFAULT_READ_TIMEOUT);
导致originalCommand.properties在构建时可以获取到一个executionTimeoutInMilliseconds的实例级默认值,从而覆盖掉了全局的executionTimeoutInMilliseconds配置,但实例级的配置并不受影响,因为hystrix property优先级为:
- Global default from code
- Dynamic global default property
- Instance default from code
- Dynamic instance property
解决方案
改写HttpClientRibbonCommand的实现
//todo:: 待补充升级spring cloud版本到Edgware.SR1以上
Spring cloud在Edgware.SR1
版本中已经解决了此问题(issue2633),如果有条件可以直接通过升级版本来解决此问题。