Hystrix源码解析流程(二):Hystrix熔断过程分析

本文深入剖析Hystrix命令执行的缓存机制,包括HystrixCachedObservable和HystrixCommandResponseFromCache的角色。通过ReplaySubject实现结果重放,确保命令执行结果被正确缓存。当请求缓存启用时,优先尝试从缓存获取响应,否则订阅执行命令的Observable。同时分析了不同隔离策略下,缓存如何配合执行逻辑。
摘要由CSDN通过智能技术生成

类分析图:

测试用例:

@RestController
public class DeptController {

	@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
	@HystrixCommand(fallbackMethod = "processHystrix_Get")
	public Dept get(@PathVariable("id") Long id) {

		System.out.println("请求的ID为:"+id);
		throw new RuntimeException("该ID:" + id + "没有没有对应的信息");

	}

	public Dept processHystrix_Get(@PathVariable("id") Long id) {
		return new Dept().setDeptno(id).setDname("该ID:" + id + "没有没有对应的信息,null--@HystrixCommand")
				.setDb_source("no this database in MySQL");
	}
	
}
接上一篇博客:继续执行
result = CommandExecutor.execute(invokable, executionType, metaHolder);
public static Object execute(HystrixInvokable invokable, ExecutionType executionType, MetaHolder metaHolder) throws RuntimeException {
    Validate.notNull(invokable);
    Validate.notNull(metaHolder);
     //从上边的请求可以知道executionType为 SYNCHRONOUS异步请求
    switch (executionType) {
        case SYNCHRONOUS: {
            return castToExecutable(invokable, executionType).execute();
        }
        case ASYNCHRONOUS: {
            HystrixExecutable executable = castToExecutable(invokable, executionType);
            if (metaHolder.hasFallbackMethodCommand()
                    && ExecutionType.ASYNCHRONOUS == metaHolder.getFallbackExecutionType()) {
                return new FutureDecorator(executable.queue());
            }
            return executable.queue();
        }
        case OBSERVABLE: {
            HystrixObservable observable = castToObservable(invokable);
            return ObservableExecutionMode.EAGER == metaHolder.getObservableExecutionMode() ? observable.observe() : observable.toObservable();
        }
        default:
            throw new RuntimeException("unsupported execution type: " + executionType);
    }
}

castToExecutable(invokable, executionType).execute();

private static HystrixExecutable castToExecutable(HystrixInvokable invokable, ExecutionType executionType) {
    if (invokable instanceof HystrixExecutable) {
        return (HystrixExecutable) invokable;//通过上一篇博客我们知道invokable(GenericCommand.class)
    }
}
execute()方法拓展以及介绍:
方法 
#execute()同步调用,返回直接结果,; 
#queue()异步调用,返回 java.util.concurrent.Future 
#observe()异步调用,返回 rx.Observable 。向 Observable 注册 rx.Subscriber 处理结果 
#toObservable()未调用,返回 rx.Observable 。向 Observable 注册 rx.Subscriber 处理结果

execute()直接调用返回结果。内部执行方法queue().get();。下面部分代码将省略

public R execute() {
    try {
        return queue().get();
    } catch (Exception e) {
        throw Exceptions.sneakyThrow(decomposeException(e));
    }
}
 public Future<R> queue() {
        /*
         *
         */
        final Future<R> delegate = toObservable().toBlocking().toFuture();
    	
        final Future<R> f = new Future<R>() {
            @Override
            public boolean isDone() {
                return delegate.isDone();
			}

            @Override
            public R get() throws InterruptedException, ExecutionException {
                return delegate.get();
            }

        /* special handling of error states that throw immediately */
        if (f.isDone()) {
            try {
                f.get();
                return f;
            } catch (Exception e) {
                
            }
        }

        return f;
    }

toObservable().toBlocking().toFuture():重点分析

引用自http://www.iocoder.cn/Hystrix/command-execute-result-cache/    芋道源码

 1:public Observable<R> toObservable() {
 2:     final AbstractCommand<R> _cmd = this;
 3: 
 4:     //doOnCompleted handler already did all of the SUCCESS work
 5:     //doOnError handler already did all of the FAILURE/TIMEOUT/REJECTION/BAD_REQUEST work
 6:     final Action0 terminateCommandCleanup = new Action0() {} // ... 省略
 7: 
 8:     //mark the command as CANCELLED and store the latency (in addition to standard cleanup)
 9:     final Action0 unsubscribeCommandCleanup = new Action0() {} // ... 省略
10: 
11:     final Func0<Observable<R>> applyHystrixSemantics = new Func0<Observable<R>>() {
12:         @Override
13:         public Observable<R> call() {
14:             if (commandState.get().equals(CommandState.UNSUBSCRIBED)) {
15:                 return Observable.never();
16:             }
17:             return applyHystrixSemantics(_cmd);//详见下一节:applyHystrixSemantics分析
18:         }
19:     };
20: 
21:     final Func1<R, R> wrapWithAllOnNextHooks = new Func1<R, R>() {} // ... 省略 
22: 
23:     final Action0 fireOnCompletedHook = new Action0() {} // ... 省略 
24: 
25:     return Observable.defer(new Func0<Observable<R>>() {
26:         @Override
27:         public Observable<R> call() {
28:            // 每次调用execute方法都需要创建一个新的Command实例,因为每个实例都有自己的状态
29:             if (!commandState.compareAndSet(CommandState.NOT_STARTED, CommandState.OBSERVABLE_CHAIN_CREATED)) {
32:                 throw new HystrixRuntimeException(FailureType.BAD_REQUEST_EXCEPTION, _cmd.getClass(), getLogMessagePrefix() + " command executed multiple times - this is not permitted.", ex, null);
33:             }
35:             // 命令开始时间戳
36:             commandStartTimestamp = System.currentTimeMillis();
38:             //【打印日志】
39:             if (properties.requestLogEnabled().get()) {
40:                 // log this command execution regardless of what happened
41:                 if (currentRequestLog != null) {
42:                     currentRequestLog.addExecutedCommand(_cmd);
43:                 }
44:             }
46:             // 缓存开关、缓存KEY
47:             final boolean requestCacheEnabled = isRequestCachingEnabled();
48:             final String cacheKey = getCacheKey();
49: 
50:             // 优先从缓存中获取
51:             /* try from cache first */
52:             if (requestCacheEnabled) {
53:                 HystrixCommandResponseFromCache<R> fromCache = (HystrixCommandResponseFromCache<R>) requestCache.get(cacheKey);
54:                 if (fromCache != null) {
55:                     isResponseFromCache = true; // 标记 从缓存中结果
56:                     return handleRequestCacheHitAndEmitValues(fromCache, _cmd);
57:                 }
58:             }
60:             // 获得 执行命令Observable
61:             Observable<R> hystrixObservable =
62:                     Observable.defer(applyHystrixSemantics)
63:                             .map(wrapWithAllOnNextHooks);
65:             // 获得 缓存Observable
66:             Observable<R> afterCache;
67:             // 当缓存特性开启,并且缓存未命中时,创建【订阅了执行命令的 Observable】的 HystrixCommandResponseFromCache 。put in cache

68:                if (requestCacheEnabled && cacheKey != null) {
69:                //创建 HystrixCommandResponseFromCache ,并添加到 requestCache
70:                 HystrixCachedObservable<R> toCache = HystrixCachedObservable.from(hystrixObservable, _cmd);//详见提示一
71:                 // 哟,HystrixRequestCache#putIfAbsent(...) 方法,多个线程添加时,只有一个线程添加成功。putIfAbsent   如果传入key对应的value已经存在,就返回存在的value,不进行替换。如果不存在,就添加key和value,返回null
72:                 HystrixCommandResponseFromCache<R> fromCache = (HystrixCommandResponseFromCache<R>) requestCache.putIfAbsent(cacheKey, toCache);
73:                 if (fromCache != null) {
74:                     //说明有其他线程在此之前执行了上面的操作
75:                     toCache.unsubscribe();
76:                     isResponseFromCache = true; // 标记 从缓存中结果
77:                     return handleRequestCacheHitAndEmitValues(fromCache, _cmd);
78:                 } else { // 添加成功

                  //这个返回的Observale是HystrixCachedObservable.from执行后封装过的

80:                     afterCache = toCache.toObservable();
81:                 }
82:             } else {
83:                 afterCache = hystrixObservable;
84:             }           
87:             return afterCache
88:                     .doOnTerminate(terminateCommandCleanup)     // perform cleanup once (either on normal terminal state (this line), or unsubscribe (next line))
89:                     .doOnUnsubscribe(unsubscribeCommandCleanup) // perform cleanup once
90:                     .doOnCompleted(fireOnCompletedHook);
91:         }
92:     });
93: }

提示一:对于开启缓存的会Observable会被ReplaySubject订阅。applyHystrixSemantics会直接执行。

HystrixCachedObservable


1: public class HystrixCachedObservable<R> {
2: /**
3:  * 订阅
4:  */
5: protected final Subscription originalSubscription;
6: /**
7:  * 缓存 cachedObservable
8:  */
9: protected final Observable<R> cachedObservable;

13: private volatile int outstandingSubscriptions = 0;
14: //private AtomicInteger outstandingSubscriptions2 = new AtomicInteger(0);
15:
16: protected HystrixCachedObservable(final Observable<R> originalObservable) {
17:     ReplaySubject<R> replaySubject = ReplaySubject.create();
18:     this.originalSubscription = originalObservable
19:             .subscribe(replaySubject);
20:
21:     this.cachedObservable = replaySubject
22:             .doOnUnsubscribe(new Action0() {
23:                 @Override
24:                 public void call() {
25:                     outstandingSubscriptions--;
26:                     if (outstandingSubscriptions == 0) {
27:                         originalSubscription.unsubscribe();
28:                     }
29:                 }
30:             })
31:             .doOnSubscribe(new Action0() {
32:                 @Override
33:                 public void call() {
34:                     outstandingSubscriptions++;
35:                 }
36:             });
37: }

第 17 至 19 行 :实际上,HystrixCachedObservable 不是一个 Observable 的子类,而是对传入的 Observable 封装 :使用 ReplaySubject 向传入的 Observable 发起订阅,通过 ReplaySubject 能够重放执行结果,从而实现缓存的功效。这里有几个卡到笔者的并且很有趣的点,我们一一道来 :从上文中,我们可以看到,传入的 originalObservable 为 hystrixObservable 执行命令 Observable 。在 Hystrix 里,提供了两种执行命令的隔离方式 :线程池( THREAD ) 和信号量( SEMAPHORE )。

  • 当使用 THREAD 隔离时,#subscribe(replaySubject) 调用完成时,实际命令并未开始执行(因为采用线程池的缘故,会将后续的执行逻辑封装为一个任务放到Rxjava中的Schedule中去异步执行),或者说,这是一个异步的执行命令的过程。那么,会不会影响返回执行结果呢?答案当然是不会,BlockingObservable 在得到执行完成才会结束阻塞,此时已经有执行结果。
  • 当使用 SEMAPHORE 隔离时,#subscribe(replaySubject) 调用完成时,实际命令已经执行完成(如果采用信号量则由当前线程来执行),所以即使 AbstractCommand#toObservavle(...) 的第 75 行 :调用 HystrixCommandResponseFromCache#unsubscribe() 方法,也会浪费,重复执行命令。而对于 THREAD 隔离的情况,通过取消订阅的方式,只会执行一次命令。当然,如果“恶搞” THREAD 隔离的情况,增加 sleep 的调用如下,就能达到重复执行命令的效果。

HystrixCommandResponseFromCache

com.netflix.hystrix.HystrixCommandResponseFromCache ,是 HystrixCachedObservable 的子类。在父类的基础上,增加了对 AbstractCommand.executionResult 的关注。

HystrixCachedObservable#from(Observable, AbstractCommand) 方法,创建 HystrixCommandResponseFromCache 对象,点击 链接 查看。


HystrixCommandResponseFromCache#toObservableWithStateCopiedInto(...) 方法,点击 链接 查看。

  • 通过 completionLogicRun 属性,保证 #doOnError() ,#doOnCompleted() ,#doOnUnsubscribe() 方法有且只有一个方法执行具体逻辑。
    • #doOnError() ,#doOnCompleted() 执行时,调用 #commandCompleted() 方法,从缓存命令( HystrixCommandResponseFromCache.originalCommand ) 复制 executionResult 属性给当前命令( commandToCopyStateInto ) 。
    • #doOnUnsubscribe() 执行时,调用 #commandUnsubscribed() 方法,使用当前命令( commandToCopyStateInto )自己的 executionResult ,不进行复制。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值