一、Hystrix源码总结
Hystrix在底层使用了Spring提供的切面技术:
- 通过HystrixCommandAspect.java定义了一个切面(该类有@Aspect注解),专门用来处理那些标注了@HystrixCommand的方法
/** * AspectJ aspect to process methods which annotated with {@link HystrixCommand} annotation. */ @Aspect public class HystrixCommandAspect {...}
- 更详细的讲,该类中定义了一个切点。切点是@HystrixCommand。
@Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)") public void hystrixCommandAnnotationPointcut() {}
- 而针对该切点,还设置一个环绕函数
@Around("hystrixCommandAnnotationPointcut()") public Object methodsAnnotatedWithHystrixCommand()
- 环绕函数中,最关键的一句是execute()。这里会根据executionType有SYNCHRONOUS,ASYNCHRONOUS,OBSERVABLE调用不同逻辑,使用rxjava的观察者模式并最终在这里计算出结果返回。
result = CommandExecutor.execute(invokable, executionType, metaHolder);
- 当调用execute()出现失败时,会调用getFallBack()方法。而如果没有设置降级方法则调用父类的getFallBack()方法,父类的getFallBack方法会抛出一个找不到降级方法的异常
protected T getFallback() { throw new RuntimeException("No fallback available.", getExecutionException()); }
二、详细解析
Hystrix在底层使用了Spring提供的切面技术。在HystrixCommandAspect.java文件中可以看到定义的切面:
/** * AspectJ aspect to process methods which annotated with {@link HystrixCommand} annotation. */ @Aspect public class HystrixCommandAspect { private static final Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP; static { META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder() .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory()) .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory()) .build(); } @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)") public void hystrixCommandAnnotationPointcut() { } @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)") public void hystrixCollapserAnnotationPointcut() { } @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()") public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable { //通过切点获取被拦截的方法 Method method = getMethodFromTarget(joinPoint); Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint); if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) { throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " + "annotations at the same time"); } MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method)); //metaholder中保存了很多和切点相关的信息,详见后文的贴图 MetaHolder metaHolder = metaHolderFactory.create(joinPoint); HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder); ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ? metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType(); //result中保留操作的结果,可能是成功操作的返回值也可能是fallback方法的返回值 Object result; try { //如果返回的结果使用了observable模式则执行以下代码 if (!metaHolder.isObservable()) { result = CommandExecutor.execute(invokable, executionType, metaHolder); } else { //否则执行else result = executeObservable(invokable, executionType, metaHolder); } } catch (HystrixBadRequestException e) { throw e.getCause() != null ? e.getCause() : e; } catch (HystrixRuntimeException e) { throw hystrixRuntimeExceptionToThrowable(metaHolder, e); } return result; } .......... }
可以看到在这个类中首先是定义了两个切面,由于我们这里讨论的是HystrixCommand,所以只关注第一个切面,在这个切面里可以看到切点是被HystrixCommand注解的方法,然后对方法的具体增强是在接下来的一个名为methodsAnnotatedWithHystrixCommand()的方法里,具体的流程用注释写在了代码中。
metaHolder的内容:
可以看到在以上代码中,最重要的便是获取到result具体结果的那一步,由于我们的例子不是使用,所以重点放在:
result = CommandExecutor.execute(invokable, executionType, metaHolder);
接下来跳转到CommandExecutor.java中的execute()方法:
/** * Calls a method of {@link HystrixExecutable} in accordance with specified execution type. * * @param invokable {@link HystrixInvokable} * @param metaHolder {@link MetaHolder} * @return the result of invocation of specific method. * @throws RuntimeException */ public static Object execute(HystrixInvokable invokable, ExecutionType executionType, MetaHolder metaHolder) throws RuntimeException { Validate.notNull(invokable); Validate.notNull(metaHolder); 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); } }
在execute()方法中使用了rxjava的观察者模式并最终在这里计算出结果返回。当出现失败是,会调用GenericCommand.java中的getFallBack()方法,代码如下:
/** * The fallback is performed whenever a command execution fails. * Also a fallback method will be invoked within separate command in the case if fallback method was annotated with * HystrixCommand annotation, otherwise current implementation throws RuntimeException and leaves the caller to deal with it * (see {@link super#getFallback()}). * The getFallback() is always processed synchronously. * Since getFallback() can throw only runtime exceptions thus any exceptions are thrown within getFallback() method * are wrapped in {@link FallbackInvocationException}. * A caller gets {@link com.netflix.hystrix.exception.HystrixRuntimeException} * and should call getCause to get original exception that was thrown in getFallback(). * * @return result of invocation of fallback method or RuntimeException */ @Override protected Object getFallback() { final CommandAction commandAction = getFallbackAction(); if (commandAction != null) { try { return process(new Action() { @Override Object execute() { MetaHolder metaHolder = commandAction.getMetaHolder(); Object[] args = createArgsForFallback(metaHolder, getExecutionException()); return commandAction.executeWithArgs(metaHolder.getFallbackExecutionType(), args); } }); } catch (Throwable e) { LOGGER.error(FallbackErrorMessageBuilder.create() .append(commandAction, e).build()); throw new FallbackInvocationException(unwrapCause(e)); } } else { return super.getFallback(); } }
在这个方法中首先会对是否设置了commandAction进行判断,commandAction就是我们之前所设置的fallback字段指向的方法,如果发现有降级方法,则调用降级方法获取结果,进入process()方法,代码如下:
/** * Executes an action. If an action has failed and an exception is ignorable then propagate it as HystrixBadRequestException * otherwise propagate original exception to trigger fallback method. * Note: If an exception occurred in a command directly extends {@link java.lang.Throwable} then this exception cannot be re-thrown * as original exception because HystrixCommand.run() allows throw subclasses of {@link java.lang.Exception}. * Thus we need to wrap cause in RuntimeException, anyway in this case the fallback logic will be triggered. * * @param action the action * @return result of command action execution */ Object process(Action action) throws Exception { Object result; try { result = action.execute(); flushCache(); } catch (CommandActionExecutionException throwable) { Throwable cause = throwable.getCause(); if (isIgnorable(cause)) { throw new HystrixBadRequestException(cause.getMessage(), cause); } if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Exception) { throw (Exception) cause; } else { // instance of Throwable throw new CommandActionExecutionException(cause); } } return result; }
最终在process()方法中获取降级方法执行的结果,而如果没有设置降级方法则调用父类的getFallBack()方法,父类的getFallBack方法会抛出一个找不到降级方法的异常,代码如下:
protected T getFallback() { throw new RuntimeException("No fallback available.", getExecutionException()); }
至于何时会跳转到降级方法,则是在AbstractCommand.java中,在这里定义了很多种执行失败的情况,通过rxjava框架的观察者模式对错误进行监听,根据不同的情况会进入不同的处理方法,最终这些处理方法都会调用HystrixCommand.java中的getFallbackObservable()方法,并最终进入上文所述的真正执行fallback方法的代码。
以上就是对Hystrix降级机制的说明,想要深入理解Hystrix,必须要对rxjava有比较深刻的认识,在Hystrix中大量运用了rxjava。
参考文献
版权声明:本文为CSDN博主「栗子栗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/turbo_zone/article/details/83831992