Hystrix源码阅读(三)与Feign集成

1,219 阅读6分钟

前言

之前已经阅读过Feign和Hystrix的源码了,这章进行spring-cloud-openfeign-core与Hystrix集成的源码分析。

之前的文章:

一、FeignClientFactoryBean创建JDK动态代理

FeignClientFactoryBean#getObject创建Bean实例

@Override
public Object getObject() throws Exception {
    return getTarget();
}

/**
 * T就是目标接口的类型
 */
<T> T getTarget() {
	// 获取FeignContext子容器工厂
    FeignContext context = this.applicationContext.getBean(FeignContext.class);
    // 获取HystrixFeign.Builder
    Feign.Builder builder = feign(context);
    if (!StringUtils.hasText(this.url)) {
        if (!this.name.startsWith("http")) {
            this.url = "http://" + this.name;
        }
        else {
            this.url = this.name;
        }
        this.url += cleanPath();
        return (T) loadBalance(builder, context,
                new HardCodedTarget<>(this.type, this.name, this.url));
    }
    // 省略写死url的逻辑...
}
// 获取Targeter,执行target方法获取动态代理
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
			HardCodedTarget<T> target) {
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        // org.springframework.cloud.openfeign.HystrixTargeter
        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创建动态代理

class HystrixTargeter implements Targeter {

	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
			FeignContext context, Target.HardCodedTarget<T> target) {
        // 如果没配置feign.hystrix.enabled=true,默认的Builder就是Feign.Builder
        // 不会走Hystrix的逻辑
		if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
			return feign.target(target);
		}
		feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
		String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
				: factory.getContextId();
        // 从子容器获取SetterFactory
        // 默认实现是feign.hystrix.SetterFactory.Default
		SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
		if (setterFactory != null) {
			builder.setterFactory(setterFactory);
		}
        // 优先获取fallback对应的Class构造代理
		Class<?> fallback = factory.getFallback();
		if (fallback != void.class) {
			return targetWithFallback(name, context, target, builder, fallback);
		}
        // 其次获取fallbackFactory对应的Class构造代理
		Class<?> fallbackFactory = factory.getFallbackFactory();
		if (fallbackFactory != void.class) {
			return targetWithFallbackFactory(name, context, target, builder,
					fallbackFactory);
		}
		// 如果fallback和fallbackFactory都没获取到,则不启用Hystrix的降级逻辑
		return feign.target(target);
	}
}

targetWithFallback降级逻辑由@FeignClient注解的fallback指定的类提供。

private <T> T targetWithFallback(String feignClientName, FeignContext context,
			Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
			Class<?> fallback) {
    // feignClientName和fallback从子容器获取对应的Bean
    T fallbackInstance = getFromContext("fallback", feignClientName, context,
            fallback, target.type());
    // 调用HystrixFeign.Builder的target方法
    return builder.target(target, fallbackInstance);
}

targetWithFallbackFactory降级逻辑由@FeignClient注解的fallbackFactory指定的类提供。

private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
			Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
			Class<?> fallbackFactoryClass) {
    // 通过feignClientName和fallbackFactoryClass获取子容器中的FallbackFactory实现类
    FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
            "fallbackFactory", feignClientName, context, fallbackFactoryClass,
            FallbackFactory.class);
   	// 调用HystrixFeign.Builder的target方法
    return builder.target(target, fallbackFactory);
}

HystrixFeign.Builder构造ReflectiveFeign

  • 通过fallback构造ReflectiveFeign
public <T> T target(Target<T> target, T fallback) {
  // 把fallback放入FallbackFactory.Default
  // FallbackFactory.Default的create方法会直接返回这个fallback实例
  ReflectiveFeign feign = build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null);
  return feign.newInstance(target);
}
  • 通过fallbackFactory构造ReflectiveFeign
public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
  ReflectiveFeign feign = build(fallbackFactory);
  return feign.newInstance(target);
}
  • 构造无降级ReflectiveFeign
@Override
public Feign build() {
  return build(null);
}
  • HystrixFeign.Builder#build(feign.hystrix.FallbackFactory<?>)统一构造ReflectiveFeign的入口
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
  // 设置创建JDKInvocationHandler的工厂
  super.invocationHandlerFactory(new InvocationHandlerFactory() {
    @Override
    public InvocationHandler create(Target target,
                                    Map<Method, MethodHandler> dispatch) {
      // 创建HystrixInvocationHandler                  
      return new HystrixInvocationHandler(target, dispatch, setterFactory,
          nullableFallbackFactory);
    }
  });
  // 代理SpringMvcContract,对方法元数据解析做一些Hystrix相关的定制改造
  super.contract(new HystrixDelegatingContract(contract));
  // 构造ReflectiveFeign
  return super.build();
}

ReflectiveFeign#newInstance()方法创建JDK动态代理,省略无关方法

@Override
public <T> T newInstance(Target<T> target) {
  // 获取方法名对应的处理类的映射关系
  Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  // 创建HystrixInvocationHandler
  InvocationHandler handler = factory.create(target, methodToHandler);
  // 创建JDK动态代理
  T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
      new Class<?>[] {target.type()}, handler);
  return proxy;
}

二、HystrixInvocationHandler执行HystrixCommand

成员变量及构造方法

final class HystrixInvocationHandler implements InvocationHandler {
  // HardCodedTarget实例
  // 封装原始FeignClient接口Class、服务名(trade-service)、url(http://trade-service)
  private final Target<?> target;
  // 目标方法 - 实际处理Handler
  private final Map<Method, MethodHandler> dispatch;
  // 降级工厂
  private final FallbackFactory<?> fallbackFactory; // Nullable
  // 目标方法 - 目标方法 setAccessible(true)的
  private final Map<Method, Method> fallbackMethodMap;
  // 目标方法 - com.netflix.hystrix.HystrixCommand.Setter
  private final Map<Method, Setter> setterMethodMap;

  HystrixInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch,
      SetterFactory setterFactory, FallbackFactory<?> fallbackFactory) {
    this.target = checkNotNull(target, "target");
    this.dispatch = checkNotNull(dispatch, "dispatch");
    this.fallbackFactory = fallbackFactory;
    // 构造目标方法 - 目标方法映射,主要是为了防止invoke方法多次调用setAccessible(true)
    this.fallbackMethodMap = toFallbackMethod(dispatch);
    // 构造目标方法 - com.netflix.hystrix.HystrixCommand.Setter
    this.setterMethodMap = toSetters(setterFactory, target, dispatch.keySet());
  }
  
  static Map<Method, Method> toFallbackMethod(Map<Method, MethodHandler> dispatch) {
    Map<Method, Method> result = new LinkedHashMap<Method, Method>();
    for (Method method : dispatch.keySet()) {
      method.setAccessible(true);
      result.put(method, method);
    }
    return result;
  }
  
  static Map<Method, Setter> toSetters(SetterFactory setterFactory,
                                       Target<?> target,
                                       Set<Method> methods) {
    Map<Method, Setter> result = new LinkedHashMap<Method, Setter>();
    for (Method method : methods) {
      method.setAccessible(true);
      // 调用SetterFactory的create方法创建Hystrix的Setter
      result.put(method, setterFactory.create(target, method));
    }
    return result;
  }
}

SetterFactory

SetterFactory的默认实现feign.hystrix.SetterFactory.Default。HystrixCommandGroupKey取HardCodedTarget实例的name属性,也就是服务名。HystrixCommandKey取的是StockClient#getStock(Long)类名和方法签名的拼接。所以默认FeignHystrix资源隔离的线程池维度是HystrixCommandGroupKey,一个服务名一个线程池。

final class Default implements SetterFactory {
  @Override
  public HystrixCommand.Setter create(Target<?> target, Method method) {
    String groupKey = target.name();
    String commandKey = Feign.configKey(target.type(), method);
    return HystrixCommand.Setter
        .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
        .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
  }
}

HystrixInvocationHandler#invoke

feign.hystrix.HystrixInvocationHandler#invoke构造HystrixCommand,并根据方法返回参数类型,决定是否同步执行execute方法。

final class HystrixInvocationHandler implements InvocationHandler {
  private final Target<?> target;
  private final Map<Method, MethodHandler> dispatch;
  private final FallbackFactory<?> fallbackFactory; // Nullable
  private final Map<Method, Method> fallbackMethodMap;
  
  @Override
  public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
    // 如果是Object提供的方法,和ReflectiveFeign.FeignInvocationHandler的处理方式一致,省略
    // ...

	// 创建HystrixCommand
    HystrixCommand<Object> hystrixCommand =
        new HystrixCommand<Object>(setterMethodMap.get(method)) {
          @Override
          protected Object run() throws Exception {
          	// 获取MethodHandler执行方法 和ReflectiveFeign.FeignInvocationHandler的处理方式一致
            return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
          }

          @Override
          protected Object getFallback() {
          	// 如果fallbackFactory为空
            // 抛出throw new UnsupportedOperationException("No fallback available.")
            if (fallbackFactory == null) {
              return super.getFallback();
            }
            // getExecutionException获取HystrixCommand执行过程中的异常
            // 调用自己定义的fallbackFactory的create方法创建fallback
            Object fallback = fallbackFactory.create(getExecutionException());
            // 反射调用fallback的对应方法
            Object result = fallbackMethodMap.get(method).invoke(fallback, args);
            // 解析方法的返回类型
            if (isReturnsHystrixCommand(method)) {
              // 如果是HystrixCommand,那么直接执行execute方法同步返回
              return ((HystrixCommand) result).execute();
            } else if (...) {
              // 省略其他类型判断,做特殊返回
              // 比如Single、CompletableFuture等等,都是直接同步返回
              return ...;
            } else {
              // 普通类型
              return result;
            }
          }
        };

	// 解析方法返回类型,确定HystrixCommand如何返回
    if (Util.isDefault(method)) {
     // 如果是默认方法直接同步执行
      return hystrixCommand.execute();
    } else if (isReturnsHystrixCommand(method)) {
     // 如果是HystrixCommand,直接返回HystrixCommand实例,不执行
      return hystrixCommand;
    } else if (isReturnXXX(...)) {
      // ...省略其他返回类型的特殊处理
    } 
    // 正常返回类型,同步执行HystrixCommand的execute方法
    return hystrixCommand.execute();
  }
  
}

三、问题

疑问一

看完HystrixInvocationHandler#invoke方法,注意到对于方法返回类型做了特殊处理,什么场景会使用到这些特殊处理呢。

比如我们可以这样使用FeignClient,虽然都是调用http://trade-service/getStockByMpId/{mpId}但是两个方法的返回值类型不一样:

@FeignClient(primary = false, name = "trade-service", fallbackFactory = StockServiceFallBackFactory.class, configuration = StockServiceFallBackFactory.class)
public interface StockClient {
    @RequestMapping(value = "/getStockByMpId/{mpId}", method = RequestMethod.GET)
    ApiResponse<Stock> getStock(@PathVariable("mpId") Long mpId);
    // 特殊的返回值类型,如HystrixCommand
    @RequestMapping(value = "/getStockByMpId/{mpId}", method = RequestMethod.GET)
    HystrixCommand<ApiResponse<Stock>> getStock2(@PathVariable("mpId") Long mpId);
}
public class StockServiceFallBackFactory implements FallbackFactory<StockClient> {
    @Override
    public StockClient create(Throwable throwable) {
        return new StockClient() {
            @Override
            public ApiResponse<Stock> getStock(Long mpId) {
                return ApiResponse.fail("商品id:" + mpId + ", error = "+ throwable.getMessage());
            }

            @Override
            public HystrixCommand<ApiResponse<Stock>> getStock2(Long mpId) {
                HystrixCommand.Setter setter = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("fallback"));
                return new HystrixCommand<ApiResponse<Stock>>(setter) {
                    @Override
                    protected ApiResponse<Stock> run() throws Exception {
                    	// 可能是又一次网络通信,比如请求某个降级服务
                        Stock stock = new Stock();
                        stock.setMpId(mpId);
                        stock.setStockNum(0);
                        return ApiResponse.success(stock);
                    }
                };
            }
        };
    }
}
public class Test {
    @Autowired
    private StockClient stockClient;
    @Test
    public void test01() {
    	// 同步执行
        System.out.println(stockClient.getStock(1L));
    }
    @Test
    public void test02() {
    	// 创建HystrixCommand但是并没有执行
        HystrixCommand<ApiResponse<Stock>> command = stockClient.getStock2(3L);
        System.out.println(command.isSuccessfulExecution());
        // 先用trade-service线程池执行远程调用
        // 降级使用fallback线程池执行某个降级服务的远程调用
        System.out.println(command.execute());
    }
}

为什么要这样做呢?因为上一章讲过fallback尽量避免阻塞操作,比如网络通信。如果因为业务需要,非得在fallback里做远程调用。那么可以返回HystrixCommand,这样fallback就可以换一个HystrixCommandGroupKey在另外的线程池,并且具有隔离的资源和独立的熔断指标统计。

疑问二

服务提供方返回的是ApiResponse,为什么FeignClient的方法返回类型是HystrixCommand,远程调用以后的反序列化不会失败呢?

原因是HystrixDelegatingContractparseAndValidateMetadata篡改了MethodMetadata里的returnType属性,Decoder反序列化是参照returnType属性执行的。

public final class HystrixDelegatingContract implements Contract {
  private final Contract delegate;
  public HystrixDelegatingContract(Contract delegate) {
    this.delegate = delegate;
  }
  @Override
  public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
  	// 先用SpringMvcContract解析方法元信息
    List<MethodMetadata> metadatas = this.delegate.parseAndValidateMetadata(targetType);
    // 然后获取参数化类型
    for (MethodMetadata metadata : metadatas) {
      Type type = metadata.returnType();
	 // 如果是HystrixCommand<T>,设置返回值类型是T
      if (type instanceof ParameterizedType
          && ((ParameterizedType) type).getRawType().equals(HystrixCommand.class)) {
        Type actualType = resolveLastTypeParameter(type, HystrixCommand.class);
        metadata.returnType(actualType);
      } else if (...) {
      	// ... 省略其他返回值类型判断
      } 
    }
    return metadatas;
  }
}

总结

spring-cloud-openfeign-core与Hystrix集成是在InvocationHandler里创建了HystrixCommand,支持降级,但是不支持缓存(创建的HystrixCommand没有重写getCacheKey方法)。

FeignClient可以返回特殊类型,比如HystrixCommand类型,这样可以在fallback里进行阻塞操作,比如远程调用。