写于:2019-06-25 22:52:37

《Feign 如何进行服务间调用》

相关版本:Spring Boot 2.1.5.RELEASE 、Spring Cloud Greenwich.SR

一、回顾

《Feign 如何进行服务间调用》 一文中,提到了Feign 自动配置类 FeignAutoConfiguration

在之前提到,Feign 对象进行实例化的时候会调用 Targeter targeter = get(context, Targeter.class); 来进行获取。

Targeter 有两种实现: DefaultTargeterHystrixTargeter

《Feign 如何进行服务间调用》 中以 DefaultTargeter 进行展开。

在 Feign Hystrix Support 中,通过查看自动配置类 FeignAutoConfiguration 信息,在有 Hystrix 相关依赖情况下,Feign Targeter 的实现为 HystrixTargeter

二、Feign Hystrix Support

Spring Cloud 官网相关案例信息

回顾熟悉一下 Feign 的使用
01.png

三、源码分析 HystrixTargeter 配置下的 Feign Client 代理对象

聚焦 Feign 自动配置类 FeignAutoConfiguration

  1. public class FeignAutoConfiguration {
  2. @Configuration
  3. @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
  4. protected static class DefaultFeignTargeterConfiguration {
  5. @Bean
  6. @ConditionalOnMissingBean
  7. public Targeter feignTargeter() {
  8. return new DefaultTargeter();
  9. }
  10. }
  11. @Configuration
  12. @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
  13. protected static class HystrixFeignTargeterConfiguration {
  14. @Bean
  15. @ConditionalOnMissingBean
  16. public Targeter feignTargeter() {
  17. return new HystrixTargeter();
  18. }
  19. }
  20. }

从自动配置类 FeignAutoConfiguration 中,我们可以很直观的看到,当我们在依赖中引入 feign-hystrixhystrix 时,Feign 会采用 HystrixTargeter作为其 Targeter 的实现类。

Feign Hystrix 功能支持,由 HystrixTargeter#target 实例化对象时生成一系列HystrixCommand 相关配置。

小贴士:Feign Client 的加载可以分为三步。HystrixTargeter#target 的调用属于第二步:Feign Client 实例化代理对象。

聚焦 HystrixTargeter#target

  1. class HystrixTargeter implements Targeter {
  2. @Override
  3. public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
  4. FeignContext context, Target.HardCodedTarget<T> target) {
  5. // 1、判定是否引入依赖 feign-hystirx
  6. if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
  7. return feign.target(target);
  8. }
  9. // 2、将 Feign.Builder 强制转化为 HystrixFeign.Builder
  10. feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
  11. // 3、获取 SetterFacotry,如果有,设置HystrixFeign.Builder 属性
  12. SetterFactory setterFactory = getOptional(factory.getName(), context,
  13. SetterFactory.class);
  14. if (setterFactory != null) {
  15. builder.setterFactory(setterFactory);
  16. }
  17. // 4、获取 fallback 方法,如果有,返回构造的 Target
  18. Class<?> fallback = factory.getFallback();
  19. if (fallback != void.class) {
  20. return targetWithFallback(factory.getName(), context, target, builder,
  21. fallback);
  22. }
  23. //5、获取 FallbackFactory ,如果有,返回构造的 Target
  24. Class<?> fallbackFactory = factory.getFallbackFactory();
  25. if (fallbackFactory != void.class) {
  26. return targetWithFallbackFactory(factory.getName(), context, target, builder,
  27. fallbackFactory);
  28. }
  29. return feign.target(target);
  30. }
  31. }

HystrixTargeter#target 逻辑

1、如果项目中没有引入 feign-hystrix 和 hystrix 依赖 HystrixTargeter 和 DefaultTargeter 返回的 Target 对象信息是一致的。

2、存在feign-hystrix 相关依赖时,Feign.Builder 强制转为 HystrixFeign.Builder。

3、尝试获取 SetterFactory,如果存在进行属性设置

4、尝试获取 Feign Client 对应的 fallback 类,如果存在,调用 HystrixTargeter#targetWithFallback 返回实例化代理对象。

5、fallback 不存在,尝试获取 fallbackFactory 类,如果存在 调用 HystrixTargeter#targetWithFallbackFactory 返回实例化代理对象。

6、如果 fallback 和 fallbackFactory 都不存在,直接调用 返回实例化代理对象。返回的 Target 对象信息想和 DefaultTargeter 一致。

也就是说:在 Feign Client 中只有定义了 Hystrix 熔断信息的类 Fallback 或者 FallbackFactory ,Feign 才会支持 Hystrix 功能。

假设此时有一个 Feign Client ,定义了 Hystrix 熔断信息类:fallbackFactory 。以此来进行源码追踪。

聚焦HystrixTargeter#targetWithFallbackFactory

  1. class HystrixTargeter implements Targeter {
  2. private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
  3. Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
  4. Class<?> fallbackFactoryClass) {
  5. FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
  6. "fallbackFactory", feignClientName, context, fallbackFactoryClass,
  7. FallbackFactory.class);
  8. return builder.target(target, fallbackFactory);
  9. }
  10. }

代码中关注 HystrixTargeter#getFromContext 方法,该方法主要是根据相关信息,从 Spring 容器中获取 FallbackFacotry 实例化对象。

之后代码调用 HystrixFeign.Builder#target() 方法。

聚焦HystrixFeign.Builder#target()

  1. public final class HystrixFeign {
  2. public static final class Builder extends Feign.Builder {
  3. public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
  4. return build(fallbackFactory).newInstance(target);
  5. }
  6. /** Configures components needed for hystrix integration. */
  7. Feign build(final FallbackFactory<?> nullableFallbackFactory) {
  8. super.invocationHandlerFactory(new InvocationHandlerFactory() {
  9. @Override
  10. public InvocationHandler create(Target target,
  11. Map<Method, MethodHandler> dispatch) {
  12. return new HystrixInvocationHandler(target, dispatch, setterFactory,
  13. nullableFallbackFactory);
  14. }
  15. });
  16. super.contract(new HystrixDelegatingContract(contract));
  17. return super.build();
  18. }
  19. }
  20. }

HystrixFeign.Builder#target() 主要是两个执行操作:

  • build 构建 HystrixInvocationHandler 对象。
    DefaultTargeter 调用 build() 默认构造对象是 ReflectiveFeign.FeignInvocationHandler
  • 进行代理对象的实例化操作。
    HystrixTargeter 实例化对象和 DefaultTargeter 都是调用的 ReflectiveFeign#newInstance

虽说 HystrixTargeter 和 DefaultTargeter 实例化代理对象调用的方法相同得到的实例对象都是 HardCodedTarget 但是对应生成的代理对象是不一样的。

验证我的观点,来看看 ReflectiveFeign#newInstance 部分代码,

  1. public class ReflectiveFeign extends Feign {
  2. private final InvocationHandlerFactory factory;
  3. @Override
  4. public <T> T newInstance(Target<T> target) {
  5. ......
  6. InvocationHandler handler = factory.create(target, methodToHandler);
  7. T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler);
  8. return proxy;
  9. }
  10. }

通过代码,能够知道被代理对象 HardCodedTarget 的代理类 接口为 InvocationHandler
HystrixTargeter 和 DefaultTargeter 对应的 InvocationHandler 是不同的。

Targeter InvocationHandler
HystrixTargeter HystrixInvocationHandler
DefaultTargeter ReflectiveFeign.FeignInvocationHandler

Build不同加上代理对象的不同,使得 HystrixTargeter 和 DefaultTargeter 生成不同的代理对象信息。

下面我们来简单的对比下 HystrixTargeter 和 DefaultTargeter 配置下生成的代理类的差异:
02.png
对比 HystrixTargeter 和 DefaultTargeter 配置下生成的代理类的差异,HystrixTargeter 相比 DefaultTargeter 多了 fallback 相关方法。

从简单的代码结构看不出具体的差异信息,我们再来看看 HystrixTargeter 与 DefaultTargeter 进行代理调用时的差异。

调用 HystrixTargeter 配置生成的代理对象

在 HystrixTargeter 配置下发起 Feign Client 服务调用是,方法会进入到 HystrixInvocationHandler 代理对象中。

聚焦 HystrixInvocationHandler#invoke

  1. final class HystrixInvocationHandler implements InvocationHandler {
  2. @Override
  3. public Object invoke(final Object proxy, final Method method, final Object[] args)throws Throwable {
  4. HystrixCommand<Object> hystrixCommand =
  5. new HystrixCommand<Object>(setterMethodMap.get(method)) {
  6. @Override
  7. protected Object run() throws Exception {
  8. try {
  9. return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
  10. } catch (Exception e) {
  11. throw e;
  12. } catch (Throwable t) {
  13. throw (Error) t;
  14. }
  15. }
  16. @Override
  17. protected Object getFallback() {
  18. ......
  19. }
  20. };
  21. ......
  22. return hystrixCommand.execute();
  23. }
  24. }

看到这里,整个执行就很直观了。 HystrixInvocationHandler 执行 Feign Client 的服务调用和 ReflectiveFeign.FeignInvocationHandler 基本一致,区别在于 HystrixInvocationHandler 在方法执行外层包裹了 HystrixCommand ,为 Feign 调用提供了 Hystrix 功能。

四、总结

Feign 提供 Hystrix 功能 对比 Feign Default 功能。

代理对象 Targeter实现
Feign Hystrix Support HystrixInvocationHandler HystrixTargeter
Feign Default ReflectiveFeign.FeignInvocationHandler DefaultTargeter

在执行 Feign Client 调用时,HystrixInvocationHandler#invoke 对比 ReflectiveFeign.FeignInvocationHandler#invoke 最大区别在于 HystrixInvocationHandler#invoke 在调用 Feign Client 时,会对请求封装一层 HystrixCommand,对 Feign 调用进行增强,提供 Hystrix 功能。