[享学Netflix] 三十、Hystrix的fallback回退/降级逻辑源码解读

2020-03-18 19:42:26 浏览数 (3)

总有一个人要赢,为什么不是我呢? 代码下载地址:https://github.com/f641385712/netflix-learning

目录
  • 前言
  • 正文
    • getFallbackOrThrowException() 源码解读
      • fallback函数执行成功or执行失败的处理
    • getFallbackOrThrowException回退步骤文字总结
  • 总结
    • 声明

前言

我们知道Hystrix是一个限流、降级容错框架,它能很好的保护我们的接口、应用。这很大程度上得益于它提供了fallback机制:回退(也叫降级)。

本文所讲述的fallabck机制是xxxCommand系列的fallback实现,它是我们主要的使用方式。由于很多小伙伴并不清楚什么时候会触发fallabck,以及常问的fallabck后到底发生了什么呢?本文就带你一起深入了解Hystrix的fallback回退逻辑源码解读部分,从根本上掌握Hystrix回退处理逻辑。


正文

xxxCommand系列正常的执行逻辑、以及出现异常的回退逻辑均在HystrixCommandHystrixObservableCommand二者的抽象父类AbstractCommand里实现的。而对fallback的所有执行逻辑便在方法getFallbackOrThrowException()里,下面我们来一探究竟。


getFallbackOrThrowException() 源码解读

该方法是AbstractCommand的一个私有方法,语义是:执行fallabck回滚或者抛出异常(一般为HystrixRuntimeException异常类型,当然不是绝对的)。

值得注意的是:若执行到了这一步,Hystrix它能100%保证无论如何都能尽快把你的请求掐断,避免系统被拖死。虽然Hystrix它建议你不要在getFallback()里面写耗时的阻塞的(比如请求网络)逻辑,但是建议归建议,但还是可能有人不这么做怎么办呢?所以fallbackSemaphoreOverride信号量的作用:专门用于fallabck的信号量控制,决不让其堆积请求

需要注意的是:调用此方法必传一个Exception,因为Hystrix认为执行进入到这里肯定是发生了一个异常才行的。

代码语言:javascript复制
AbstractCommand:

	// _cmd:命令对象
	// eventType:事件类型
	// failureType:失败类型枚举(BAD_REQUEST_EXCEPTION...) 见下面枚举定义
	// message:失败的消息。如timed-out、failed、short-circuited等
	// Exception:导致失败的异常(一定只有异常才能导致失败),如java.util.concurrent.TimeoutException
	private Observable<R> getFallbackOrThrowException(
		AbstractCommand<R> _cmd, HystrixEventType eventType, 
		FailureType failureType, String message, 
		Exception originalException) {
	
		// 因为fallabck也是在线程池里面执行,所以也是需要传递数据的
		HystrixRequestContext requestContext = HystrixRequestContext.getContextForCurrentThread();
		// 统计相关事件的次数:详见事件计数器
		executionResult = executionResult.addEvent((int) latency, eventType);
		...
		// 如果异常类型是ExceptionNotWrappedByHystrix类型
		if (shouldNotBeWrapped(originalException)){
			... // 触发executionHook.onError()动作
			return Observable.error(e);
		// 是否是不可恢复的异常类型:比如StackOverflowError/VirtualMachineError...
		// 因为并不是所有的Error都不会恢复,所以这里例举出来。如果是不可恢复的错误,就包装一下抛出
		} else if (isUnrecoverable(originalException)) {
			...
			// 若是不可恢复错误,转未HystrixRuntimeException异常抛出
			return Observable.error(new HystrixRuntimeException(failureType, ... );
		} else {
			// 若是可自己恢复的Error,如IOError,那就输入一句日志即可
			if (isRecoverableError(originalException)) { ... }

			// =======普通异常类型(比如NPE之类的),那就开始执行fallabck函数========
			if (properties.fallbackEnabled().get()) { // 显然默认是开启的,不建议关闭它
				// 若你没指定,默认使用的是TryableSemaphoreActual 10个信号量
				// 你可以通过fallbackIsolationSemaphoreMaxConcurrentRequests指定这个值
				// 该信号量用于杜绝你的fallabck还非常耗时的case,以防万一
				TryableSemaphore fallbackSemaphore = getFallbackSemaphore();

				...
				// 最终return的可观察对象
				// getFallbackObservable是个抽象方法,由子类提供
				// 比如command它就是使用Observable.just(getFallback())把任意对象转换为Observable
				// 当然它是个defer的Observable
				Observable<R> fallbackExecutionChain;
				// 若还有信号量资源就继续执行(否则会会做异常处理,见下面)
				if (fallbackSemaphore.tryAcquire()) { 
					// 用户是否自定义了fallabck函数
					// 也就是是否重写了getFallabck()方法嘛
					if (isFallbackUserDefined()) { 
						... // executionHook.onFallbackStart(this);
						fallbackExecutionChain = getFallbackObservable();
					} else { // 区别是:若没有复写这个方法,就不会触发onFallbackStart()动作
						fallbackExecutionChain = getFallbackObservable();
					}
					// 绑定固定的处理函数
					return fallbackExecutionChain
								// 作用于每个iter身上:setRequestContextIfNeeded(requestContext);
								// 确保上下文是同一个,数据是通的
			                    .doOnEach(setRequestContext)
			                    // 主要是为了触发executionHook相关回调
			                    .lift(new FallbackHookApplication(_cmd))
			                    .lift(new DeprecatedOnFallbackHookApplication(_cmd))
			                    // 发布事件:HystrixEventType.FALLBACK_EMIT
			                    // executionResult.addEvent(HystrixEventType.FALLBACK_EMIT)
			                    .doOnNext(markFallbackEmit)
			                    // 完成了。发布事件:HystrixEventType.FALLBACK_SUCCESS
			                    // 证明fallabck成功
			                    .doOnCompleted(markFallbackCompleted)
			                    // fallabck失败(出现异常了),发布事件
			                    // HystrixEventType.FALLBACK_MISSING、FALLBACK_FAILURE
			                    .onErrorResumeNext(handleFallbackError)
			                    // 释放信号量:fallbackSemaphore.release();
			                    .doOnTerminate(singleSemaphoreRelease)
			                    // fallbackSemaphore.release();
			                    .doOnUnsubscribe(singleSemaphoreRelease);
				} else { // 此else对应逻辑:信号量不够的情况
					return handleFallbackRejectionByEmittingError();
				}
			} else { // 此else对应逻辑:properties禁用了fallabck的情况
				return handleFallbackDisabledByEmittingError(originalException, failureType, message);
			}
		}
	}


// 定义失败枚举:一个7种类型
HystrixRuntimeException:
    public static enum FailureType {
        BAD_REQUEST_EXCEPTION, COMMAND_EXCEPTION, TIMEOUT, SHORTCIRCUIT, REJECTED_THREAD_EXECUTION, REJECTED_SEMAPHORE_EXECUTION, REJECTED_SEMAPHORE_FALLBACK
    }

fallback函数执行成功or执行失败的处理

针对以上fallbackExecutionChain被观察对象的执行,还需注意关心它的执行成功or失败,需要做对应的处理:

  • .doOnNext(markFallbackEmit):没发射一个数据(执行fallabck方法之前),给事件计数executionResult.addEvent(HystrixEventType.FALLBACK_EMIT)
  • doOnCompleted(markFallbackCompleted)正常执行完成(fallabck未抛出异常)时,记录结果事件executionResult.addEvent((int) latency, HystrixEventType.FALLBACK_SUCCESS)
  • .onErrorResumeNext(handleFallbackError):若执行fallabck时候发生错误(重点),就执行如下逻辑:
代码语言:javascript复制
	// 若异常类型是该类型(比如你使用HystrixCommand,但没重写getFallabck()方法,执行就抛出此异常)
	// 就包装为HystrixRuntimeException给你抛出
	// 并且有你熟悉的抛错消息: message   " and no fallback available."
	if (fe instanceof UnsupportedOperationException) {
		...
		// 记录结果:事件类型是FALLBACK_MISSING
		executionResult.addEvent((int) latency, HystrixEventType.FALLBACK_MISSING);
		return Observable.error(new HystrixRuntimeException(failureType, ...);
	} else { // 你的fallabck方法里抛出了其它异常
		...
		// 记录事件,此时事件类型是Fallabck执行失败FALLBACK_FAILURE
		executionResult.addEvent((int) latency, HystrixEventType.FALLBACK_FAILURE);
		return Observable.error(new HystrixRuntimeException(failureType ...  message   " and fallback failed." ...);
	}

对于fallabck执行失败的case,请注意错误消息and no fallback available.and fallback failed.的区别。前者是木有提供fallback函数,后者是提供了但是执行时抛错了(fallback函数里都出错了也是人才)。

另外,源码处有两个else逻辑此处也给个简要描述:

  • 如果properties.fallbackEnabled() = false显示禁用了fallabck功能,最终会触发handleFallbackDisabledByEmittingError()方法(此为唯一调用处):
代码语言:javascript复制
AbstractCommand:

	private Observable<R> handleFallbackDisabledByEmittingError(Exception underlying, FailureType failureType, String message) {
		... // 触发executionHook.onError动作
		// 转换为一个HystrixRuntimeException抛出
		return Observable.error(new HystrixRuntimeException(failureType, ... " and fallback disabled." ... );
	}
  • 如果执行fallabck时请求信号量资源不够用了,那么执行handleFallbackRejectionByEmittingError()方法(此处为唯一调用处):
代码语言:javascript复制
AbstractCommand:

	private Observable<R> handleFallbackRejectionByEmittingError() {
		...
		// 这里记录了结果哦。统计对应事件的次数(毕竟出现此case的数据还是蛮有意义的)
		executionResult = executionResult.addEvent((int) latencyWithFallback, HystrixEventType.FALLBACK_REJECTION);
		return Observable.error(new HystrixRuntimeException(FailureType.REJECTED_SEMAPHORE_FALLBACK,... " fallback execution rejected." ...);
    }

总的来说,出现以上两种else情况,最终的效果均是抛出一个HystrixRuntimeException异常。这是Hystrixfallback处理的全部逻辑,那么,针对其正常的回退步骤,下面用一个文字版步骤总结。


getFallbackOrThrowException回退步骤文字总结

首先需要明确:执行此fallabck步骤肯定是发生了Exception异常的(当然有可能是Error错误),所以异常类型很关键,此处用e来表示源生异常类型(如目标方法自己产生的NPE)。

  1. e不需要被包装,那就不用使用HystrixRuntimeException去包它了,直接返回:Observable.error(e);
    1. ExceptionNotWrappedByHystrix是个标记接口:若你的异常类型实现了此接口,那么抛出此类型的异常将不会再被 HystrixRuntimeException包起来了
  2. e是不可恢复的异常类型如:StackOverflowError/VirtualMachineError/ThreadDeath/LinkageError,那就直接包装为HystrixRuntimeException类型抛出。
  3. 到这一步,e的类型“过关”了,着手执行fallabck逻辑。若禁用了fallabck,就执行handleFallbackDisabledByEmittingError()方法 -> 抛出HystrixRuntimeException异常,否则继续。
  4. 准备执行fallabck函数,先请求fallabck专用的信号量。若无资源了,那就执行handleFallbackRejectionByEmittingError()犯法 -> 抛出HystrixRuntimeException异常,否则继续。否则继续
  5. 通过抽象方法getFallbackObservable()拿到被观察对象Observable<R>,然后便可开始执行目标fallabck函数了。其中执行目标fallback函数时分为成功or失败。
    1. 成功: 执行doOnCompleted放,整成记录FALLBACK_SUCCESS事件到结果即可
    2. 失败:分为未提供fallabck函数和fallback函数内部抛出了异常两种case -> 均抛出HystrixRuntimeException异常,对应异常消息是著名的:and no fallback available.and fallback failed.

总结

关于Hystrix的fallback回退逻辑源码解读就介绍到这了,本文主要介绍了AbstractCommand#getFallbackOrThrowException的执行逻辑以及源码分析,相信你已经对Hystrix是如何调用目标fallback函数以及执行目标fallback函数时若发生异常时的处理有了一定的认识,但是你或许还会关注一个重点:什么时候会触发fallabck回退呢?

0 人点赞