前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[享学Netflix] 三十、Hystrix的fallback回退/降级逻辑源码解读

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

作者头像
YourBatman
发布2020-03-18 19:42:26
2K0
发布2020-03-18 19:42:26
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦

总有一个人要赢,为什么不是我呢? 代码下载地址: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回退呢?

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 前言
  • 正文
    • getFallbackOrThrowException() 源码解读
      • fallback函数执行成功or执行失败的处理
    • getFallbackOrThrowException回退步骤文字总结
    • 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档