一文看懂Openfeign服务调用原理

    OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。
    OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

一、简单使用

引入依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

编写服务引用

@Component
@Slf4j
public class DemoAPIProxy implements InitializingBean {

    @Autowired
    private DemoAPI demoAPI;

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info(this.demoAPI.hello());
    }
}

@FeignClient(name = "openfeign-provider")
@RequestMapping("demo")
public interface DemoAPI {

    @GetMapping("hello")
    String hello();
}

服务启动类

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"org.example.api"})
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

    这样我们就完成了openfeign的服务引用。

二、生成代理

    开启Openfeign能力是通过EnableFeignClients来实现:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

	String[] value() default {};

	String[] basePackages() default {};

	Class<?>[] basePackageClasses() default {};

	Class<?>[] defaultConfiguration() default {};

	Class<?>[] clients() default {};
}

    value是basePackages别名,指向basePackages,是通过类的方式定义扫描路径,defaultConfiguration是默认配置,clients通过具体类的方式指定扫描范围,与basePackages互斥,我们通常使用basePackages方式。
    EnableFeignClients导入了FeignClientsRegistrar类,看一下类继承关系:
在这里插入图片描述

    FeignClientsRegistrar本质是一个ImportBeanDefinitionRegistrar,并且持有环境变量和资源加载器的能力,FeignClientsRegistrar重写了registerBeanDefinitions方法,该方法在容器上下文刷新(启动时调用refresh)时被调用,调用时机此处不展开分析,我们看一下FeignClientsRegistrar#registerBeanDefinitions实现:

public void registerBeanDefinitions(AnnotationMetadata metadata,
		BeanDefinitionRegistry registry) {
	registerDefaultConfiguration(metadata, registry);
	registerFeignClients(metadata, registry);
}

    先注册默认配置,然后注册FeignClient相关类,接着看注册默认配置:

//注册默认配置
private void registerDefaultConfiguration(AnnotationMetadata metadata,
		BeanDefinitionRegistry registry) {
	Map<String, Object> defaultAttrs = metadata
			.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

	if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
		String name;
		if (metadata.hasEnclosingClass()) {
			name = "default." + metadata.getEnclosingClassName();
		}
		else {
			name = "default." + metadata.getClassName();
		}
		registerClientConfiguration(registry, name,
				defaultAttrs.get("defaultConfiguration"));
	}
}
//将配置注册到容器中
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
		Object configuration) {
	BeanDefinitionBuilder builder = BeanDefinitionBuilder
			.genericBeanDefinition(FeignClientSpecification.class);
	builder.addConstructorArgValue(name);
	builder.addConstructorArgValue(configuration);
	registry.registerBeanDefinition(
			name + "." + FeignClientSpecification.class.getSimpleName(),
			builder.getBeanDefinition());
}

    很简单,从EnableFeignClients注解中取出默认配置,然后注册成FeignClientSpecification类定义备用,FeignClientSpecification是包装了名称和配置的类:

class FeignClientSpecification implements NamedContextFactory.Specification {

	private String name;

	private Class<?>[] configuration;

}

    接着看注册FeignClient:

public void registerFeignClients(AnnotationMetadata metadata,
		BeanDefinitionRegistry registry) {
	ClassPathScanningCandidateComponentProvider scanner = getScanner();
	scanner.setResourceLoader(this.resourceLoader);

	Set<String> basePackages;

	Map<String, Object> attrs = metadata
			.getAnnotationAttributes(EnableFeignClients.class.getName());
	AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
			FeignClient.class);
	final Class<?>[] clients = attrs == null ? null
			: (Class<?>[]) attrs.get("clients");
	if (clients == null || clients.length == 0) {
		scanner.addIncludeFilter(annotationTypeFilter);
		basePackages = getBasePackages(metadata);
	}
	else {
		final Set<String> clientClasses = new HashSet<>();
		basePackages = new HashSet<>();
		for (Class<?> clazz : clients) {
			basePackages.add(ClassUtils.getPackageName(clazz));
			clientClasses.add(clazz.getCanonicalName());
		}
		AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
			@Override
			protected boolean match(ClassMetadata metadata) {
				String cleaned = metadata.getClassName().replaceAll("\\$", ".");
				return clientClasses.contains(cleaned);
			}
		};
		scanner.addIncludeFilter(
				new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
	}

	for (String basePackage : basePackages) {
		Set<BeanDefinition> candidateComponents = scanner
				.findCandidateComponents(basePackage);
		for (BeanDefinition candidateComponent : candidateComponents) {
			if (candidateComponent instanceof AnnotatedBeanDefinition) {
				// verify annotated class is an interface
				AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
				AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
				Assert.isTrue(annotationMetadata.isInterface(),
						"@FeignClient can only be specified on an interface");

				Map<String, Object> attributes = annotationMetadata
						.getAnnotationAttributes(
								FeignClient.class.getCanonicalName());

				String name = getClientName(attributes);
				registerClientConfiguration(registry, name,
						attributes.get("configuration"));

				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}
}

    先调用getScanner方法生成类扫描器:

protected ClassPathScanningCandidateComponentProvider getScanner() {
	return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
		@Override
		protected boolean isCandidateComponent(
				AnnotatedBeanDefinition beanDefinition) {
			boolean isCandidate = false;
			if (beanDefinition.getMetadata().isIndependent()) {
				if (!beanDefinition.getMetadata().isAnnotation()) {
					isCandidate = true;
				}
			}
			return isCandidate;
		}
	};
}

    扫描器初始条件限定为被注解标注的独立接口.
    然后设置过滤条件为带FeignClient注解的接口,然后设置扫描包路径或者指定类集合,然后调用registerFeignClient方法执行注册逻辑:

private void registerFeignClient(BeanDefinitionRegistry registry,
		AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
	String className = annotationMetadata.getClassName();
	BeanDefinitionBuilder definition = BeanDefinitionBuilder
			.genericBeanDefinition(FeignClientFactoryBean.class);
	validate(attributes);
	definition.addPropertyValue("url", getUrl(attributes));
	definition.addPropertyValue("path", getPath(attributes));
	String name = getName(attributes);
	definition.addPropertyValue("name", name);
	String contextId = getContextId(attributes);
	definition.addPropertyValue("contextId", contextId);
	definition.addPropertyValue("type", className);
	definition.addPropertyValue("decode404", attributes.get("decode404"));
	definition.addPropertyValue("fallback", attributes.get("fallback"));
	definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
	definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

	String alias = contextId + "FeignClient";
	AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

	boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
															// null

	beanDefinition.setPrimary(primary);

	String qualifier = getQualifier(attributes);
	if (StringUtils.hasText(qualifier)) {
		alias = qualifier;
	}

	BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
			new String[] { alias });
	BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

    将属性处理后封装成FeignClientFactoryBean注册到上下文容器中,而它又是个FactoryBean:
在这里插入图片描述

    在之前文章《@Autowired注解原理分析》有讲到对于FactoryBean类型的类定义,会在注入的时候调用getObject来返回具体实例,具体实现后边分析。
    @EnableFeignClient生成接口代理的时序图如下:
在这里插入图片描述

三、代理注入

    继续用前边的例子来说:

@FeignClient(name = "openfeign-provider")
@RequestMapping("demo")
public interface DemoAPI {

    @GetMapping("hello")
    String hello();
}


@Autowired
private DemoAPI demoAPI;

    为什么DemoAPI是二方服务定义的接口,消费方是看不到具体实现的,为什么可以直接拿来注入并且使用呢?
    我们前边说被FeignClient标注的接口会包装成FeignClientFactoryBean注册到容器中,在使用的时候通过getObject来注入,那么我们来看一下FeignClientFactoryBean的getObject实现:

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

<T> T getTarget() {
	FeignContext context = this.applicationContext.getBean(FeignContext.class);
	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));
	}
	if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
		this.url = "http://" + this.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();
		}
		builder.client(client);
	}
	Targeter targeter = get(context, Targeter.class);
	return (T) targeter.target(this, builder, context,
			new HardCodedTarget<>(this.type, this.name, url));
}

    getObject会调用getTarget,先从容器上下文获取FeignContext实例,那么FeignContext是个什么东西呢?
在这里插入图片描述

public class FeignContext extends NamedContextFactory<FeignClientSpecification> {

	public FeignContext() {
		super(FeignClientsConfiguration.class, "feign", "feign.client.name");
	}

}

    他是创建Feign实例的工厂,继承于NamedContextFactory类(后边分析),那么FeignContext什么时候生成的呢?
    来看另一个配置类:

@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
		FeignHttpClientProperties.class })
public class FeignAutoConfiguration {

	@Autowired(required = false)
	private List<FeignClientSpecification> configurations = new ArrayList<>();

	@Bean
	public HasFeatures feignFeature() {
		return HasFeatures.namedFeature("Feign", Feign.class);
	}

	@Bean
	public FeignContext feignContext() {
		FeignContext context = new FeignContext();
		context.setConfigurations(this.configurations);
		return context;
	}

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

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

	}

	@Configuration
	@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
	protected static class DefaultFeignTargeterConfiguration {

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

	}
	......
}

    FeignAutoConfiguration是Feign的自动配置类,创建了FeignContext,并且选择性创建了Targeter,以及省略调的feignClient配置.
    回到getTarget,获取到FeignContext之后,调用feign方法使用FeignContext生成Feign.Builder:

protected Feign.Builder feign(FeignContext context) {
	FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
	Logger logger = loggerFactory.create(this.type);

	// @formatter:off
	Feign.Builder builder = get(context, Feign.Builder.class)
			// required values
			.logger(logger)
			.encoder(get(context, Encoder.class))
			.decoder(get(context, Decoder.class))
			.contract(get(context, Contract.class));
	// @formatter:on

	configureFeign(context, builder);

	return builder;
}

    这里边从上下问里边获取Feign.Builder实例,那么它是个什么玩意儿?什么时候生成的?

public static class Builder {

    private final List<RequestInterceptor> requestInterceptors =
        new ArrayList<RequestInterceptor>();
    private Logger.Level logLevel = Logger.Level.NONE;
    private Contract contract = new Contract.Default();
    private Client client = new Client.Default(null, null);
    private Retryer retryer = new Retryer.Default();
    private Logger logger = new NoOpLogger();
    private Encoder encoder = new Encoder.Default();
    private Decoder decoder = new Decoder.Default();
    private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
    private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
    private Options options = new Options();
    private InvocationHandlerFactory invocationHandlerFactory =
        new InvocationHandlerFactory.Default();
    private boolean decode404;
    private boolean closeAfterDecode = true;
    private ExceptionPropagationPolicy propagationPolicy = NONE;
}

    Builder是Feign的一个静态内部类,封装了Client、Retryer、Encoder、Decoder和InvocationHandlerFactory等相关组件.
    在FeignClientsConfiguration配置类中定义和注入:

@Configuration
public class FeignClientsConfiguration {


	@Autowired(required = false)
	private Logger logger;

	@Bean
	@ConditionalOnMissingBean
	public Decoder feignDecoder() {
		return new OptionalDecoder(
				new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
	}

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
	public Encoder feignEncoder() {
		return new SpringEncoder(this.messageConverters);
	}

	@Bean
	@ConditionalOnClass(name = "org.springframework.data.domain.Pageable")
	@ConditionalOnMissingBean
	public Encoder feignEncoderPageable() {
		return new PageableSpringEncoder(new SpringEncoder(this.messageConverters));
	}

	@Bean
	@ConditionalOnMissingBean
	public Contract feignContract(ConversionService feignConversionService) {
		return new SpringMvcContract(this.parameterProcessors, feignConversionService);
	}

	@Bean
	public FormattingConversionService feignConversionService() {
		FormattingConversionService conversionService = new DefaultFormattingConversionService();
		for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) {
			feignFormatterRegistrar.registerFormatters(conversionService);
		}
		return conversionService;
	}

	@Bean
	@ConditionalOnMissingBean
	public Retryer feignRetryer() {
		return Retryer.NEVER_RETRY;
	}

	@Bean
	@Scope("prototype")
	@ConditionalOnMissingBean
	public Feign.Builder feignBuilder(Retryer retryer) {
		return Feign.builder().retryer(retryer);
	}
	......
}

    FeignClientsConfiguration中也定义了默认编码解码和重试组件,Builder的作用于为prototype(不同的二方服务不共享)。
    回到feign方法,从容器中拿到Feign.Builder,然后再拿到编解码器和springmvc注解契约装配Builder并返回。

    接着看getTarget方法,如果url没有内容,则会走到loadBalance方法,否则从FeignContext获取Targeter并执行
Targeter.target返回调用。其实loadBalance最终也会调用Targeter.target,所以我们直接看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);
		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?");
}

    首先从context中获取Client,可以理解成最终执行网络请求的客户端,可以自己实现也可以使用FeignAutoConfiguration中定义的ApacheHttpClient或者OkHttpClient,具体使用哪一种classpath中引入了哪种实现和相关依赖,ApacheHttpClient需要引入feign.httpclient.ApacheHttpClient和httpclient,而OkHttpClient需要引入feign.okhttp.OkHttpClient和OkHttp,看一下我们前边FeignAutoConfiguration代码中省略的Client相关配置:
ApacheHttpClient

@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {

	private final Timer connectionManagerTimer = new Timer(
			"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);

	@Autowired(required = false)
	private RegistryBuilder registryBuilder;

	private CloseableHttpClient httpClient;

	@Bean
	@ConditionalOnMissingBean(HttpClientConnectionManager.class)
	public HttpClientConnectionManager connectionManager(
			ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
			FeignHttpClientProperties httpClientProperties) {
		final HttpClientConnectionManager connectionManager = connectionManagerFactory
				.newConnectionManager(httpClientProperties.isDisableSslValidation(),
						httpClientProperties.getMaxConnections(),
						httpClientProperties.getMaxConnectionsPerRoute(),
						httpClientProperties.getTimeToLive(),
						httpClientProperties.getTimeToLiveUnit(),
						this.registryBuilder);
		this.connectionManagerTimer.schedule(new TimerTask() {
			@Override
			public void run() {
				connectionManager.closeExpiredConnections();
			}
		}, 30000, httpClientProperties.getConnectionTimerRepeat());
		return connectionManager;
	}

	@Bean
	public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
			HttpClientConnectionManager httpClientConnectionManager,
			FeignHttpClientProperties httpClientProperties) {
		RequestConfig defaultRequestConfig = RequestConfig.custom()
				.setConnectTimeout(httpClientProperties.getConnectionTimeout())
				.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
				.build();
		this.httpClient = httpClientFactory.createBuilder()
				.setConnectionManager(httpClientConnectionManager)
				.setDefaultRequestConfig(defaultRequestConfig).build();
		return this.httpClient;
	}

	@Bean
	@ConditionalOnMissingBean(Client.class)
	public Client feignClient(HttpClient httpClient) {
		return new ApacheHttpClient(httpClient);
	}

	@PreDestroy
	public void destroy() throws Exception {
		this.connectionManagerTimer.cancel();
		if (this.httpClient != null) {
			this.httpClient.close();
		}
	}

}

OkHttpClient

@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {

	private okhttp3.OkHttpClient okHttpClient;

	@Bean
	@ConditionalOnMissingBean(ConnectionPool.class)
	public ConnectionPool httpClientConnectionPool(
			FeignHttpClientProperties httpClientProperties,
			OkHttpClientConnectionPoolFactory connectionPoolFactory) {
		Integer maxTotalConnections = httpClientProperties.getMaxConnections();
		Long timeToLive = httpClientProperties.getTimeToLive();
		TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
		return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
	}

	@Bean
	public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
			ConnectionPool connectionPool,
			FeignHttpClientProperties httpClientProperties) {
		Boolean followRedirects = httpClientProperties.isFollowRedirects();
		Integer connectTimeout = httpClientProperties.getConnectionTimeout();
		Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
		this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
				.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
				.followRedirects(followRedirects).connectionPool(connectionPool)
				.build();
		return this.okHttpClient;
	}

	@PreDestroy
	public void destroy() {
		if (this.okHttpClient != null) {
			this.okHttpClient.dispatcher().executorService().shutdown();
			this.okHttpClient.connectionPool().evictAll();
		}
	}

	@Bean
	@ConditionalOnMissingBean(Client.class)
	public Client feignClient(okhttp3.OkHttpClient client) {
		return new OkHttpClient(client);
	}

}

    回到loadBalance,从上下文拿到Client后配置到Builder中,然后从上下文中拿到Targeter并调用target方法,Targeter实现有两个,分别是DefaultTargeter和HystrixTargeter,后者具有熔断和Fallback调用能力,我们直接看DefaultTargeter#target:

class DefaultTargeter implements Targeter {

	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
			FeignContext context, Target.HardCodedTarget<T> target) {
		return feign.target(target);
	}
}

    调用Feign.Builder的target方法,继续看实现:

public <T> T target(Target<T> target) {
  return build().newInstance(target);
}

    先调用build返回Feign实例,然后调用newInstance方法,看build实现:

public Feign build() {
  SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
      new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
          logLevel, decode404, closeAfterDecode, propagationPolicy);
  ParseHandlersByName handlersByName =
      new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
          errorDecoder, synchronousMethodHandlerFactory);
  return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

    创建SynchronousMethodHandler.Factory和ParseHandlersByName然后封装成ReflectiveFeign(继承了Feign)并返回,然后看newInstance实现:

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)));
  }
}
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
    new Class<?>[] {target.type()}, handler);

for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
  defaultMethodHandler.bindTo(proxy);
}
return proxy;
}

    看到这里就比较清晰了,把接口中的方法和默认实现放到Map<Method, MethodHandler>中然后使用InvocationHandlerFactory.Default()创建InvocationHandler,然后使用jdk动态代理生成接口的代理并返回,这里主要看一下Feign的InvocationHandler实现:

  static class FeignInvocationHandler implements InvocationHandler {

    private final Target target;
    private final Map<Method, MethodHandler> dispatch;

    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
      this.target = checkNotNull(target, "target");
      this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      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();
      }

      return dispatch.get(method).invoke(args);
    }
   }

    到这里二方服务定义的接口就被@EnableFeignClient通过动态代理的方式注入到了服务中,整流程大致如下:
在这里插入图片描述

四、服务调用

    正如我们前边所说,通过@Autowired或者@Resource注入的时候,注入的是被封装之后的代理类实现,
jdk动态代理持有的是ReflectiveFeign.FeignInvocationHandler类型的InvocationHandler,那么具体调用的时候,会调用FeignInvocationHandler#invoke:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  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();
  }

  return dispatch.get(method).invoke(args);
}

    前几个判断分支都是调用了Object的基本方法,最后dispatch.get(method).invoke(args)才是接口的业务方法调用,dispatch的类型是Map<Method, MethodHandler>,是接口中方法与MethodHandler的映射关系,而MethodHandler又被SynchronousMethodHandler.Factory封装成SynchronousMethodHandler(实现了MethodHandler):

public MethodHandler create(Target<?> target,
                            MethodMetadata md,
                            RequestTemplate.Factory buildTemplateFromArgs,
                            Options options,
                            Decoder decoder,
                            ErrorDecoder errorDecoder) {
  return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
      logLevel, md, buildTemplateFromArgs, options, decoder,
      errorDecoder, decode404, closeAfterDecode, propagationPolicy);
}

    那么dispatch.get(method).invoke(args)最终调用的就是SynchronousMethodHandler#invoke:

  @Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        try {
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

    可以看到调用的时候默认是带有重试能力,默认是5次,具体调用交给executeAndDecode来实现:

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
 	....
 	//省略
  }

    核心是client.execute,我们选择性看一下OkHttpClient的实现:

  @Override
  public feign.Response execute(feign.Request input, feign.Request.Options options)
      throws IOException {
    okhttp3.OkHttpClient requestScoped;
    if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis()
        || delegate.readTimeoutMillis() != options.readTimeoutMillis()
        || delegate.followRedirects() != options.isFollowRedirects()) {
      requestScoped = delegate.newBuilder()
          .connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
          .readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
          .followRedirects(options.isFollowRedirects())
          .build();
    } else {
      requestScoped = delegate;
    }
    Request request = toOkHttpRequest(input);
    Response response = requestScoped.newCall(request).execute();
    return toFeignResponse(response, input).toBuilder().request(input).build();
  }

    很明显,最终服务调用会委托给okhttp3.OkHttpClient来执行,然后组装结果和状态码返回调用,到这里openfeign的服务调用就分析完了,为了帮助理解和有更直观的概念,我们看一下服务调用时序图:
在这里插入图片描述

五、组件支撑

  • FeignClientsRegistrar:扫描@FeignClient注解的接口并注册成BeanDefinition到应用容器
  • FeignClientFactoryBean:注入远程服务接口代理实现
  • FeignContext:维护服务维度上下文,存储服务调用相关工具
  • Feign.Builder:用于构建服务调用的Client,维护服务调用相关组件(编解码器、重试组件、代理创建工厂等)
  • Targeter:用于组装服务接口代理
  • Feign:用于创建服务接口代理
  • ReflectiveFeign:接口代理实现
  • Client:封装网络调用工具,并提供请求调用能力(Default、ApacheHttpClient和OkHttpClient等)

六、总结

    本文分析了Openfeign的服务调用流程,通过源码从生成代理、代理注入和服务调用维度分析了服务调用的具体实现,后边接涉及到了整个流程中涉及到的一些核心概念和组件,中间涉及到负载均衡部分没有展开深入分析,如果感兴趣可以自己翻阅相关资料或者源码,希望通过本篇文章的介绍,对大家Openfeign的使用和原理理解所有帮助,如若文中有错误或者理解偏差的地方,希望及时联系作者修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值