Spring Cloud 源码学习之 Hystrix 入门
欢迎访问陈同学博客原文
Hystrix 功能非常多,本文仅对 Hystrix 源码做入门学习。为便于阅读,文中源码有较大删减,仅保留入门学习必要的源码,降低其他逻辑的干扰。
从 Hystrix 名字说起
Spring Cloud 众多组件,了解其名字背后的寓意也是一种乐趣。
下面是我拼的一张图,分别为:Hystrix、豪猪、刺猬。
Hystrix 译为 "豪猪",豪猪以棘刺闻名,集肉用、药用、欣赏价值于一体。刺猬的小短刺和豪猪长矛比起来,根本不在同一个level。超市中70块一斤的猪肉指不定就是豪猪,当然,也可能是丁磊家的黑猪。
豪猪的棘刺能保护自己不受天敌伤害,代表了强大的防御能力。Netflix 将该组件取名为 Hystrix,宣言为 "defend your app",寓意应该是:当系统受到伤害时,能够像豪猪的棘刺一样保护系统。
Spring Cloud Hystrix 基于 Netflix Hystrix 实现,具备服务降级、服务熔断、线程与信号隔离、请求缓存、请求合并以及服务监控等强大功能。
入门学习素材
本文使用下面的样例代码来做源码学习。
ServiceA 中 hello() 方法由 @HystrixCommand 注解标记,调用 ServiceB 的 hello() 接口。若调用失败,则执行 error() 方法。
@HystrixCommand(fallbackMethod = "error") public String hello() { return restTemplate.getForEntity("http://serviceB/hello", String.class).getBody(); } public String error() { return "error"; }
ServiceB hello() 抛出异常,以便 ServiceA执行 error() 方法。
@GetMapping("/hello") public String hello() { throw new RuntimeException("error occurred"); }
样例代码表示的就是 服务降级,服务降级换些名词来描述就是:B计划、应急预案、备用方案、替补,以便在出现问题时,"预备队"可以立马顶上。
有时,技术名词晦涩难懂,但经验与智慧都来自于现实世界。
代码执行入口
Spring 中也有一种类似 Java SPI 的加载机制,允许在 META-INF/spring.factories 文件中配置接口实现类,Spring 会自动处理。开发人员仅需引入 jar 包,就能达到插拔式效果,十分方便。
引入 spring-cloud-starter-hystrix 依赖,spring-cloud-netflix-core 的 jar 包中包含 spring.factories 文件,其中有 Hytrix 和 其他组件相关配置。
在 HystrixCircuitBreakerConfiguration 中,注入了 HystrixCommandAspect。
@Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); }
HystrixCommandAspect 用于处理被注解 @HystrixCommand 标记的方法。通过名字和下面代码可以知道,Hystrix 基于 AOP 机制实现,对目标方法做了代理,然后实现了自己一系列功能特性。
@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 来执行。
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() 的代码。
public HystrixInvokable create(MetaHolder metaHolder) { return new GenericCommand(...create(metaHolder)); }
实现类是 GenericCommand,我们看看类图。
三个抽象父类 AbstractHystrixCommand、HystrixCommand、AbstractCommand 帮助 GenericCommand 做了不少公共的事情,而 GenericCommand 负责执行具体的方法和fallback时的方法。
// 执行具体的方法,如: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 的整个执行过程。
Object result = CommandExecutor.execute(invokable, executionType, metaHolder);
CommandExecutor.execute() 首先将 invokable 转换为 HystrixExecutable,再执行 HystrixExecutable 的execute() 方法。
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() 实现,代码如下:
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反射机制来执行。
上图右边部分标记出来的就是 RxJava 中的部分调用链,下面的截图简单展示下最后的调用。
OnSubscribeDefer.call() -> HystrixCommand.getExecutionObservable() -> GenericCommand.run()。
小结
本文只是一个简单小例子,没有涉及到 Hystrix 的其他特性,后面将接着学习。另,Hystrix 的官方 Wiki 是非常好的学习材料。
欢迎关注陈同学的公众号,一起学习,一起成长
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Kotlin可以拯救Java程序员,但Java9程序员不用!
不知不觉Java8已经三年了,团队转Kotlin也已经小一年。犹记得Java8推出Stream API时候的兴奋——真是Java标准库最好的改进之一。当然,还有Optional。事情发展往往就是这样:你痛骂它,却不得不在实践中不断的使用它——因为它真的很完善,即使重了一些。这就是Java。我用了13年,老司机。 现在Java9经过诸多的坎坷终于发布,对于老Java程序员,颇激动,第一时间就要和大家分享这种激动,迅速下载JDK,做了第一个尝试,我想在这个尝试之前,大家心里都有这个痛 jshell>"abc".matches("a[bc]{2}")$3==>true jshell> IntStream.iterate(1, i -> i <10, i -> i +1).forEach(System.out::println) jshell> Java程序员们,再也不用羡慕了。很酷,大家一起开始玩耍吧! JShell,Java9 Shell 大家在学习某一门语言的时候,有一个方便的交互形式,真的很有用。Java大家都是知道的: public stat...
- 下一篇
PYTHON系列-从零开始的爬虫入门指南
入门0.准备工作 需要准备的东西: Python、scrapy、一个IDE或者随便什么文本编辑工具。 1.技术部已经研究决定了,你来写爬虫。 随便建一个工作目录,然后用命令行建立一个工程,工程名为miao,可以替换为你喜欢的名字。 1scrapy startproject miao 随后你会得到如下的一个由scrapy创建的目录结构 从零开始的爬虫入门指南在spiders文件夹中创建一个python文件,比如miao.py,来作为爬虫的脚本。 内容如下: 123456789101112131415import scrapy class NgaSpider(scrapy.Spider): name = "NgaSpider" host = "http://bbs.ngacn.cc/" # start_urls是我们准备爬的初始页 start_urls = [ "http://bbs.ngacn.cc/thread.php?fid=406", ] # 这个是解析函数,如果不特别指明的话,scrapy抓回来的页面会由这个函数进行解析。 # 对页面的处理和分析工作都在此进行,这个示例里我们只是...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL8.0.19开启GTID主从同步CentOS8
- Linux系统CentOS6、CentOS7手动修改IP地址
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合Redis,开启缓存,提高访问速度
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作