欢迎访问陈同学博客原文
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);// 准备各种材料后,创建HystrixInvokableHystrixInvokable 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() {@OverrideObject execute() {return getCommandAction().execute(getExecutionType());}});
}
// 执行fallback方法,如:ServiceA的error()
protected Object getFallback() {final CommandAction commandAction = getFallbackAction();return process(new Action() {@OverrideObject 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 是非常好的学习材料。


欢迎关注陈同学的公众号,一起学习,一起成长

Spring Cloud 源码学习之 Hystrix 入门相关推荐

  1. Spring Cloud源码分析(二)Ribbon(续)

    因文章长度限制,故分为两篇.上一篇:<Spring Cloud源码分析(二)Ribbon> 负载均衡策略 通过上一篇对Ribbon的源码解读,我们已经对Ribbon实现的负载均衡器以及其中 ...

  2. Spring Cloud源码分析(一)Eureka

    看过之前文章的朋友们,相信已经对Eureka的运行机制已经有了一定的了解.为了更深入的理解它的运作和配置,下面我们结合源码来分别看看服务端和客户端的通信行为是如何实现的.另外写这篇文章,还有一个目的, ...

  3. Spring Cloud源码分析(四)Zuul:核心过滤器

    通过之前发布的<Spring Cloud构建微服务架构(五)服务网关>一文,相信大家对于Spring Cloud Zuul已经有了一个基础的认识.通过前文的介绍,我们对于Zuul的第一印象 ...

  4. Spring Cloud源码分析(二)Ribbon

    断断续续看Ribbon的源码差不多也有7-8天了,总算告一段落.本文记录了这些天对源码的阅读过程与一些分析理解,如有不对还请指出. 友情提示:本文较长,请选择一个较为舒适的姿势来阅读 在之前介绍使用R ...

  5. Spring Cloud源码分析——Ribbon客户端负载均衡

    年前聊了Eureka和Zookeeper的区别,然后微服务架构系列就鸽了三个多月,一直沉迷逛B站,无法自拔.最近公司复工,工作状态慢慢恢复(又是元气满满地划水).本文从以下3个方面进行分析(参考了翟永 ...

  6. Spring Cloud源码阅读(一)

    问题 Spring Cloud如何创建两个上下文环境的 Spring Cloud如何加载bootstrap.yml配置文件的 Spring Cloud Config是如何获取远程配置的 Spring ...

  7. Spring Cloud源码分析之Eureka篇第三章:EnableDiscoveryClient与EnableEurekaClient的区别(Edgware版本)

    在基于SpringCloud做开发的时候,EnableDiscoveryClient和EnableEurekaClient这两个注解我们并不陌生,今天就来聊聊它们的区别,和网上更早期的类似文章不同的是 ...

  8. Spring Aop源码学习--Advice通知

    Advice通知,所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置.后置.异常.最终.环绕通知五类 (1)BeforeAdvice.AfterAdvice:SpringAOP自定义的通知, ...

  9. Spring Aop源码学习--PointCut切入点

    PointCut切入点简单来说就是用来指明Advice(增强)所作用的地方(一般指方法),PointCut简单来说是一个基于表达式的拦截条件. PointCut接口及实现类: PointCut接口提供 ...

最新文章

  1. 基于OpenLayers+rbush实现高德轨迹样式
  2. 自定义汇编程序,Weaver和运行时的可插拔知识
  3. Android官方开发文档Training系列课程中文版:OpenGL绘图之图形定义
  4. 抖音封杀小猪佩奇,一年赚100亿的“社会人”得罪了谁?
  5. 小米手机在欧洲首次登顶,市场份额超越苹果;腾讯推出游戏“双减双打”新措施;三星成全球最大芯片厂商|极客头条...
  6. 发送邮件(注册用户并激活邮箱)
  7. vue-props入门
  8. Codeforces 15C Industrial Nim 简单的游戏
  9. JDBC:深入理解PreparedStatement和Statement
  10. Android音视频——H.264帧码流(SODB、RBSP、EBSP)浅析
  11. 电大在线计算机考试,2016电大计算机考试题库(计算机应用基础选择题)
  12. HTML5气泡悬浮框(已经加上完整文件)
  13. 初中计算机课堂游戏设计,如何设计初中信息技术课堂作业
  14. 喜报接连,闪马智能与创始人兼CEO彭垚斩获猎云网、雷锋网多项奖项
  15. 微信小程序云开发完整案例
  16. 钉钉关联微信公众号刷步数思路
  17. SAR图像飞机目标检测识别进展
  18. 遥感影像单目标提取精度评价指标kappa系数的计算公式
  19. ubuntu 10.04 如何进入grub命令行
  20. HTML amp ASP网页制作教程,深入浅出HTML+%26amp%253+ASP网页制作_11497615_松桥..pdf-得力文库...

热门文章

  1. mysql全外连接和笛卡尔积_数据库(join) 内连接、外连接、笛卡尔积
  2. 录录(高清录屏) - Video321 从下载到安装使用
  3. 行式数据库与列式数据库
  4. 构建一个CPU模拟器
  5. cad dwg文件在线展示平台源代码cad格式 dwg文件解析
  6. JavaWeb DWR框架介绍
  7. 如何让word中的表格不分页
  8. 常见工控通讯协议(ICS)
  9. 华为畅玩9a不能升级鸿蒙系统,华为畅享9高配和低配差多少?
  10. http和https有什么区别 端口号多少