zoukankan      html  css  js  c++  java
  • Spring Cloud 源码学习之 Hystrix 入门

    Spring Cloud 源码学习之 Hystrix 入门

    https://blog.csdn.net/myle69/article/details/82730801

    欢迎访问陈同学博客原文
    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";
    }
    1
    2
    3
    4
    5
    6
    7
    8
    ServiceB hello() 抛出异常,以便 ServiceA执行 error() 方法。

    @GetMapping("/hello")
    public String hello() {
    throw new RuntimeException("error occurred");
    }
    1
    2
    3
    4
    样例代码表示的就是 服务降级,服务降级换些名词来描述就是: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();
    }
    1
    2
    3
    4
    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 {
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    处理逻辑就在 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;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    HystrixInvokable 只是一个空接口,没有任何方法,只是用来标记具备可执行的能力。

    那 HystrixInvokable 又是如何创建的?它具体的实现类又是什么?先看看 HystrixCommandFactory.getInstance().create() 的代码。

    public HystrixInvokable create(MetaHolder metaHolder) {
    return new GenericCommand(...create(metaHolder));
    }
    1
    2
    3
    实现类是 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);
    }
    });
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    目标方法执行细节
    执行过程想来应该很简单,即先执行目标方法,失败则执行fallback方法。

    再来看看 methodsAnnotatedWithHystrixCommand() 的具体执行代码,它完成了 Hystrix 的整个执行过程。

    Object result = CommandExecutor.execute(invokable, executionType, metaHolder);
    1
    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();
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    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;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    利用 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 是非常好的学习材料。

    欢迎关注陈同学的公众号,一起学习,一起成长
    ---------------------
    作者:码代码的陈同学
    来源:CSDN
    原文:https://blog.csdn.net/myle69/article/details/82730801
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    HP惠普战66电源黄灯闪烁无法充电
    C#.NET rabbit mq 持久化时报错 durable
    手动解压安装mysql8.0 on windows my.ini
    C#.NET MySql8.0 EF db first
    EF MYSQL 出现:输入字符串的格式不正确
    EF MYSQL DB FIRST 出现2次数据库名
    mysql windows 下配置可远程连接
    团队项目的Git分支管理规范
    一个简单的软件测试流程
    微服务架构下的质量迷思——混沌工程
  • 原文地址:https://www.cnblogs.com/handsome1013/p/11193769.html
Copyright © 2011-2022 走看看