陈同学
微服务
Accelerator
About
# Spring Cloud 源码学习之 Hystrix 入门 > Hystrix 功能非常多,本文仅对 Hystrix 源码做入门学习。为便于阅读,文中源码有较大删减,仅保留入门学习必要的源码,降低其他逻辑的干扰。 ## 从 Hystrix 名字说起 Spring Cloud 众多组件,了解其名字背后的寓意也是一种乐趣。 下面是我拼的一张图,分别为:**Hystrix、豪猪、刺猬**。 ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/09/12/fc4f8fa68b774503af17b96f37a26534.png) **Hystrix** 译为 "豪猪",豪猪以棘刺闻名,集肉用、药用、欣赏价值于一体。刺猬的小短刺和豪猪长矛比起来,根本不在同一个level。超市中70块一斤的猪肉指不定就是豪猪,当然,也可能是丁磊家的黑猪。 豪猪的棘刺能保护自己不受天敌伤害,代表了强大的防御能力。Netflix 将该组件取名为 Hystrix,宣言为 "defend your app",寓意应该是:**当系统受到伤害时,能够像豪猪的棘刺一样保护系统**。 Spring Cloud Hystrix 基于 Netflix Hystrix 实现,具备服务降级、服务熔断、线程与信号隔离、请求缓存、请求合并以及服务监控等强大功能。 ## 入门学习素材 本文使用下面的样例代码来做源码学习。 ServiceA 中 *hello()* 方法由 *@HystrixCommand* 注解标记,调用 ServiceB 的 *hello()* 接口。若调用失败,则执行 *error()* 方法。 ```java @HystrixCommand(fallbackMethod = "error") public String hello() { return restTemplate.getForEntity("http://serviceB/hello", String.class).getBody(); } public String error() { return "error"; } ``` ServiceB **hello()** 抛出异常,以便 ServiceA执行 *error()* 方法。 ```java @GetMapping("/hello") public String hello() { throw new RuntimeException("error occurred"); } ``` 样例代码表示的就是 **服务降级**,服务降级换些名词来描述就是:B计划、应急预案、备用方案、替补,以便在出现问题时,"预备队"可以立马顶上。 有时,技术名词晦涩难懂,但经验与智慧都来自于现实世界。 ## 代码执行入口 Spring 中也有一种类似 [Java SPI](https://chenyongjun.vip/articles/68) 的加载机制,允许在 *META-INF/spring.factories* 文件中配置接口实现类,Spring 会自动处理。开发人员仅需引入 jar 包,就能达到插拔式效果,十分方便。 引入 **spring-cloud-starter-hystrix** 依赖,**spring-cloud-netflix-core** 的 jar 包中包含 spring.factories 文件,其中有 Hytrix 和 其他组件相关配置。 ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/09/12/4f53bd780b4148d395d34d9b960b57b2.png) 在 **HystrixCircuitBreakerConfiguration** 中,注入了 **HystrixCommandAspect**。 ```java @Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); } ``` **HystrixCommandAspect** 用于处理被注解 *@HystrixCommand* 标记的方法。通过名字和下面代码可以知道,Hystrix 基于 AOP 机制实现,对目标方法做了代理,然后实现了自己一系列功能特性。 ```java @Aspect public class HystrixCommandAspect { @Pointcut("@annotation(...annotation.HystrixCommand)") public void hystrixCommandAnnotationPointcut() { } @Around("hystrixCommandAnnotationPointcut()") public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable { } } ``` 处理逻辑就在 *methodsAnnotatedWithHystrixCommand()* 中。 ## 处理逻辑 *methodsAnnotatedWithHystrixCommand()* 用来执行目标方法,Hystrix 将需要执行的Method(如ServiceA的*hello()* ) 最终封装成了 **HystrixInvokable** 来执行。 ```java public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable { // 被@HystrixCommand标记的hello()方法 Method method = getMethodFromTarget(joinPoint); MetaHolderFactory metaHolderFactory = ...get(HystrixPointcutType.of(method)); MetaHolder metaHolder = metaHolderFactory.create(joinPoint); // 准备各种材料后,创建HystrixInvokable HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder); Object result; try { if (!metaHolder.isObservable()) { // 利用工具CommandExecutor来执行 result = CommandExecutor.execute(invokable, executionType, metaHolder); } } return result; } ``` HystrixInvokable 只是一个空接口,没有任何方法,只是用来标记具备可执行的能力。 那 **HystrixInvokable** 又是如何创建的?它具体的实现类又是什么?先看看 *HystrixCommandFactory.getInstance().create*() 的代码。 ```java public HystrixInvokable create(MetaHolder metaHolder) { return new GenericCommand(...create(metaHolder)); } ``` 实现类是 **GenericCommand**,我们看看类图。 <img src="https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/09/16/194948103f5a4a0491e7326a70c50c59.png" width="50%"/> 三个抽象父类 **AbstractHystrixCommand、HystrixCommand、AbstractCommand** 帮助 **GenericCommand** 做了不少公共的事情,而 **GenericCommand** 负责执行具体的方法和fallback时的方法。 ```java // 执行具体的方法,如:ServiceA的hello() protected Object run() throws Exception { return process(new Action() { @Override Object execute() { return getCommandAction().execute(getExecutionType()); } }); } // 执行fallback方法,如:ServiceA的error() protected Object getFallback() { final CommandAction commandAction = getFallbackAction(); return process(new Action() { @Override Object execute() { MetaHolder metaHolder = commandAction.getMetaHolder(); Object[] args = createArgsForFallback(...); return commandAction.executeWithArgs(..., args); } }); } ``` ## 目标方法执行细节 执行过程想来应该很简单,即先执行目标方法,失败则执行fallback方法。 再来看看 *methodsAnnotatedWithHystrixCommand()* 的具体执行代码,它完成了 Hystrix 的整个执行过程。 ```java Object result = CommandExecutor.execute(invokable, executionType, metaHolder); ``` *CommandExecutor.execute()* 首先将 invokable 转换为 **HystrixExecutable**,再执行 HystrixExecutable 的*execute()* 方法。 ```java public static Object execute(HystrixInvokable invokable, ExecutionType executionType, MetaHolder metaHolder) throws RuntimeException { switch (executionType) { // 这里仅贴出这一种case case SYNCHRONOUS: { // 转为 HystrixExecutable 并执行 return castToExecutable(invokable, executionType).execute(); } } } ``` HystrixExecutable 的 *execute()* 方法由 HystrixCommand.execute() 实现,代码如下: ```java public R execute() { // 调用下面的queue() return queue().get(); } public Future<R> queue() { final Future<R> delegate = toObservable().toBlocking().toFuture(); final Future<R> f = new Future<R>() { ... }; if (f.isDone()) { try { f.get(); return f; } } return f; } ``` 利用 JUC 的 Future 来异步执行,通过 **f.get()** 来获取 *hello()* 方法的执行结果。Hystrix 结合了 RxJava 来实现异步编程,我做了下调试,看了stackframe,执行过程层层调用,略微恶心。RxJava 有点复杂,同时也需要了解响应式编程模型,这里直接跳过。 ServiceA 的 **hello()** 还是由 GenericCommand 来执行的,如下图,getCommandAction() 这个 CommandAction 指的就是被执行的hello()方法,利用Java反射机制来执行。 ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/09/16/15ed5e7d39104c4cb36e860b59fb961b.png) 上图右边部分标记出来的就是 RxJava 中的部分调用链,下面的截图简单展示下最后的调用。 **OnSubscribeDefer.call() -> HystrixCommand.getExecutionObservable() -> GenericCommand.run()**。 ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/09/16/7895c40f3d4743b58ebe6321582b43c7.jpg) ## 小结 本文只是一个简单小例子,没有涉及到 Hystrix 的其他特性,后面将接着学习。另,[Hystrix 的官方 Wiki](https://github.com/Netflix/Hystrix/wiki) 是非常好的学习材料。
本文由
cyj
创作,可自由转载、引用,但需署名作者且注明文章出处。
文章标题:
Spring Cloud 源码学习之 Hystrix 入门
文章链接:
https://chenyongjun.vip/articles/75
扫码或搜索 cyjrun 关注微信公众号, 结伴学习, 一起努力