public R execute(){try{//queue()方法返回future,再调用get阻塞等待returnqueue().get();}catch(Exception e){throw Exceptions.sneakyThrow(decomposeException(e));}}public Future<R>queue(){//toObservable会把服务封装为Observablefinal Future<R> delegate =toObservable().toBlocking().toFuture();...}
public Observable<R>toObservable(){final AbstractCommand<R> _cmd =this;//命令执行完的回调操作final Action0 terminateCommandCleanup =newAction0(){...};//unsubscribe取消订阅时执行的操作final Action0 unsubscribeCommandCleanup =newAction0(){...};//这个是执行命令时的回调函数,重点final Func0<Observable<R>> applyHystrixSemantics =newFunc0<Observable<R>>(){@Overridepublic Observable<R>call(){if(commandState.get().equals(CommandState.UNSUBSCRIBED)){return Observable.never();}returnapplyHystrixSemantics(_cmd);}};final Func1<R, R> wrapWithAllOnNextHooks =newFunc1<R, R>(){...};final Action0 fireOnCompletedHook =newAction0(){...};//创建Observable,设置各种处理操作return Observable.defer(newFunc0<Observable<R>>(){@Overridepublic Observable<R>call(){//设置已启动标志if(!commandState.compareAndSet(CommandState.NOT_STARTED, CommandState.OBSERVABLE_CHAIN_CREATED)){
IllegalStateException ex =newIllegalStateException("This instance can only be executed once. Please instantiate a new instance.");//TODO make a new error type for thisthrownewHystrixRuntimeException(FailureType.BAD_REQUEST_EXCEPTION, _cmd.getClass(),getLogMessagePrefix()+" command executed multiple times - this is not permitted.", ex, null);}
commandStartTimestamp = System.currentTimeMillis();...//缓存处理finalboolean requestCacheEnabled =isRequestCachingEnabled();final String cacheKey =getCacheKey();/* try from cache first */if(requestCacheEnabled){
HystrixCommandResponseFromCache<R> fromCache =(HystrixCommandResponseFromCache<R>) requestCache.get(cacheKey);if(fromCache != null){
isResponseFromCache =true;returnhandleRequestCacheHitAndEmitValues(fromCache, _cmd);}}//创建Observable,注意内部使用applyHystrixSemantics来生成Observable
Observable<R> hystrixObservable =
Observable.defer(applyHystrixSemantics).map(wrapWithAllOnNextHooks);
Observable<R> afterCache;// put in cache 根据是否缓存来做处理if(requestCacheEnabled && cacheKey != null){// wrap it for caching
HystrixCachedObservable<R> toCache = HystrixCachedObservable.from(hystrixObservable, _cmd);
HystrixCommandResponseFromCache<R> fromCache =(HystrixCommandResponseFromCache<R>) requestCache.putIfAbsent(cacheKey, toCache);if(fromCache != null){// another thread beat us so we'll use the cached value instead
toCache.unsubscribe();
isResponseFromCache =true;returnhandleRequestCacheHitAndEmitValues(fromCache, _cmd);}else{// we just created an ObservableCommand so we cast and return it
afterCache = toCache.toObservable();}}else{
afterCache = hystrixObservable;}//这个afterCache最终是前面的hystrixObservable,或由他封装而来,//这里设置各种监听处理操作return afterCache
.doOnTerminate(terminateCommandCleanup)// perform cleanup once (either on normal terminal state (this line), or unsubscribe (next line)).doOnUnsubscribe(unsubscribeCommandCleanup)// perform cleanup once.doOnCompleted(fireOnCompletedHook);}});}
final Func0<Observable<R>> applyHystrixSemantics =newFunc0<Observable<R>>(){@Overridepublic Observable<R>call(){if(commandState.get().equals(CommandState.UNSUBSCRIBED)){return Observable.never();}//调用AbstractCommand的方法returnapplyHystrixSemantics(_cmd);}};private Observable<R>applyHystrixSemantics(final AbstractCommand<R> _cmd){// mark that we're starting execution on the ExecutionHook// if this hook throws an exception, then a fast-fail occurs with no fallback. No state is left inconsistent
executionHook.onStart(_cmd);//执行前判断断路器是否打开if(circuitBreaker.allowRequest()){//拿到该服务的信号量final TryableSemaphore executionSemaphore =getExecutionSemaphore();final AtomicBoolean semaphoreHasBeenReleased =newAtomicBoolean(false);final Action0 singleSemaphoreRelease =newAction0(){@Overridepublicvoidcall(){if(semaphoreHasBeenReleased.compareAndSet(false,true)){
executionSemaphore.release();}}};final Action1<Throwable> markExceptionThrown =newAction1<Throwable>(){@Overridepublicvoidcall(Throwable t){
eventNotifier.markEvent(HystrixEventType.EXCEPTION_THROWN, commandKey);}};//尝试获取信号量if(executionSemaphore.tryAcquire()){try{/* used to track userThreadExecutionTime */
executionResult = executionResult.setInvocationStartTime(System.currentTimeMillis());//调用executeCommandAndObserve创建Observable,并设置各种回调returnexecuteCommandAndObserve(_cmd).doOnError(markExceptionThrown).doOnTerminate(singleSemaphoreRelease).doOnUnsubscribe(singleSemaphoreRelease);}catch(RuntimeException e){return Observable.error(e);}}else{//获取失败则降级returnhandleSemaphoreRejectionViaFallback();}}else{//断路器已打开,直接降级returnhandleShortCircuitViaFallback();}}
private Observable<R>executeCommandWithSpecifiedIsolation(final AbstractCommand<R> _cmd){//线程隔离if(properties.executionIsolationStrategy().get()== ExecutionIsolationStrategy.THREAD){//创建一个Observablereturn Observable.defer(newFunc0<Observable<R>>(){@Overridepublic Observable<R>call(){...//设置线程启动if(threadState.compareAndSet(ThreadState.NOT_USING_THREAD, ThreadState.STARTED)){//we have not been unsubscribed, so should proceed
HystrixCounters.incrementGlobalConcurrentThreads();
threadPool.markThreadExecution();// store the command that is being run
endCurrentThreadExecutingCommand = Hystrix.startCurrentThreadExecutingCommand(getCommandKey());
executionResult = executionResult.setExecutedInThread();/**
* If any of these hooks throw an exception, then it appears as if the actual execution threw an error
*/try{
executionHook.onThreadStart(_cmd);
executionHook.onRunStart(_cmd);
executionHook.onExecutionStart(_cmd);//返回一个Observable,这个函数最终会返回一个封装了我们的run()逻辑的ObservablereturngetUserExecutionObservable(_cmd);}catch(Throwable ex){return Observable.error(ex);}}else{//command has already been unsubscribed, so return immediatelyreturn Observable.error(newRuntimeException("unsubscribed before executing run()"));}}//回调设置}).doOnTerminate(newAction0(){...}).doOnUnsubscribe(newAction0(){...}).subscribeOn(threadPool.getScheduler(newFunc0<Boolean>(){...}));}else{//信号量隔离...}}
private Observable<R>applyHystrixSemantics(final AbstractCommand<R> _cmd){// mark that we're starting execution on the ExecutionHook// if this hook throws an exception, then a fast-fail occurs with no fallback. No state is left inconsistent
executionHook.onStart(_cmd);//执行前判断断路器是否打开if(circuitBreaker.allowRequest()){//拿到该服务的信号量final TryableSemaphore executionSemaphore =getExecutionSemaphore();final AtomicBoolean semaphoreHasBeenReleased =newAtomicBoolean(false);final Action0 singleSemaphoreRelease =newAction0(){@Overridepublicvoidcall(){if(semaphoreHasBeenReleased.compareAndSet(false,true)){
executionSemaphore.release();}}};}...}
接下来看一下HystrixCircuitBreakerImpl的allowRequest方法
@OverridepublicbooleanallowRequest(){//检查是否配置了forceOpen属性,是则返回false,代表不允许执行if(properties.circuitBreakerForceOpen().get()){// properties have asked us to force the circuit open so we will allow NO requestsreturnfalse;}//检查是否配置了forceClose属性,是则返回true,代表允许执行if(properties.circuitBreakerForceClosed().get()){// we still want to allow isOpen() to perform it's calculations so we simulate normal behaviorisOpen();// properties have asked us to ignore errors so we will ignore the results of isOpen and just allow all traffic throughreturntrue;}//如果没配置以上属性,那么断路器会根据当前命令调用情况判断断路器是否打开return!isOpen()||allowSingleTest();}
看一下isOpen方法
@OverridepublicbooleanisOpen(){//若断路器已打开,直接返回trueif(circuitOpen.get()){// if we're open we immediately return true and don't bother attempting to 'close' ourself as that is left to allowSingleTest and a subsequent successful test to closereturntrue;}//如果现在断路器关闭,还需要通过metrics的指标判断以下是否已经达到了打开的标准
HealthCounts health = metrics.getHealthCounts();// 判断请求次数是否到达阈值,未到达则不进行判断,直接返回falseif(health.getTotalRequests()< properties.circuitBreakerRequestVolumeThreshold().get()){// we are not past the minimum volume threshold for the statisticalWindow so we'll return false immediately and not calculate anythingreturnfalse;}//判断异常率是否小于阈值if(health.getErrorPercentage()< properties.circuitBreakerErrorThresholdPercentage().get()){returnfalse;}else{//到这里说明异常率高于阈值了,需要打开断路器if(circuitOpen.compareAndSet(false,true)){// if the previousValue was false then we want to set the currentTime
circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());returntrue;}else{//这里说明其他线程已经设置了断路器打开,直接返回truereturntrue;}}}
publicbooleanallowSingleTest(){long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get();//如果当前断路器已经打开,并且已经过了时间窗口指定的时间,则允许执行if(circuitOpen.get()&& System.currentTimeMillis()> timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()){// We push the 'circuitOpenedTime' ahead by 'sleepWindow' since we have allowed one request to try.// If it succeeds the circuit will be closed, otherwise another singleTest will be allowed at the end of the 'sleepWindow'.if(circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())){// if this returns true that means we set the time so we'll return true to allow the singleTest// if it returned false it means another thread raced us and allowed the singleTest before we didreturntrue;}}returnfalse;}
当命令执行成功后,会调用markSuccess命令,关闭断路器,并重置Metrics的统计信息
publicvoidmarkSuccess(){if(circuitOpen.get()){if(circuitOpen.compareAndSet(true,false)){//win the thread race to reset metrics//Unsubscribe from the current stream to reset the health counts stream. This only affects the health counts view,//and all other metric consumers are unaffected by the reset
metrics.resetStream();}}}
4.Hystrix隔离策略
概述
Hystrix的隔离策略主要有
信号量隔离:每个命令执行前必须拿到信号量,如果拿不到,就调用fallback方法降级
线程隔离:任务执行在单独的线程里
源码分析
在第二节分析命令执行过程中,有如下过程
private Observable<R>applyHystrixSemantics(final AbstractCommand<R> _cmd){...//判断断路器是否打开if(circuitBreaker.allowRequest()){//拿到执行的信号量,不同隔离策略的信号量实现类不同final TryableSemaphore executionSemaphore =getExecutionSemaphore();final AtomicBoolean semaphoreHasBeenReleased =newAtomicBoolean(false);...//尝试获取信号量if(executionSemaphore.tryAcquire()){try{/* used to track userThreadExecutionTime */
executionResult = executionResult.setInvocationStartTime(System.currentTimeMillis());returnexecuteCommandAndObserve(_cmd).doOnError(markExceptionThrown).doOnTerminate(singleSemaphoreRelease).doOnUnsubscribe(singleSemaphoreRelease);}catch(RuntimeException e){return Observable.error(e);}}else{//获取失败则直接降级returnhandleSemaphoreRejectionViaFallback();}}else{returnhandleShortCircuitViaFallback();}}
public Subscriber<?super R>call(final Subscriber<?super R> child){final CompositeSubscription s =newCompositeSubscription();// if the child unsubscribes we unsubscribe our parent as well
child.add(s);//超时的策略,调用onError发布超时事件final HystrixContextRunnable timeoutRunnable =newHystrixContextRunnable(originalCommand.concurrencyStrategy,newRunnable(){@Overridepublicvoidrun(){
child.onError(newHystrixTimeoutException());}});//当命令超时时,会调用listener的tick方法
TimerListener listener =newTimerListener(){@Overridepublicvoidtick(){//尝试将命令状态从未完成切换为超时if(originalCommand.isCommandTimedOut.compareAndSet(TimedOutStatus.NOT_EXECUTED, TimedOutStatus.TIMED_OUT)){//发布超时事件
originalCommand.eventNotifier.markEvent(HystrixEventType.TIMEOUT, originalCommand.commandKey);// shut down the original request
s.unsubscribe();//调用上面创建的Runnable
timeoutRunnable.run();//if it did not start, then we need to mark a command start for concurrency metrics, and then issue the timeout}}//返回定义的超时时间@OverridepublicintgetIntervalTimeInMilliseconds(){return originalCommand.properties.executionTimeoutInMilliseconds().get();}};//拿到HystrixTimer定时器,添加定时任务final Reference<TimerListener> tl = HystrixTimer.getInstance().addTimerListener(listener);...return parent;}
接下来看看HystrixTimer
publicclassHystrixTimer{privatestatic HystrixTimer INSTANCE =newHystrixTimer();privateHystrixTimer(){// private to prevent public instantiation}//getInstance会返回一个静态实例publicstatic HystrixTimer getInstance(){return INSTANCE;}public Reference<TimerListener>addTimerListener(final TimerListener listener){startThreadIfNeeded();// add the listener//创建Runnable,内部调用传进来的listener.tick()方法,即超时时的操作
Runnable r =newRunnable(){@Overridepublicvoidrun(){try{
listener.tick();}catch(Exception e){
logger.error("Failed while ticking TimerListener", e);}}};//加入定时任务,这样当任务执行超过期限后,就会调用前面创建的runnable.run(),完成超时的控制
ScheduledFuture<?> f = executor.get().getThreadPool().scheduleAtFixedRate(r, listener.getIntervalTimeInMilliseconds(), listener.getIntervalTimeInMilliseconds(), TimeUnit.MILLISECONDS);returnnewTimerReference(listener, f);}}