程序员L札记

V1

2022/07/04阅读:22主题:橙心

Spring Cloud OpenFeign整合Hystrix源码分析

基本用法

  1. 引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 开启配置application.properties
feign.hystrix.enabled=true
  1. @FeignClient注解上指定fallBack或者fallBackFactory属性

只需要以上三步即可开启openFeign对hystrix的支持

知识准备

为了更好的分析源码,首先将分析过程中需要用到的一些关键类,加以阐述。

FeignClientsConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ HystrixCommand.classHystrixFeign.class })
protected static class HystrixFeignConfiguration 
{

 @Bean
 @Scope("prototype")
 @ConditionalOnMissingBean
 @ConditionalOnProperty(name = "feign.hystrix.enabled")
 public Feign.Builder feignHystrixBuilder() {
  return HystrixFeign.builder();
 }

}

在FeignClientsConfiguration类中有这么一个配置:在类路径中存在HystrixCommand.class和HystrixFeign.class两个类并且在配置文件中有feign.hystrix.enabled属性时,向容器中注入HystrixFeign.builder()返回值类型的Builder。

FeignAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {

 @Bean
 @ConditionalOnMissingBean
 public Targeter feignTargeter() {
  return new HystrixTargeter();
 }

}

在FeignAutoConfiguration配置类中有这么一个配置:在类路径中存在HystrixFeign类并且容器中缺少Targeter类型的Bean时,向容器中注册的是HystrixTargeter类型的Targeter。

那么在配置文件中配置feign.hystrix.enabled=true时,在spring容器中分别注册了HystrixFeign.builder()返回值类型的Feign.Builder以及HystrixTargeter类型的Targeter。

源码分析

在《Spring Cloud OpenFeign源码解析》篇中,分析到:会通过FeignClientFactoryBean的getObject方法执行具体的调用逻辑。关于具体执行流程已经在这一篇中做了介绍,那么这一篇只分析与hystrix相关的代码。

FeignClientFactoryBean

@Override
public Object getObject() throws Exception {
 return getTarget();
}
<T> getTarget() {
 FeignContext context = applicationContext.getBean(FeignContext.class);
 Feign.Builder builder = feign(context);

 if (!StringUtils.hasText(url)) {
  if (!name.startsWith("http")) {
   url = "http://" + name;
  }
  else {
   url = name;
  }
  url += cleanPath();
  return (T) loadBalance(builder, context,
    new HardCodedTarget<>(type, name, url));
 }
 if (StringUtils.hasText(url) && !url.startsWith("http")) {
  url = "http://" + url;
 }
 String url = this.url + cleanPath();
 Client client = getOptional(context, Client.class);
 if (client != null) {
  if (client instanceof LoadBalancerFeignClient) {
   // not load balancing because we have a url,
   // but ribbon is on the classpath, so unwrap
   client = ((LoadBalancerFeignClient) client).getDelegate();
  }
  if (client instanceof FeignBlockingLoadBalancerClient) {
   // not load balancing because we have a url,
   // but Spring Cloud LoadBalancer is on the classpath, so unwrap
   client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
  }
  builder.client(client);
 }
 Targeter targeter = get(context, Targeter.class);
 return (T) targeter.target(this, builder, context,
   new HardCodedTarget<>(type, name, url));
}
protected Feign.Builder feign(FeignContext context) {
 FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
 Logger logger = loggerFactory.create(type);

 // @formatter:off
 // 此处从容器中获取的就是HystrixFeign.builder()类型的builder
 Feign.Builder builder = get(context, Feign.Builder.class)
   // required values
   .logger(logger)
   .encoder(get(contextEncoder.class))
   .decoder(get(contextDecoder.class))
   .contract(get(contextContract.class))
;
 // @formatter:on

 configureFeign(context, builder);

 return builder;
}

进入loadBalance方法

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
   HardCodedTarget<T> target) {
 Client client = getOptional(context, Client.class);
 if (client != null) {
  builder.client(client);
  // 此处从容器中获取的是HystrixTargeter类型的Targeter
  Targeter targeter = get(context, Targeter.class);
  return targeter.target(this, builder, context, target);
 }

 throw new IllegalStateException(
   "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}

那么进入HystrixTargeter的target方法:

HystrixTargeter

@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);
 }
 // 强转成HystrixFeign.Builder类型的builder
 feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
 String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
   : factory.getContextId();
 SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
 if (setterFactory != null) {
  builder.setterFactory(setterFactory);
 }
 //获取@FeignClient注解上的fallback属性
 Class<?> fallback = factory.getFallback();
 if (fallback != void.class) {
  return targetWithFallback(name, context, target, builder, fallback);
 }
 //获取@FeignClient注解上的fallbackFactory属性
 Class<?> fallbackFactory = factory.getFallbackFactory();
 if (fallbackFactory != void.class) {
  return targetWithFallbackFactory(name, context, target, builder,
    fallbackFactory);
 }
 // 如果既没配置fallBack也没配置fallbackFactory,那么就不走降级逻辑
 return feign.target(target);
}

由以上源码可知:@FeignClient注解上的fallBack属性优先级高于fallbackFactory属性。

进入targetWithFallback方法,至于targetWithFallbackFactory方法,就留给读者朋友自行阅读啦。

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

进入HystrixFeign.Builder的target方法:

HystrixFeign.Builder

public <T> target(Target<T> target, T fallback) {
     return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
         .newInstance(target);
   }
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
  //调用父类Feign.Builder的方法设置InvocationHandlerFactory实例,通过内部类创建InvocationHandlerFactory实例并实现create方法
  super.invocationHandlerFactory(new InvocationHandlerFactory() {
    @Override
    public InvocationHandler create(Target target,
                                    Map<Method, MethodHandler> dispatch)
 
{
      // 返回HystrixInvocationHandler类型的InvocationHandler(jdk的动态代理)
      return new HystrixInvocationHandler(target, dispatch, setterFactory,
          nullableFallbackFactory);
    }
  });
  super.contract(new HystrixDelegatingContract(contract));
  // 最终会调用父类Feign.Builder的build方法,返回ReflectiveFeign类型的Feign实例
  return super.build();
}

进入Feign实例的newInstance方法:

Feign

public abstract <T> T newInstance(Target<T> target);

一个抽象方法,子类ReflectiveFeign负责实现。

ReflectiveFeign

子类ReflectiveFeign继承了Feign抽象类,并实现了newInstance方法

@Override
public <T> T newInstance(Target<T> target) {
  Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

  for (Method method : target.type().getMethods()) {
    if (method.getDeclaringClass() == Object.class) {
      continue;
    } else if (Util.isDefault(method)) {
      DefaultMethodHandler handler = new DefaultMethodHandler(method);
      defaultMethodHandlers.add(handler);
      methodToHandler.put(method, handler);
    } else {
      methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
  }
  //此处通过之前创建的匿名内部类InvocationHandlerFactory的create方法,返回HystrixInvocationHandler实例
  InvocationHandler handler = factory.create(target, methodToHandler);
  //创建jdk动态代理类
  T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
      new Class<?>[] {target.type()}, handler);

  for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    defaultMethodHandler.bindTo(proxy);
  }
  //返回代理类
  return proxy;
}

通过源码分析后知道,利用jdk动态代理最终返回的是@FeignClient注解标识的接口的代理类,那么在方法调用时,会调用HystrixInvocationHandler类的invoke方法。

HystrixInvocationHandler

HystrixInvocationHandler实现了java.lang.reflect.InvocationHandler接口,并重写了invoke方法

@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
    throws Throwable 
{
  // early exit if the invoked method is from java.lang.Object
  // code is the same as ReflectiveFeign.FeignInvocationHandler
  if ("equals".equals(method.getName())) {
    try {
      Object otherHandler =
          args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
      return equals(otherHandler);
    } catch (IllegalArgumentException e) {
      return false;
    }
  } else if ("hashCode".equals(method.getName())) {
    return hashCode();
  } else if ("toString".equals(method.getName())) {
    return toString();
  }

  HystrixCommand<Object> hystrixCommand =
      new HystrixCommand<Object>(setterMethodMap.get(method)) {
        @Override
        protected Object run() throws Exception {
          try {
            return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
          } catch (Exception e) {
            throw e;
          } catch (Throwable t) {
            throw (Error) t;
          }
        }

        @Override
        protected Object getFallback() {
          if (fallbackFactory == null) {
            return super.getFallback();
          }
          try {
            Object fallback = fallbackFactory.create(getExecutionException());
            Object result = fallbackMethodMap.get(method).invoke(fallback, args);
            if (isReturnsHystrixCommand(method)) {
              return ((HystrixCommand) result).execute();
            } else if (isReturnsObservable(method)) {
              // Create a cold Observable
              return ((Observable) result).toBlocking().first();
            } else if (isReturnsSingle(method)) {
              // Create a cold Observable as a Single
              return ((Single) result).toObservable().toBlocking().first();
            } else if (isReturnsCompletable(method)) {
              ((Completable) result).await();
              return null;
            } else if (isReturnsCompletableFuture(method)) {
              return ((Future) result).get();
            } else {
              return result;
            }
          } catch (IllegalAccessException e) {
            // shouldn't happen as method is public due to being an interface
            throw new AssertionError(e);
          } catch (InvocationTargetException | ExecutionException e) {
            // Exceptions on fallback are tossed by Hystrix
            throw new AssertionError(e.getCause());
          } catch (InterruptedException e) {
            // Exceptions on fallback are tossed by Hystrix
            Thread.currentThread().interrupt();
            throw new AssertionError(e.getCause());
          }
        }
      };

  if (Util.isDefault(method)) {
    return hystrixCommand.execute();
  } else if (isReturnsHystrixCommand(method)) {
    return hystrixCommand;
  } else if (isReturnsObservable(method)) {
    // Create a cold Observable
    return hystrixCommand.toObservable();
  } else if (isReturnsSingle(method)) {
    // Create a cold Observable as a Single
    return hystrixCommand.toObservable().toSingle();
  } else if (isReturnsCompletable(method)) {
    return hystrixCommand.toObservable().toCompletable();
  } else if (isReturnsCompletableFuture(method)) {
    return new ObservableCompletableFuture<>(hystrixCommand);
  }
  return hystrixCommand.execute();
}

最终底层会调用Hystrix API,构造HystrixCommand实例,然后调用其execute方法执行远程调用。至于HystrixCommand的执行流程不在本篇介绍的范围内,因此这里不再展开介绍。以后会专门写一篇关于HystrixCommand的源码解析。

总结

至此,关于OpenFeign与Hystrix整合的源码就分析完了,其中细节,还请自行阅读。如有错误之处,还请指正,谢谢!

欢迎关注我的公众号:程序员L札记

更多原创文章,请扫码关注我的微信公众号
更多原创文章,请扫码关注我的微信公众号

分类:

后端

标签:

Java

作者介绍

程序员L札记
V1