写于: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 的使用

SpringCloud案例

# 源码分析 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-hystrixhystrix 时,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 功能。

精彩内容推送,请关注公众号!
最近更新时间: 4/22/2020, 7:23:03 PM