写于:2019-06-25 22:52:37
相关版本:Spring Boot 2.1.5.RELEASE 、Spring Cloud Greenwich.SR
一、回顾
在 《Feign 如何进行服务间调用》 一文中,提到了Feign 自动配置类 FeignAutoConfiguration。
在之前提到,Feign 对象进行实例化的时候会调用 Targeter targeter = get(context, Targeter.class); 来进行获取。
Targeter 有两种实现: DefaultTargeter 和 HystrixTargeter。
《Feign 如何进行服务间调用》 中以 DefaultTargeter
进行展开。
在 Feign Hystrix Support 中,通过查看自动配置类 FeignAutoConfiguration 信息,在有 Hystrix 相关依赖情况下,Feign Targeter 的实现为 HystrixTargeter。
二、Feign Hystrix Support
Spring Cloud 官网相关案例信息
三、源码分析 HystrixTargeter 配置下的 Feign Client 代理对象
聚焦 Feign 自动配置类 FeignAutoConfiguration
public class FeignAutoConfiguration {
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
}
从自动配置类 FeignAutoConfiguration 中,我们可以很直观的看到,当我们在依赖中引入 feign-hystrix 和 hystrix 时,Feign 会采用 HystrixTargeter作为其 Targeter 的实现类。
Feign Hystrix 功能支持,由 HystrixTargeter#target 实例化对象时生成一系列HystrixCommand 相关配置。
小贴士:Feign Client 的加载可以分为三步。HystrixTargeter#target 的调用属于第二步:Feign Client 实例化代理对象。
聚焦 HystrixTargeter#target
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
// 1、判定是否引入依赖 feign-hystirx
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
// 2、将 Feign.Builder 强制转化为 HystrixFeign.Builder
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
// 3、获取 SetterFacotry,如果有,设置HystrixFeign.Builder 属性
SetterFactory setterFactory = getOptional(factory.getName(), context,
SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
// 4、获取 fallback 方法,如果有,返回构造的 Target
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder,
fallback);
}
//5、获取 FallbackFactory ,如果有,返回构造的 Target
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder,
fallbackFactory);
}
return feign.target(target);
}
}
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
class HystrixTargeter implements Targeter {
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);
return builder.target(target, fallbackFactory);
}
}
代码中关注 HystrixTargeter#getFromContext 方法,该方法主要是根据相关信息,从 Spring 容器中获取 FallbackFacotry 实例化对象。
之后代码调用 HystrixFeign.Builder#target() 方法。
聚焦HystrixFeign.Builder#target()
public final class HystrixFeign {
public static final class Builder extends Feign.Builder {
public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
return build(fallbackFactory).newInstance(target);
}
/** Configures components needed for hystrix integration. */
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();
}
}
}
HystrixFeign.Builder#target() 主要是两个执行操作:
- build 构建 HystrixInvocationHandler 对象。
DefaultTargeter 调用 build() 默认构造对象是 ReflectiveFeign.FeignInvocationHandler - 进行代理对象的实例化操作。
HystrixTargeter 实例化对象和 DefaultTargeter 都是调用的 ReflectiveFeign#newInstance
虽说 HystrixTargeter 和 DefaultTargeter 实例化代理对象调用的方法相同得到的实例对象都是 HardCodedTarget 但是对应生成的代理对象是不一样的。
验证我的观点,来看看 ReflectiveFeign#newInstance 部分代码,
public class ReflectiveFeign extends Feign {
private final InvocationHandlerFactory factory;
@Override
public <T> T newInstance(Target<T> target) {
......
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler);
return proxy;
}
}
通过代码,能够知道被代理对象 HardCodedTarget 的代理类 接口为 InvocationHandler。
HystrixTargeter 和 DefaultTargeter 对应的 InvocationHandler 是不同的。
Targeter | InvocationHandler |
---|---|
HystrixTargeter | HystrixInvocationHandler |
DefaultTargeter | ReflectiveFeign.FeignInvocationHandler |
Build不同加上代理对象的不同,使得 HystrixTargeter 和 DefaultTargeter 生成不同的代理对象信息。
下面我们来简单的对比下 HystrixTargeter 和 DefaultTargeter 配置下生成的代理类的差异:
对比 HystrixTargeter 和 DefaultTargeter 配置下生成的代理类的差异,HystrixTargeter 相比 DefaultTargeter 多了 fallback 相关方法。
从简单的代码结构看不出具体的差异信息,我们再来看看 HystrixTargeter 与 DefaultTargeter 进行代理调用时的差异。
调用 HystrixTargeter 配置生成的代理对象
在 HystrixTargeter 配置下发起 Feign Client 服务调用是,方法会进入到 HystrixInvocationHandler 代理对象中。
聚焦 HystrixInvocationHandler#invoke
final class HystrixInvocationHandler implements InvocationHandler {
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)throws Throwable {
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() {
......
}
};
......
return hystrixCommand.execute();
}
}
看到这里,整个执行就很直观了。 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 功能。