Feign源码解析之使用Hystrix

/ Java / 没有评论 / 1798浏览

Feign源码解析之使用Hystrix

前文

Feign源码解析之注入IOC容器 Feign源码解析之生成jdk动态代理 Feign源码解析之代理类的处理逻辑

正文

上一篇文章讲解了在springcloud项目中feign代理类和feign方法的处理逻辑,在实际项目中,feign经常和hystrix一起使用。hystrix是一种熔断机制,当某个时间单位内错误次数达到一定比例,hystrix会认为服务出现故障,触发熔断,后续请求不再走该服务,防止引发系统错误的雪崩效应。当熔断时间错误后,hystrx会向对应服务尝试一次请求,判断服务是否恢复。

接下来我们看一下是用来hystrix后feign的代理类和处理逻辑和原来相比有哪些变化。

曾经在feign中是默认使用hystrix,虽然在后续的版本中改为了默认不使用hyxtrix,不过要想启用feign的hystrix,只需要增加配置feign.hystrix.enabled:true。另外提一点,可以通过@EnableHystrix或@EnableCircuitBreaker在整个项目中使用hystrix。

当feign中使用了hystrix,根据FeignClientsConfiguration的HystrixFeignConfiguration和FeignAutoConfiguration的HystrixFeignTargeterConfiguration可以看出,IOC容器中注入的Feign.Builder类和HystrixTargeter发生了变化。

@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
  @Bean
  @Scope("prototype")
  @ConditionalOnMissingBean
  @ConditionalOnProperty(name = "feign.hystrix.enabled")
  public Feign.Builder feignHystrixBuilder() {
    return HystrixFeign.builder();
  }
}
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
  @Bean
  @ConditionalOnMissingBean
  public Targeter feignTargeter() {
    return new HystrixTargeter();
  }
}

从上一篇文章已经知道,Feign接口通过FeignClientFactoryBean的getObject方法委托targeter.target方法生成代理类,我们进行HystrixTargeter类的target方法。

@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);
}

可以看出分4种情况:

A. 非hyxtrix的feign builder类,走feign.target方法

这种情况用于用户自定义配置的场景,后续生成动态代理的逻辑由用户自行实现。

B. FeignClient带fallback属性,走targetWithFallback方法。

  1. 首先从applicationContext中根据类型获取fallback对象,因此在设置fallback属性时要注意两点:
    • fallback类必须注入IOC容器。
    • fallback必须实现对应的feign类。
  2. 然后将fallback封装成fallbackFactory进行处理,后续代码与targetWithFallbackFactory相同,分析targetWithFallbackFactory方法时再展开。
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);
}
private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context,
                             Class<?> beanType, Class<T> targetType) {
  Object fallbackInstance = context.getInstance(feignClientName, beanType);
  if (fallbackInstance == null) {
    throw new IllegalStateException(String.format(
      "No " + fallbackMechanism + " instance of type %s found for feign client %s",
      beanType, feignClientName));
  }

  if (!targetType.isAssignableFrom(beanType)) {
    throw new IllegalStateException(
      String.format(
        "Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
        beanType, targetType, feignClientName));
  }
  return (T) fallbackInstance;
}
public <T> T target(Target<T> target, T fallback) {
  return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
    .newInstance(target);
}

C. FeignClient带fallbackFactory属性,走targetWithFallbackFactory方法。

  1. 首先从application获取到targetWithFallbackFactory对象,在设置fallbackFactory时需要注意两点:
    • fallbackFactory必须注入IOC容器,
    • fallbackFactory的create方法返回值必须是接口类型或其子类型。
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
                                        Target.HardCodedTarget<T> target,
                                        HystrixFeign.Builder builder,
                                        Class<?> fallbackFactoryClass) {
  FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>)
    getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
  /* We take a sample fallback from the fallback factory to check if it returns a fallback
	that is compatible with the annotated feign interface. */
  Object exampleFallback = fallbackFactory.create(new RuntimeException());
  Assert.notNull(exampleFallback,
                 String.format(
                   "Incompatible fallbackFactory instance for feign client %s. Factory may not produce null!",
                   feignClientName));
  if (!target.type().isAssignableFrom(exampleFallback.getClass())) {
    throw new IllegalStateException(
      String.format(
        "Incompatible fallbackFactory instance for feign client %s. Factory produces instances of '%s', but should produce instances of '%s'",
        feignClientName, exampleFallback.getClass(), target.type()));
  }
  return builder.target(target, fallbackFactory);
}
public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
  return build(fallbackFactory).newInstance(target);
}
  1. 接着看build方法,可以看出,和其父类的build方法(Feign默认的build逻辑)相比,只是修改了其InvocationHandlerFactory属性和ParseHandlersByName属性的contract属性,返回的依旧是ReflevtiveFeign方法,后续的逻辑和上一篇的feign默认的逻辑相同,不再展开。
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();
}

那么,修改了ReflevtiveFeign的InvocationHandlerFactory类型的属性会产生什么影响呢? 这一属性修改对应的是ReflectiveFeign的newInstance方法的InvocationHandler handler = factory.create(target, methodToHandler);语句的执行结果。

默认的不带hystrix的逻辑会调用InvocationHandlerFactory.Default方法得到FeignInvocationHandler类,而带hystrix的feign逻辑从上文对应invocationHandlerFactory的匿名类可以看出,返回的是HystrixInvocationHandler,代理类处理方法时的逻辑自然发生了变化。

static final class Default implements InvocationHandlerFactory {

  @Override
  public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
    return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
  }
}

修改了ReflevtiveFeign的ParseHandlersByName属性的contract属性会产生什么影响呢? 这一属性的修改对应的是ReflectiveFeign的newInstance方法的Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);语句,更细致点说对应ParseHandlersByName类的apply方法的List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());语句,由Feign方法到MethodMetaData的逻辑发生了变化。

接下来仔细看看发生了什么变化,如下文,这里的delegate对应的就是Feign中的默认Contract,因此可以看出在执行完原先Feign的处理逻辑之后,HystrixDelegatingContract又在其后进行遍历增加了对返回类型的额外判断和处理。

@Override
public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
  List<MethodMetadata> metadatas = this.delegate.parseAndValidatateMetadata(targetType);

  for (MethodMetadata metadata : metadatas) {
    Type type = metadata.returnType();

    if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(HystrixCommand.class) {
      Type actualType = resolveLastTypeParameter(type, HystrixCommand.class);
      metadata.returnType(actualType);
    } else if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(Observable.class)) {
      Type actualType = resolveLastTypeParameter(type, Observable.class);
      metadata.returnType(actualType);
    } else if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(Single.class)) {
      Type actualType = resolveLastTypeParameter(type, Single.class);
      metadata.returnType(actualType);
    } else if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(Completable.class)) {
      metadata.returnType(void.class);
    }
  }
  return metadatas;
}

ParameterizedType表示参数化类型,即泛型,如Collection。getRawType表示对应ParameterizedType的类型,如Collection的getRawType返回Collection的className。 HystrixDelegatingContract针对返回类型是泛型的方法且getRawType为hystrix中特殊类的方法做处理,getRawType为HystrixCommand、Observable、Single类型都由resolveLastTypeParameter获取到返回值,Completable返回值类型设置为void。

D. FeignClient没有设置回滚,走feign.target。

此时按fallbackFactory为null作为参数条用build方法,其它逻辑与上述的一致。

@Override
 public Feign build() {
   return build(null);
 }

二. HystrixInvocationHandler的invoke方法

最后,看一看启用了hystrix的feign对应的HystrixInvocationHandler的invoke方法,分4步:

  1. 对于equals、hashCode、toString的方法处理,和FeignInvocationHandler相同。
  2. 封装成hystrixCommand,包括执行逻辑run和回滚逻辑fallback。
  1. 返回值为HystrixCommand、Observable、Single、Completable的特殊处理。
  2. 执行hystrixCommand的execute方法,
@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 {
          return result;
        }-
      } catch (IllegalAccessException e) {
        // shouldn't happen as method is public due to being an interface
        throw new AssertionError(e);
      } catch (InvocationTargetException e) {
        // Exceptions on fallback are tossed by Hystrix
        throw new AssertionError(e.getCause());
      }
    }
  };

  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();
  }
  return hystrixCommand.execute();
}