Hystrix源码深度刨析-(1)初探:整合Feign

“不积跬步,无以至千里。”

这个专题开始分析SpringCloud中“Circuit Breaker”的始祖Hystrix。

Circuit Breaker,断路器,SC技术栈中,最早的断路器就是Hystrix,早先是人家奈飞公司的一个限流组件,后来被Spring Cloud整合到自己的技术栈中,其实也只是对它做了一些封装。

现在是2021年下半年了,如果企业中需要选择一款断路器来使用,那么Hystrix未必是“最佳人选”,因为这个组件实际上官方已经很久不维护了

Sentinel,Spring cloud alibaba技术栈中负责断路器的角色,现在国内互联网普遍青睐阿里开源的Alibaba这一套技术栈,功能完备,社区活跃,经过阿里平台的淬炼,稳定性当然是可靠的。

之所以还要写Hystrix,是出于一些初衷和考量的

首先,个人认为,微服务架构,重要的不是用什么技术,什么版本,新或旧,那是API调用工程师关注的,我们这一小撮希望深耕技术的技术人所重视的是优秀开源框的架构设计、运行流程、核心机制,以便于我们以后可以更轻松的看更多开源框架源码,以及借鉴他人好的设计,提升自己的内功。因为,作为曾经的一款优秀的断路器组件,Hystrix值得一阅。

其次,这个栏目之前写了Eureka、Feign、Ribbon,就差一个Hystrix了,然后老一代的微服务框架就告一段落了。

当然,随着写作的进行,新一代的技术栈Alibaba那一套,也在源码分析的计划之中,到时候,可以对比看看,一定受益匪浅,话不多说,正式开始探索Hystrix。

分析源码之前,先建一个工程,引入Hystrix相关依赖:

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <spring.cloud-version>Finchley.SR2</spring.cloud-version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud-version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

我使用的 version 是Finchley.SR2,2019年企业中使用比较广泛的一个版本

Hystrix一般不会单独使用,一般都是和Feign整合使用,在代码中只要加上以下配置,Feign生成的动态代理就可以被Hystrix接管使用了

feign:
  hystrix:
    enabled: true

Hystrix技术具体的使用流程,感兴趣的同学,可以去CSDN上搜索其他的博文,这里就不赘述了,因为专题的定位是源码解析。

一般整合了Hystrix进来,可以在HystrixTargeter的 target方法打个断点看看(Feign源码刨析之前深入讲解过,对Feign不太熟悉的朋友可以去看一下)

@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
                    Target.HardCodedTarget<T> target) {
    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
        return feign.target(target);
    }
    feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
    SetterFactory setterFactory = getOptional(factory.getName(), context,
                                              SetterFactory.class);
    if (setterFactory != null) {
        builder.setterFactory(setterFactory);
    }
    Class<?> fallback = factory.getFallback();
    if (fallback != void.class) {
        return targetWithFallback(factory.getName(), context, target, builder, fallback);
    }
    Class<?> fallbackFactory = factory.getFallbackFactory();
    if (fallbackFactory != void.class) {
        return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
    }

    return feign.target(target);
}

feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;

接下来会走到这行代码,因为现在 Feign.Builderd的类型是 HystrixFeign.Builder

Hystrix源码深度刨析-(1)初探:整合Feign

大致看一下,这个 Builder里面也没有什么特别的东西,和Feign默认的那个Builder相比,应该只有一些超时重试参数可以通过 application.yml 配置文件来配置

Class<?> fallback = factory.getFallback();

这里会获取所有的降级类,就是实现了FeignClient接口的类,然后配置在@FeignClien注解的fallback属性上的那些类,如果你没有编写,那么就会获得一个“void”

private <T> T targetWithFallback(String feignClientName, FeignContext context,
                                 Target.HardCodedTarget<T> target,
                                 HystrixFeign.Builder builder, Class<?> fallback) {
    T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
    return builder.target(target, fallbackInstance);
}

T fallbackInstance = getFromContext(“fallback”, feignClientName, context, fallback, target.type());

这行代码,feignClientName,context,“fallback”,就是从当前FeignClient这个服务对应的Spring容器中获取降级对象。

调用每个服务的时候,那个服务对应的Fallback实例都存在与和那个服务关联的Spring容器中,在需要的时候,直接去对应的Spring容器中获取就行了,当然要注意一下,既然是从容器中拿,那么降级类一定要加上@Component注解,把降级对象交给Spring管理,否则启动会报错,说找不到这个Fallback类型的实例。

后面就简单了,直接调用到了HystrixFeign.Builder的target方法,同时传入一个降级的实例对象fallbackInstance,我们猜想,一旦动态代理中远程http调用目标的方法出现了超时、异常或者熔断,就会调用到降级对象的对应的方法,执行降级逻辑。

builder.target(target, fallbackInstance),跟到target方法看看

public <T> T target(Target<T> target, T fallback) {
    return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
        .newInstance(target);
}

build(fallback != null ? new FallbackFactory.Default(fallback) : null)

先调用了一个build方法,这个fallback 就是降级的对象,肯定不是null,然后就new 出来一个FallbackFactory.Default,并且把这个fallback 传了进去

Feign build(final FallbackFactory<?> nullableFallbackFactory) {
    super.invocationHandlerFactory(new InvocationHandlerFactory() {
        @Override public InvocationHandler create(Target target,
                                                  Map<Method, MethodHandler> dispatch) {
            return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);
        }
    });
    super.contract(new HystrixDelegatingContract(contract));
    return super.build();
}

Configures components needed for hystrix integration.

注意看注释,这个方法在说,配置hystrix集成所需的组件,什么组件,做什么用,先有个基本的印象,然后继续看方法。

return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);

这是极为关键的一行代码

target:就是你要调用的服务

dispatch:一个map,key是FeignClient每个方法的Method对象,value是SynchronousMethodHandler对象

setterFactory:null

nullableFallbackFactory:就是降级对象包装的FallbackFactory工厂

HystrixInvocationHandler这个东西包含了以上4种对象,非常关键,这里先记住

接着走,super.contract(new HystrixDelegatingContract(contract));

这里也比较关键,contract,是用来解析第三方注解的组件,Feign默认的是SpringMvcContract,只用来解析Spring mvc这类注解,现在替换成了HystrixDelegatingContract之后,还可以解析 Hystrix相关的注解,如@HystrixCommand之类的。

super.build();

然后调用父类的build方法,它的父类就是Feign.Builder,所以后面构建流程就跟Feign默认的构建流程一样了,最终返回一个ReflectiveFeign,调用它的newInstance方法生成动态代理,这一块东西,Feign源码解析的时候写过,这里不再赘述。

上一篇:MyBatis 自定义 SQL 拦截器


下一篇:设计模式--创建性模式--建造者模式(Bulider模式)