Feign源码解析

àì夳堔傛蜴生んèń 2022-09-12 06:51 268阅读 0赞

文章目录

    • 代码示例
    • 源码解析
      • 自动装配
      • 初始化
      • 生成动态代理类
      • 调用接口方法
    • 参考资料

代码示例

启动类上添加@EnableFeignClients注解,表示开启feign功能

  1. @SpringBootApplication
  2. @EnableFeignClients
  3. public class ServiceFeignApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run( ServiceFeignApplication.class, args );
  6. }
  7. }
  8. @FeignClient(value = "eureka-client") //指定调用哪个服务
  9. public interface FeignService {
  10. @RequestMapping("/hello")
  11. String helloFromClientOne(@RequestParam("name") String name);
  12. }

源码解析

自动装配

先看feign的自动装配,即看下spring.factories中有哪些自动装配类

在这里插入图片描述
其中主要的就是FeignRibbonClientAutoConfiguration和FeignAutoConfiguration
先看下FeignRibbonClientAutoConfiguration,看类名应该是跟ribbon的负载均衡有关,feign默认使用ribbon作为负载均衡器

  1. @ConditionalOnClass({ ILoadBalancer.class, Feign.class })
  2. @Configuration
  3. @AutoConfigureBefore(FeignAutoConfiguration.class)
  4. @EnableConfigurationProperties({ FeignHttpClientProperties.class })
  5. //Order is important here, last should be the default, first should be optional
  6. // see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
  7. @Import({ HttpClientFeignLoadBalancedConfiguration.class,
  8. OkHttpFeignLoadBalancedConfiguration.class,
  9. DefaultFeignLoadBalancedConfiguration.class })
  10. public class FeignRibbonClientAutoConfiguration {
  11. // 用于创建并缓存负载均衡器
  12. @Bean
  13. @Primary
  14. @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
  15. public CachingSpringLoadBalancerFactory cachingLBClientFactory(
  16. SpringClientFactory factory) {
  17. return new CachingSpringLoadBalancerFactory(factory);
  18. }
  19. @Bean
  20. @Primary
  21. @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
  22. public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
  23. SpringClientFactory factory,
  24. LoadBalancedRetryFactory retryFactory) {
  25. return new CachingSpringLoadBalancerFactory(factory, retryFactory);
  26. }
  27. @Bean
  28. @ConditionalOnMissingBean
  29. public Request.Options feignRequestOptions() {
  30. return LoadBalancerFeignClient.DEFAULT_OPTIONS;
  31. }
  32. }

@AutoConfigureBefore(FeignAutoConfiguration.class)说明FeignRibbonClientAutoConfiguration先于FeignAutoConfiguration加载
看注解CachingSpringLoadBalancerFactory应该是跟重试相关的
Request.Options是超时的设置
该配置类最重要的就是import了另外3个配置类

在这里插入图片描述
HttpClientFeignLoadBalancedConfiguration

  1. @Configuration
  2. @ConditionalOnClass(ApacheHttpClient.class)
  3. @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
  4. class HttpClientFeignLoadBalancedConfiguration {
  5. @Configuration
  6. @ConditionalOnMissingBean(CloseableHttpClient.class)
  7. protected static class HttpClientFeignConfiguration {
  8. // 创建httpclient以及一些相关配置
  9. }
  10. @Bean
  11. @ConditionalOnMissingBean(Client.class)
  12. public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
  13. SpringClientFactory clientFactory, HttpClient httpClient) {
  14. ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
  15. return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
  16. }
  17. }

OkHttpFeignLoadBalancedConfiguration

  1. @Configuration
  2. @ConditionalOnClass(OkHttpClient.class)
  3. @ConditionalOnProperty(value = "feign.okhttp.enabled")
  4. class OkHttpFeignLoadBalancedConfiguration {
  5. @Configuration
  6. @ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
  7. protected static class OkHttpFeignConfiguration {
  8. // 创建OkHttpClient以及一些相关配置
  9. }
  10. @Bean
  11. @ConditionalOnMissingBean(Client.class)
  12. public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
  13. SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
  14. OkHttpClient delegate = new OkHttpClient(okHttpClient);
  15. return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
  16. }
  17. }

DefaultFeignLoadBalancedConfiguration

  1. @Configuration
  2. class DefaultFeignLoadBalancedConfiguration {
  3. @Bean
  4. @ConditionalOnMissingBean
  5. public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
  6. SpringClientFactory clientFactory) {
  7. return new LoadBalancerFeignClient(new Client.Default(null, null),
  8. cachingFactory, clientFactory);
  9. }
  10. }

这3个配置类均是用来创建执行request的client的,默认是Client.Default,而ApacheHttpClient和OkHttpClient均需要导入相应的依赖以及开启配置,这说明可以使用HttpClient和OKHttp来替代默认的HttpUrlConnection。该3个配置创建client均使用LoadBalancerFeignClient来包装一层,说明都是带负载均衡功能的client。
FeignAutoConfiguration

  1. @Configuration
  2. @ConditionalOnClass(Feign.class)
  3. @EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class})
  4. public class FeignAutoConfiguration {
  5. @Autowired(required = false)
  6. private List<FeignClientSpecification> configurations = new ArrayList<>();
  7. @Bean
  8. public HasFeatures feignFeature() {
  9. return HasFeatures.namedFeature("Feign", Feign.class);
  10. }
  11. @Bean
  12. public FeignContext feignContext() {
  13. FeignContext context = new FeignContext();
  14. context.setConfigurations(this.configurations);
  15. return context;
  16. }
  17. @Configuration
  18. @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
  19. protected static class HystrixFeignTargeterConfiguration {
  20. @Bean
  21. @ConditionalOnMissingBean
  22. public Targeter feignTargeter() {
  23. return new HystrixTargeter();
  24. }
  25. }
  26. @Configuration
  27. @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
  28. protected static class DefaultFeignTargeterConfiguration {
  29. @Bean
  30. @ConditionalOnMissingBean
  31. public Targeter feignTargeter() {
  32. return new DefaultTargeter();
  33. }
  34. }
  35. // the following configuration is for alternate feign clients if
  36. // ribbon is not on the class path.
  37. // see corresponding configurations in FeignRibbonClientAutoConfiguration
  38. // for load balanced ribbon clients.
  39. @Configuration
  40. @ConditionalOnClass(ApacheHttpClient.class)
  41. @ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
  42. @ConditionalOnMissingBean(CloseableHttpClient.class)
  43. @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
  44. protected static class HttpClientFeignConfiguration {
  45. private final Timer connectionManagerTimer = new Timer(
  46. "FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
  47. @Autowired(required = false)
  48. private RegistryBuilder registryBuilder;
  49. private CloseableHttpClient httpClient;
  50. @Bean
  51. @ConditionalOnMissingBean(HttpClientConnectionManager.class)
  52. public HttpClientConnectionManager connectionManager(
  53. ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
  54. FeignHttpClientProperties httpClientProperties) {
  55. final HttpClientConnectionManager connectionManager = connectionManagerFactory
  56. .newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
  57. httpClientProperties.getMaxConnectionsPerRoute(),
  58. httpClientProperties.getTimeToLive(),
  59. httpClientProperties.getTimeToLiveUnit(), registryBuilder);
  60. this.connectionManagerTimer.schedule(new TimerTask() {
  61. @Override
  62. public void run() {
  63. connectionManager.closeExpiredConnections();
  64. }
  65. }, 30000, httpClientProperties.getConnectionTimerRepeat());
  66. return connectionManager;
  67. }
  68. @Bean
  69. public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
  70. HttpClientConnectionManager httpClientConnectionManager,
  71. FeignHttpClientProperties httpClientProperties) {
  72. RequestConfig defaultRequestConfig = RequestConfig.custom()
  73. .setConnectTimeout(httpClientProperties.getConnectionTimeout())
  74. .setRedirectsEnabled(httpClientProperties.isFollowRedirects())
  75. .build();
  76. this.httpClient = httpClientFactory.createBuilder().
  77. setConnectionManager(httpClientConnectionManager).
  78. setDefaultRequestConfig(defaultRequestConfig).build();
  79. return this.httpClient;
  80. }
  81. @Bean
  82. @ConditionalOnMissingBean(Client.class)
  83. public Client feignClient(HttpClient httpClient) {
  84. return new ApacheHttpClient(httpClient);
  85. }
  86. @PreDestroy
  87. public void destroy() throws Exception {
  88. connectionManagerTimer.cancel();
  89. if(httpClient != null) {
  90. httpClient.close();
  91. }
  92. }
  93. }
  94. @Configuration
  95. @ConditionalOnClass(OkHttpClient.class)
  96. @ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
  97. @ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
  98. @ConditionalOnProperty(value = "feign.okhttp.enabled")
  99. protected static class OkHttpFeignConfiguration {
  100. private okhttp3.OkHttpClient okHttpClient;
  101. @Bean
  102. @ConditionalOnMissingBean(ConnectionPool.class)
  103. public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
  104. OkHttpClientConnectionPoolFactory connectionPoolFactory) {
  105. Integer maxTotalConnections = httpClientProperties.getMaxConnections();
  106. Long timeToLive = httpClientProperties.getTimeToLive();
  107. TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
  108. return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
  109. }
  110. @Bean
  111. public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
  112. ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
  113. Boolean followRedirects = httpClientProperties.isFollowRedirects();
  114. Integer connectTimeout = httpClientProperties.getConnectionTimeout();
  115. Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
  116. this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).
  117. connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
  118. followRedirects(followRedirects).
  119. connectionPool(connectionPool).build();
  120. return this.okHttpClient;
  121. }
  122. @PreDestroy
  123. public void destroy() {
  124. if(okHttpClient != null) {
  125. okHttpClient.dispatcher().executorService().shutdown();
  126. okHttpClient.connectionPool().evictAll();
  127. }
  128. }
  129. @Bean
  130. @ConditionalOnMissingBean(Client.class)
  131. public Client feignClient() {
  132. return new OkHttpClient(this.okHttpClient);
  133. }
  134. }
  135. }

FeignAutoConfiguration则就是单单feign的自动装配类。可以看到,这个配置类也创建了几个bean,包括FeignContext,Targeter等,Targeter还分为HystrixTargeter和DefaultTargeter,由于feign集成了hystrix,所以这里会创建HystrixTargeter。该配置类也有创建Client的代码,不带负载均衡功能,但是由于FeignRibbonClientAutoConfiguration先生效,已经有Client的bean了,所以这里不会重复创建bean。

自动装配类也就是创建一些相关的bean,具体的入口还得看开启feign的注解。

初始化

先看@EnableFeignClients注解

  1. /** * 扫描被@FeignClient 注解标注的类作为feign客户端 * */
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.TYPE)
  4. @Documented
  5. @Import(FeignClientsRegistrar.class)
  6. public @interface EnableFeignClients {
  7. String[] value() default { };
  8. String[] basePackages() default { };
  9. Class<?>[] basePackageClasses() default { };
  10. /** * feign全局配置类 */
  11. Class<?>[] defaultConfiguration() default { };
  12. /** * 用@FeignClient注释的类列表。如果不为空,则禁用类路径扫描,否则扫描类路径 */
  13. Class<?>[] clients() default { };
  14. }

看到了@Import(FeignClientsRegistrar.class)
FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,在Spring容器初始化过程中会调用registerBeanDefinitions方法动态注册BeanDefinition,这是第三方框架与Spring集成的常用方式。

  1. @Override
  2. public void registerBeanDefinitions(AnnotationMetadata metadata,
  3. BeanDefinitionRegistry registry) {
  4. registerDefaultConfiguration(metadata, registry);
  5. registerFeignClients(metadata, registry);
  6. }

这里包括两个步骤
1、注册@EnableFeignClients中定义defaultConfiguration属性下的类,包装成FeignClientSpecification,注册到Spring容器

  1. private void registerDefaultConfiguration(AnnotationMetadata metadata,
  2. BeanDefinitionRegistry registry) {
  3. Map<String, Object> defaultAttrs = metadata
  4. .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
  5. if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
  6. String name;
  7. if (metadata.hasEnclosingClass()) {
  8. name = "default." + metadata.getEnclosingClassName();
  9. }
  10. else {
  11. name = "default." + metadata.getClassName();
  12. }
  13. // 注册feign的全局配置类
  14. registerClientConfiguration(registry, name,
  15. defaultAttrs.get("defaultConfiguration"));
  16. }
  17. }
  18. private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
  19. Object configuration) {
  20. // 使用BeanDefinitionBuilder构建BeanDefinition,bean类型是FeignClientSpecification
  21. BeanDefinitionBuilder builder = BeanDefinitionBuilder
  22. .genericBeanDefinition(FeignClientSpecification.class);
  23. // 添加FeignClientSpecification构造方法的两个参数
  24. builder.addConstructorArgValue(name);
  25. builder.addConstructorArgValue(configuration);
  26. registry.registerBeanDefinition(
  27. name + "." + FeignClientSpecification.class.getSimpleName(),
  28. builder.getBeanDefinition());
  29. }

将所有的全局配置配包装成一个FeignClientSpecification,里面包括name和配置类数组,后面每个feign客户端自定义配置也会分别包装为FeignClientSpecification。
2、对于每个@FeignClient进行解析,并将他们注册到spring容器

  1. public void registerFeignClients(AnnotationMetadata metadata,
  2. BeanDefinitionRegistry registry) {
  3. ClassPathScanningCandidateComponentProvider scanner = getScanner();
  4. scanner.setResourceLoader(this.resourceLoader);
  5. Set<String> basePackages;
  6. Map<String, Object> attrs = metadata
  7. .getAnnotationAttributes(EnableFeignClients.class.getName());
  8. // 只过滤出带@FeignClient注解的类
  9. AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
  10. FeignClient.class);
  11. final Class<?>[] clients = attrs == null ? null
  12. : (Class<?>[]) attrs.get("clients");
  13. // 如果@EnableFeignClients注解中的clients属性没有设置,则扫描类路径下的包
  14. if (clients == null || clients.length == 0) {
  15. scanner.addIncludeFilter(annotationTypeFilter);
  16. basePackages = getBasePackages(metadata);
  17. }
  18. else {
  19. final Set<String> clientClasses = new HashSet<>();
  20. basePackages = new HashSet<>();
  21. for (Class<?> clazz : clients) {
  22. basePackages.add(ClassUtils.getPackageName(clazz));
  23. clientClasses.add(clazz.getCanonicalName());
  24. }
  25. AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
  26. @Override
  27. protected boolean match(ClassMetadata metadata) {
  28. String cleaned = metadata.getClassName().replaceAll("\\$", ".");
  29. return clientClasses.contains(cleaned);
  30. }
  31. };
  32. scanner.addIncludeFilter(
  33. new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
  34. }
  35. // 扫描包
  36. for (String basePackage : basePackages) {
  37. Set<BeanDefinition> candidateComponents = scanner
  38. .findCandidateComponents(basePackage);
  39. for (BeanDefinition candidateComponent : candidateComponents) {
  40. if (candidateComponent instanceof AnnotatedBeanDefinition) {
  41. // verify annotated class is an interface
  42. AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
  43. AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
  44. Assert.isTrue(annotationMetadata.isInterface(),
  45. "@FeignClient can only be specified on an interface");
  46. Map<String, Object> attributes = annotationMetadata
  47. .getAnnotationAttributes(
  48. FeignClient.class.getCanonicalName());
  49. String name = getClientName(attributes);
  50. // 把@FeignClient上配置的configuration包装成FeignClientSpecification到容器中
  51. registerClientConfiguration(registry, name,
  52. attributes.get("configuration"));
  53. // 注册feign客户端
  54. registerFeignClient(registry, annotationMetadata, attributes);
  55. }
  56. }
  57. }
  58. }

registerFeignClient方法把FeignClientFactoryBean注入到容器,FeignClientFactoryBean用于生产FeignClient

  1. private void registerFeignClient(BeanDefinitionRegistry registry,
  2. AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
  3. String className = annotationMetadata.getClassName();
  4. // bean类型是FeignClientFactoryBean
  5. BeanDefinitionBuilder definition = BeanDefinitionBuilder
  6. .genericBeanDefinition(FeignClientFactoryBean.class);
  7. validate(attributes);
  8. // 设置bean的属性
  9. definition.addPropertyValue("url", getUrl(attributes));
  10. definition.addPropertyValue("path", getPath(attributes));
  11. String name = getName(attributes);
  12. definition.addPropertyValue("name", name);
  13. definition.addPropertyValue("type", className);
  14. definition.addPropertyValue("decode404", attributes.get("decode404"));
  15. definition.addPropertyValue("fallback", attributes.get("fallback"));
  16. definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
  17. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  18. String alias = name + "FeignClient";
  19. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
  20. boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
  21. beanDefinition.setPrimary(primary);
  22. String qualifier = getQualifier(attributes);
  23. if (StringUtils.hasText(qualifier)) {
  24. alias = qualifier;
  25. }
  26. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
  27. new String[] { alias });
  28. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  29. }

FeignClientFactoryBean实现了FactoryBean接口,所以当获取FeignClientFactoryBean实例时是调用FeignClientFactoryBean的getObject方法。

FeignClientSpecification继承了NamedContextFactory.Specification,而NamedContextFactory用于创建子上下文将NamedContextFactory.Specification放入其中。NamedContextFactory中维护一个context的map,name是各个feignClient,value是AnnotationConfigApplicationContext即子上下文,即每个feign客户端都有一个自己的上下文,互不影响,这点跟Ribbon一样。在feign中定义了一个FeignContext继承NamedContextFactory,来统一维护feign中各个feign客户端相互隔离的上下文。

  1. /** * 创建 feign 类实例的工厂。 它为每个客户端名称创建一个 Spring ApplicationContext, * 并从那里提取它需要的 bean */
  2. public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
  3. public FeignContext() {
  4. super(FeignClientsConfiguration.class, "feign", "feign.client.name");
  5. }
  6. }
  7. public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
  8. implements DisposableBean, ApplicationContextAware {
  9. public interface Specification {
  10. String getName();
  11. Class<?>[] getConfiguration();
  12. }
  13. /** * 维护每个feign的上下文 */
  14. private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
  15. /** * 维护每个feign的配置类 */
  16. private Map<String, C> configurations = new ConcurrentHashMap<>();
  17. private ApplicationContext parent;
  18. /** * feign默认配置类,FeignClientsConfiguration */
  19. private Class<?> defaultConfigType;
  20. private final String propertySourceName;
  21. private final String propertyName;
  22. public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
  23. String propertyName) {
  24. this.defaultConfigType = defaultConfigType;
  25. this.propertySourceName = propertySourceName;
  26. this.propertyName = propertyName;
  27. }
  28. }

可以看到Feign默认的配置类是FeignClientsConfiguration,这个在后面创建feign的各自容器时都会加载一次。
FeignContext是在上文提到的feign自动装配类中创建的

在这里插入图片描述
configurations就是前面注册的每个FeignClientSpecification bean实例。

生成动态代理类

上面说到registerFeignClient方法把FeignClientFactoryBean注入到容器,而FeignClientFactoryBean又实现了FactoryBean接口,所以在创建单例bean时是调用FeignClientFactoryBean的getObject方法来生成feignClient的代理对象(每个feign接口都有一个FeignClientFactoryBean的bean定义,存放了注解信息)

  1. public Object getObject() throws Exception {
  2. // 从spring容器获取上文提到的FeignContext对象
  3. FeignContext context = applicationContext.getBean(FeignContext.class);
  4. // 从spring容器获取Feign.Builder对象
  5. Feign.Builder builder = feign(context);
  6. // 如果@FeignClient的url属性没有配置,则走这里面的逻辑
  7. // 该例子中url = "", name = "eureka-client"
  8. // url为空说明走负载均衡
  9. if (!StringUtils.hasText(this.url)) {
  10. String url;
  11. if (!this.name.startsWith("http")) {
  12. url = "http://" + this.name;
  13. }
  14. else {
  15. url = this.name;
  16. }
  17. // url = "http://eureka-client"
  18. url += cleanPath();
  19. // 去负载均衡
  20. return loadBalance(builder, context, new HardCodedTarget<>(this.type,
  21. this.name, url));
  22. }
  23. // url有值,无需负载均衡
  24. if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
  25. this.url = "http://" + this.url;
  26. }
  27. String url = this.url + cleanPath();
  28. Client client = getOptional(context, Client.class);
  29. if (client != null) {
  30. if (client instanceof LoadBalancerFeignClient) {
  31. // not lod balancing because we have a url,
  32. // but ribbon is on the classpath, so unwrap
  33. client = ((LoadBalancerFeignClient)client).getDelegate();
  34. }
  35. builder.client(client);
  36. }
  37. Targeter targeter = get(context, Targeter.class);
  38. return targeter.target(this, builder, context, new HardCodedTarget<>(
  39. this.type, this.name, url));
  40. }

得到Feign.Builder对象,用来构建Feign对象

  1. protected Feign.Builder feign(FeignContext context) {
  2. FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
  3. Logger logger = loggerFactory.create(this.type);
  4. // 从各自的容器中获取相关组件来组建Feign
  5. Feign.Builder builder = get(context, Feign.Builder.class)
  6. // required values
  7. .logger(logger)
  8. .encoder(get(context, Encoder.class))
  9. .decoder(get(context, Decoder.class))
  10. .contract(get(context, Contract.class));
  11. // @formatter:on
  12. // 根据优先级从各自的配置文件和.properties文件中加载配置
  13. // 把application.yml的属性配置进builder中
  14. configureFeign(context, builder);
  15. return builder;
  16. }
  17. protected <T> T get(FeignContext context, Class<T> type) {
  18. // 从各自的容器中获取相关组件
  19. T instance = context.getInstance(this.name, type);
  20. if (instance == null) {
  21. throw new IllegalStateException("No bean found of type " + type + " for "
  22. + this.name);
  23. }
  24. return instance;
  25. }
  26. public <T> T getInstance(String name, Class<T> type) {
  27. // 从map中获取feignClient所属的容器,name就是@FeignClient注解的名称
  28. AnnotationConfigApplicationContext context = getContext(name);
  29. if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
  30. type).length > 0) {
  31. return context.getBean(type);
  32. }
  33. return null;
  34. }
  35. protected AnnotationConfigApplicationContext getContext(String name) {
  36. // 获取feignClient所属的容器,若没有则创建一个
  37. if (!this.contexts.containsKey(name)) {
  38. synchronized (this.contexts) {
  39. if (!this.contexts.containsKey(name)) {
  40. // 创建自己的容器,其中会加载自己的配置类和默认配置类
  41. // 所以默认配置类每个容器中都会加载,如果没有自定义配置,则使用默认的配置组件
  42. this.contexts.put(name, createContext(name));
  43. }
  44. }
  45. }
  46. return this.contexts.get(name);
  47. }

这个Feign.Builder也是从spring容器拿的,FeignClientsConfiguration中有两个,一个hystrix的,一个retryer(默认)的

在这里插入图片描述
虽说feign集成了hystrix,但看生效条件只有在配置文件中启用hystrix,才会创建第一个。
然后创建HardCodedTarget对象,调用loadBalance方法

  1. protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
  2. HardCodedTarget<T> target) {
  3. Client client = getOptional(context, Client.class);
  4. if (client != null) {
  5. // 给builder设置client
  6. builder.client(client);
  7. Targeter targeter = get(context, Targeter.class);
  8. return targeter.target(this, builder, context, target);
  9. }
  10. throw new IllegalStateException(
  11. "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
  12. }

通过容器拿到一个Client,自动装配中提到Client注入的是LoadBalancerFeignClient,里面包装了Client.Default,用来执行request请求的

在这里插入图片描述
在getObject方法的最后会调用Targeter.target方法来组装对象。通过容器拿到一个Targeter,targeter定义在FeignAutoConfiguration自动装配类中

在这里插入图片描述
因为该demo中feign本身集成了hystrix,有HystrixFeign类,所以默认是使用HystrixTargeter

  1. @Override
  2. public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
  3. Target.HardCodedTarget<T> target) {
  4. // 这个例子中没有启用hystrix,所以Builder不是用hystrix相关的,所以这里直接调用feign.target(target)
  5. if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
  6. return feign.target(target);
  7. }
  8. feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
  9. SetterFactory setterFactory = getOptional(factory.getName(), context,
  10. SetterFactory.class);
  11. if (setterFactory != null) {
  12. builder.setterFactory(setterFactory);
  13. }
  14. Class<?> fallback = factory.getFallback();
  15. if (fallback != void.class) {
  16. return targetWithFallback(factory.getName(), context, target, builder, fallback);
  17. }
  18. Class<?> fallbackFactory = factory.getFallbackFactory();
  19. if (fallbackFactory != void.class) {
  20. return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
  21. }
  22. return feign.target(target);
  23. }
  24. public <T> T target(Target<T> target) {
  25. return build().newInstance(target);
  26. }
  27. public Feign build() {
  28. SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
  29. new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
  30. logLevel, decode404);
  31. ParseHandlersByName handlersByName =
  32. new ParseHandlersByName(contract, options, encoder, decoder,
  33. errorDecoder, synchronousMethodHandlerFactory);
  34. return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
  35. }

这里才看到使用builder创建Feign实例,再利用Feign实例去创建代理对象
ReflectiveFeign

  1. public <T> T newInstance(Target<T> target) {
  2. // 根据contract,解析target里面所有方法,包含属性和各种springmvc注解解析,
  3. // 形成SynchronousMethodHandler
  4. Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  5. // method与SynchronousMethodHandler对应
  6. Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  7. List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
  8. for (Method method : target.type().getMethods()) {
  9. if (method.getDeclaringClass() == Object.class) {
  10. continue;
  11. } else if(Util.isDefault(method)) {
  12. DefaultMethodHandler handler = new DefaultMethodHandler(method);
  13. defaultMethodHandlers.add(handler);
  14. methodToHandler.put(method, handler);
  15. } else {
  16. methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
  17. }
  18. }
  19. // 创建InvocationHandler
  20. InvocationHandler handler = factory.create(target, methodToHandler);
  21. // 创建代理对象
  22. T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{ target.type()}, handler);
  23. for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
  24. defaultMethodHandler.bindTo(proxy);
  25. }
  26. return proxy;
  27. }

使用默认的InvocationHandler工厂类创建InvocationHandler实例,其中维护了Target和Map

  1. public interface InvocationHandlerFactory {
  2. InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);
  3. /** * Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a * single method. */
  4. interface MethodHandler {
  5. Object invoke(Object[] argv) throws Throwable;
  6. }
  7. static final class Default implements InvocationHandlerFactory {
  8. @Override
  9. public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
  10. return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
  11. }
  12. }
  13. }

最后使用Proxy.newProxyInstance创建代理对象,后面调用feign接口的方法就会调用到ReflectiveFeign.FeignInvocationHandler类的invoke方法。这里需要对每个定义的接口方法进行特定的处理实现,所以这里会出现一个MethodHandler的概念,就是对应方法级别的InvocationHandler。

调用接口方法

调用接口方法最终调用到FeignInvocationHandler类的invoke方法

  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  2. if ("equals".equals(method.getName())) {
  3. try {
  4. Object
  5. otherHandler =
  6. args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
  7. return equals(otherHandler);
  8. } catch (IllegalArgumentException e) {
  9. return false;
  10. }
  11. } else if ("hashCode".equals(method.getName())) {
  12. return hashCode();
  13. } else if ("toString".equals(method.getName())) {
  14. return toString();
  15. }
  16. // 交给对应方法的处理器处理
  17. return dispatch.get(method).invoke(args);
  18. }

当代理类接到执行请求时, 通过一个map分发给对应的MethodHandler执行,如此就实现了针对每个方法的个性化代理实现
SynchronousMethodHandler

  1. public Object invoke(Object[] argv) throws Throwable {
  2. // 构建一个RequestTemplate请求模板
  3. RequestTemplate template = buildTemplateFromArgs.create(argv);
  4. Retryer retryer = this.retryer.clone();
  5. while (true) {
  6. try {
  7. return executeAndDecode(template);
  8. } catch (RetryableException e) {
  9. // 重试
  10. retryer.continueOrPropagate(e);
  11. if (logLevel != Logger.Level.NONE) {
  12. logger.logRetry(metadata.configKey(), logLevel);
  13. }
  14. continue;
  15. }
  16. }
  17. }
  18. Object executeAndDecode(RequestTemplate template) throws Throwable {
  19. // 根据请求模板生成request请求对象,目前的请求url是http://eureka-client/hello?name=huang
  20. Request request = targetRequest(template);
  21. if (logLevel != Logger.Level.NONE) {
  22. logger.logRequest(metadata.configKey(), logLevel, request);
  23. }
  24. Response response;
  25. long start = System.nanoTime();
  26. try {
  27. // 执行请求并返回响应
  28. response = client.execute(request, options);
  29. // ensure the request is set. TODO: remove in Feign 10
  30. response.toBuilder().request(request).build();
  31. } catch (IOException e) {
  32. if (logLevel != Logger.Level.NONE) {
  33. logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
  34. }
  35. throw errorExecuting(request, e);
  36. }
  37. long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
  38. boolean shouldClose = true;
  39. try {
  40. if (logLevel != Logger.Level.NONE) {
  41. response =
  42. logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
  43. // ensure the request is set. TODO: remove in Feign 10
  44. response.toBuilder().request(request).build();
  45. }
  46. if (Response.class == metadata.returnType()) {
  47. if (response.body() == null) {
  48. return response;
  49. }
  50. if (response.body().length() == null ||
  51. response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
  52. shouldClose = false;
  53. return response;
  54. }
  55. // Ensure the response body is disconnected
  56. byte[] bodyData = Util.toByteArray(response.body().asInputStream());
  57. return response.toBuilder().body(bodyData).build();
  58. }
  59. if (response.status() >= 200 && response.status() < 300) {
  60. if (void.class == metadata.returnType()) {
  61. return null;
  62. } else {
  63. return decode(response);
  64. }
  65. } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
  66. return decode(response);
  67. } else {
  68. throw errorDecoder.decode(metadata.configKey(), response);
  69. }
  70. } catch (IOException e) {
  71. if (logLevel != Logger.Level.NONE) {
  72. logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
  73. }
  74. throw errorReading(request, response, e);
  75. } finally {
  76. if (shouldClose) {
  77. ensureClosed(response.body());
  78. }
  79. }
  80. }

Client底层可以是HttpUrlConnection、HttpClient也可以是Okhttp
LoadBalancerFeignClient

  1. public Response execute(Request request, Request.Options options) throws IOException {
  2. try {
  3. URI asUri = URI.create(request.url());
  4. String clientName = asUri.getHost();
  5. URI uriWithoutHost = cleanUrl(request.url(), clientName);
  6. // 创建RibbonRequest
  7. FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
  8. this.delegate, request, uriWithoutHost);
  9. IClientConfig requestConfig = getClientConfig(options, clientName);
  10. // 负载均衡器是使用ribbon的ZoneAwareLoadBalancer
  11. // 用FeignLoadBalancer包装了一层
  12. return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
  13. requestConfig).toResponse();
  14. }
  15. catch (ClientException e) {
  16. IOException io = findIOException(e);
  17. if (io != null) {
  18. throw io;
  19. }
  20. throw new RuntimeException(e);
  21. }
  22. }

看下如何选择负载均衡器

  1. private FeignLoadBalancer lbClient(String clientName) {
  2. return this.lbClientFactory.create(clientName);
  3. }

CachingSpringLoadBalancerFactory

  1. public class CachingSpringLoadBalancerFactory {
  2. private final SpringClientFactory factory;
  3. private LoadBalancedRetryFactory loadBalancedRetryFactory = null;
  4. private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
  5. public CachingSpringLoadBalancerFactory(SpringClientFactory factory) {
  6. this.factory = factory;
  7. }
  8. public CachingSpringLoadBalancerFactory(SpringClientFactory factory, LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) {
  9. this.factory = factory;
  10. this.loadBalancedRetryFactory = loadBalancedRetryPolicyFactory;
  11. }
  12. public FeignLoadBalancer create(String clientName) {
  13. // 根据clientName从缓存中获取相对应的负载均衡器
  14. FeignLoadBalancer client = this.cache.get(clientName);
  15. if(client != null) {
  16. return client;
  17. }
  18. IClientConfig config = this.factory.getClientConfig(clientName);
  19. // 从SpringClientFactory中获取负载均衡器,该SpringClientFactory是ribbon的,
  20. // 根据feign的FeignContext类似,都继承了NamedContextFactory,
  21. // 缓存了每个客户端的spring容器,所以也就相当于从各自的spring容器中获取ILoadBalancer实例
  22. ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
  23. ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
  24. // 使用FeignLoadBalancer包装后放到缓存中。如果启用了重试,则用RetryableFeignLoadBalancer
  25. client = loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
  26. loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
  27. this.cache.put(clientName, client);
  28. return client;
  29. }
  30. }

可以看到也是通过ILoadBalancer来作为负载均衡器,而ILoadBalancer是Ribbon非常核心的一个组件,在初始化的时候会从EurekaClient获取服务列表,EurekaClient中获取服务实例,然后通过IL oadBalancer中的IRule组件的负载均衡算法,在同一个服务的多个服务实例中choose挑选一个 服务出来,作为本次请求的服务实例目标。看下如何通过负载均衡器选择一个服务后执行请求

  1. public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
  2. LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
  3. try {
  4. // 在submit中会选择一个具体的服务
  5. return command.submit(
  6. new ServerOperation<T>() {
  7. // 选择到一个服务后会回调该方法
  8. @Override
  9. public Observable<T> call(Server server) {
  10. // 替换URI
  11. URI finalUri = reconstructURIWithServer(server, request.getUri());
  12. S requestForServer = (S) request.replaceUri(finalUri);
  13. try {
  14. // 执行请求
  15. return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
  16. }
  17. catch (Exception e) {
  18. return Observable.error(e);
  19. }
  20. }
  21. })
  22. .toBlocking()
  23. .single();
  24. } catch (Exception e) {
  25. Throwable t = e.getCause();
  26. if (t instanceof ClientException) {
  27. throw (ClientException) t;
  28. } else {
  29. throw new ClientException(e);
  30. }
  31. }
  32. }

进入到submit看看

  1. public Observable<T> submit(final ServerOperation<T> operation) {
  2. final ExecutionInfoContext context = new ExecutionInfoContext();
  3. // .........
  4. Observable<T> o =
  5. (server == null ? selectServer() : Observable.just(server))
  6. .concatMap(new Func1<Server, Observable<T>>() {
  7. //........
  8. });
  9. // .......
  10. }

其它代码也看不懂,咱就看selectServer方法,看名称应该是选择服务

  1. private Observable<Server> selectServer() {
  2. return Observable.create(new OnSubscribe<Server>() {
  3. @Override
  4. public void call(Subscriber<? super Server> next) {
  5. try {
  6. Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);
  7. next.onNext(server);
  8. next.onCompleted();
  9. } catch (Exception e) {
  10. next.onError(e);
  11. }
  12. }
  13. });
  14. }

再跟进getServerFromLoadBalancer

  1. public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
  2. String host = null;
  3. int port = -1;
  4. if (original != null) {
  5. host = original.getHost();
  6. }
  7. if (original != null) {
  8. Pair<String, Integer> schemeAndPort = deriveSchemeAndPortFromPartialUri(original);
  9. port = schemeAndPort.second();
  10. }
  11. // Various Supported Cases
  12. // The loadbalancer to use and the instances it has is based on how it was registered
  13. // In each of these cases, the client might come in using Full Url or Partial URL
  14. // 获取负载均衡器
  15. ILoadBalancer lb = getLoadBalancer();
  16. if (host == null) {
  17. // Partial URI or no URI Case
  18. // well we have to just get the right instances from lb - or we fall back
  19. if (lb != null){
  20. // 负载均衡选择一个服务
  21. Server svc = lb.chooseServer(loadBalancerKey);
  22. if (svc == null){
  23. throw new ClientException(ClientException.ErrorType.GENERAL,
  24. "Load balancer does not have available server for client: "
  25. + clientName);
  26. }
  27. host = svc.getHost();
  28. if (host == null){
  29. throw new ClientException(ClientException.ErrorType.GENERAL,
  30. "Invalid Server for :" + svc);
  31. }
  32. logger.debug("{} using LB returned Server: {} for request {}", new Object[]{ clientName, svc, original});
  33. return svc;
  34. } else {
  35. // No Full URL - and we dont have a LoadBalancer registered to
  36. // obtain a server
  37. // if we have a vipAddress that came with the registration, we
  38. // can use that else we
  39. // bail out
  40. if (vipAddresses != null && vipAddresses.contains(",")) {
  41. throw new ClientException(
  42. ClientException.ErrorType.GENERAL,
  43. "Method is invoked for client " + clientName + " with partial URI of ("
  44. + original
  45. + ") with no load balancer configured."
  46. + " Also, there are multiple vipAddresses and hence no vip address can be chosen"
  47. + " to complete this partial uri");
  48. } else if (vipAddresses != null) {
  49. try {
  50. Pair<String,Integer> hostAndPort = deriveHostAndPortFromVipAddress(vipAddresses);
  51. host = hostAndPort.first();
  52. port = hostAndPort.second();
  53. } catch (URISyntaxException e) {
  54. throw new ClientException(
  55. ClientException.ErrorType.GENERAL,
  56. "Method is invoked for client " + clientName + " with partial URI of ("
  57. + original
  58. + ") with no load balancer configured. "
  59. + " Also, the configured/registered vipAddress is unparseable (to determine host and port)");
  60. }
  61. } else {
  62. throw new ClientException(
  63. ClientException.ErrorType.GENERAL,
  64. this.clientName
  65. + " has no LoadBalancer registered and passed in a partial URL request (with no host:port)."
  66. + " Also has no vipAddress registered");
  67. }
  68. }
  69. } else {
  70. // Full URL Case
  71. // This could either be a vipAddress or a hostAndPort or a real DNS
  72. // if vipAddress or hostAndPort, we just have to consult the loadbalancer
  73. // but if it does not return a server, we should just proceed anyways
  74. // and assume its a DNS
  75. // For restClients registered using a vipAddress AND executing a request
  76. // by passing in the full URL (including host and port), we should only
  77. // consult lb IFF the URL passed is registered as vipAddress in Discovery
  78. boolean shouldInterpretAsVip = false;
  79. if (lb != null) {
  80. shouldInterpretAsVip = isVipRecognized(original.getAuthority());
  81. }
  82. if (shouldInterpretAsVip) {
  83. Server svc = lb.chooseServer(loadBalancerKey);
  84. if (svc != null){
  85. host = svc.getHost();
  86. if (host == null){
  87. throw new ClientException(ClientException.ErrorType.GENERAL,
  88. "Invalid Server for :" + svc);
  89. }
  90. logger.debug("using LB returned Server: {} for request: {}", svc, original);
  91. return svc;
  92. } else {
  93. // just fall back as real DNS
  94. logger.debug("{}:{} assumed to be a valid VIP address or exists in the DNS", host, port);
  95. }
  96. } else {
  97. // consult LB to obtain vipAddress backed instance given full URL
  98. //Full URL execute request - where url!=vipAddress
  99. logger.debug("Using full URL passed in by caller (not using load balancer): {}", original);
  100. }
  101. }
  102. // end of creating final URL
  103. if (host == null){
  104. throw new ClientException(ClientException.ErrorType.GENERAL,"Request contains no HOST to talk to");
  105. }
  106. // just verify that at this point we have a full URL
  107. return new Server(host, port);
  108. }

这段代码关键就是lb.chooseServer(loadBalancerKey)从负载均衡器中选择一个服务,可以看到的是这里获取到了之前构造好的ZoneAwareLoadBalancer然后调用chooseServer方法获取server,这个是跟Ribbon 中是一样的流程。获取到了server 后,会回调先前 executeWithLoadBalancer 方法里构造的 ServerOperation 的 call 方法,执行 AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig) 进行最后的调用

在这里插入图片描述
这里AbstractLoadBalancerAwareClient实例就是FeignLoadBalancer

  1. public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
  2. throws IOException {
  3. Request.Options options;
  4. if (configOverride != null) {
  5. RibbonProperties override = RibbonProperties.from(configOverride);
  6. options = new Request.Options(
  7. override.connectTimeout(this.connectTimeout),
  8. override.readTimeout(this.readTimeout));
  9. }
  10. else {
  11. options = new Request.Options(this.connectTimeout, this.readTimeout);
  12. }
  13. Response response = request.client().execute(request.toRequest(), options);
  14. return new RibbonResponse(request.getUri(), response);
  15. }

由于是demo,所以这里client使用的是默认的Client.Default

  1. public Response execute(Request request, Options options) throws IOException {
  2. HttpURLConnection connection = convertAndSend(request, options);
  3. return convertResponse(connection).toBuilder().request(request).build();
  4. }

可以看到最后使用的是HttpURLConnection来作为Http客户端。

参考资料

Feign源码解析系列-核心初始化
spring-cloud-openfeign 源码解析

发表评论

表情:
评论列表 (有 0 条评论,268人围观)

还没有评论,来说两句吧...

相关阅读

    相关 HashMap

    来不及整理电子版,先献丑把笔记本拍几张,随后整理。 有人问,什么年代了,还手写笔记,哈哈,如果不亲自手写一遍,我是真心记不住。很多API不用知道工作原理 一样可以使用,所以

    相关 HashMap

    源码博客相关博客写了那么多,突然想起来都没有写一篇我们日常开发中最常见的HashMap,今天简单补一下! HashMap简介: `HashMap` 是应用更加广泛的哈希

    相关 hashMap

    源码来自jdk:1.8,和其他jdk版本可能有少许差异。 一.hashMap的实现原理     hashMap底层是一个有Node组成的数组,每个Node都有一个key