SpringCloud原理--FeignClient

原文网址:SpringCloud原理--FeignClient_IT利刃出鞘的博客-CSDN博客

简介

说明

        本文介绍SpringCloud的FeignClient的原理。

Feign服务调用的工作原理可以总结为以下几个步骤

  1. 首先通过@EnableFeignCleints注解开启FeignCleint。
  2. 根据Feign的规则实现接口,添加@FeignCleint注解。程序启动后,会扫描所有有@FeignCleint的类,并将这些信息注入到ioc容器中。
  3. 注入时从FeignClientFactoryBean.class获取FeignClient
  4. 当接口的方法被调用时,通过jdk的代理,来生成具体的RequesTemplate,RequesTemplate生成http的Request
  5. Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
  6. Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。

整体流程图

默认配置

Decoder: ResponseEntityDecoder(对SpringDecoder的封装)
Encoder: SpringEncoder
Logger: Slf4jLogger
Contract: SpringMvcContract
Feign.Builder: Feign.Builder(配置了hystrix则为HystrixFeign.Builder)

配置类

spring-cloud-netflix-core的FeignClientsConfiguration.class:


   
   
  1. @Configuration
  2. public class FeignClientsConfiguration {
  3. @Autowired
  4. private ObjectFactory<HttpMessageConverters> messageConverters;
  5. @Autowired(required = false)
  6. private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
  7. @Autowired(required = false)
  8. private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
  9. @Autowired(required = false)
  10. private Logger logger;
  11. @Bean
  12. @ConditionalOnMissingBean
  13. public Decoder feignDecoder () {
  14. return new OptionalDecoder(
  15. new ResponseEntityDecoder( new SpringDecoder( this.messageConverters)));
  16. }
  17. @Bean
  18. @ConditionalOnMissingBean
  19. @ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
  20. public Encoder feignEncoder () {
  21. return new SpringEncoder( this.messageConverters);
  22. }
  23. @Bean
  24. @ConditionalOnClass(name = "org.springframework.data.domain.Pageable")
  25. @ConditionalOnMissingBean
  26. public Encoder feignEncoderPageable () {
  27. return new PageableSpringEncoder( new SpringEncoder( this.messageConverters));
  28. }
  29. @Bean
  30. @ConditionalOnMissingBean
  31. public Contract feignContract (ConversionService feignConversionService) {
  32. return new SpringMvcContract( this.parameterProcessors, feignConversionService);
  33. }
  34. @Bean
  35. public FormattingConversionService feignConversionService () {
  36. FormattingConversionService conversionService = new DefaultFormattingConversionService();
  37. for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) {
  38. feignFormatterRegistrar.registerFormatters(conversionService);
  39. }
  40. return conversionService;
  41. }
  42. @Bean
  43. @ConditionalOnMissingBean
  44. public Retryer feignRetryer () {
  45. return Retryer.NEVER_RETRY;
  46. }
  47. @Bean
  48. @Scope("prototype")
  49. @ConditionalOnMissingBean
  50. public Feign.Builder feignBuilder (Retryer retryer) {
  51. return Feign.builder().retryer(retryer);
  52. }
  53. @Bean
  54. @ConditionalOnMissingBean(FeignLoggerFactory.class)
  55. public FeignLoggerFactory feignLoggerFactory () {
  56. return new DefaultFeignLoggerFactory( this.logger);
  57. }
  58. @Bean
  59. @ConditionalOnClass(name = "org.springframework.data.domain.Page")
  60. public Module pageJacksonModule () {
  61. return new PageJacksonModule();
  62. }
  63. @Configuration
  64. @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
  65. protected static class HystrixFeignConfiguration {
  66. @Bean
  67. @Scope("prototype")
  68. @ConditionalOnMissingBean
  69. @ConditionalOnProperty(name = "feign.hystrix.enabled")
  70. public Feign.Builder feignHystrixBuilder () {
  71. return HystrixFeign.builder();
  72. }
  73. }
  74. }

一、启动时扫描

其他网址

Feign源码分析之EnableFeignClients - 克虏伯的个人空间 - OSCHINA - 中文开源技术交流社区

1.@EnableFeignClients

        开启Spring Cloud Feign功能是实现的。程序启动时,首先会检测是否有@EnableFeignClients注解,如果有,则会开启扫描功能,并扫描被@FeignClient注解修饰的接口。接下来,我们从@EnableFeignClients注解入手,分析Feign远程调用服务的工作原理。

查看@EnableFeignClients注解的源码,具体代码如下所示。


   
   
  1. package org.springframework.cloud.openfeign;
  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. Class<?>[] defaultConfiguration() default {};
  11. Class<?>[] clients() default {};
  12. }

2.FeignClientsRegistrar

从上述源码可以看出,@EnableFeignClients注解引入了FeignClientsRegistrar类,跟踪该类源码,发现其内部定义了一个registerBeanDefinitions方法,具体代码如下所示。


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

registerDefaultConfiguration()

        注册所有@EnableFeignClients的defaultConfiguration属性标记的配置类数组(配置类数组:用@Configuration标记的配置类,可自定义feign.codec.Decoder、feign.codec.Encoder、feign.Contract)

registerFeignClients()

        扫描并注册所有@FeignClient注解的接口。

这里,我们重点查看registerFeignClients()方法内部实现细节。

registerFeignClients()的源码如下所示。


   
   
  1. public void registerFeignClients (AnnotationMetadata metadata,
  2. BeanDefinitionRegistry registry) {
  3. ClassPathScanningCandidateComponentProvider scanner = getScanner(); //1
  4. scanner.setResourceLoader( this.resourceLoader);
  5. Set<String> basePackages;
  6. Map<String, Object> attrs = metadata
  7. .getAnnotationAttributes(EnableFeignClients.class.getName()); //2
  8. AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
  9. FeignClient.class); //3
  10. final Class<?>[] clients = attrs == null ? null
  11. : (Class<?>[]) attrs.get( "clients");
  12. if (clients == null || clients.length == 0) { //4
  13. scanner.addIncludeFilter(annotationTypeFilter);
  14. basePackages = getBasePackages(metadata);
  15. }
  16. else { //5
  17. final Set<String> clientClasses = new HashSet<>();
  18. basePackages = new HashSet<>();
  19. for (Class<?> clazz : clients) {
  20. basePackages.add(ClassUtils.getPackageName(clazz));
  21. clientClasses.add(clazz.getCanonicalName());
  22. }
  23. AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
  24. @Override
  25. protected boolean match (ClassMetadata metadata) {
  26. String cleaned = metadata.getClassName().replaceAll( "\\$", ".");
  27. return clientClasses.contains(cleaned);
  28. }
  29. };
  30. scanner.addIncludeFilter(
  31. new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
  32. }
  33. for (String basePackage : basePackages) {
  34. Set<BeanDefinition> candidateComponents = scanner
  35. .findCandidateComponents(basePackage);
  36. for (BeanDefinition candidateComponent : candidateComponents) {
  37. if (candidateComponent instanceof AnnotatedBeanDefinition) {
  38. // verify annotated class is an interface
  39. AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
  40. AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
  41. Assert.isTrue(annotationMetadata.isInterface(),
  42. "@FeignClient can only be specified on an interface");
  43. Map<String, Object> attributes = annotationMetadata
  44. .getAnnotationAttributes(
  45. FeignClient.class.getCanonicalName());
  46. String name = getClientName(attributes);
  47. registerClientConfiguration(registry, name,
  48. attributes.get( "configuration")); //6
  49. registerFeignClient(registry, annotationMetadata, attributes); //7
  50. }
  51. }
  52. }
  53. }
  1. 1处获取ClassPathScanner,用于扫描类路径
  2. 2处获取EnableFeignClients的所有属性
  3. 3处构造一个AnnotationTypeFilter,构造方法参数是FeignClient,这个用于过滤出只含有FeignClient的类
  4. 获得EnableFeignClients的clients属性值,4处如果是空,则获得EnableFeignClients所在的package路径(如果没有设置basePackageClasses)
  5. 5处,EnableFeignClients的clients属性不是空,则遍历,放入集合中,同时获取client所在的package路面,加入到basePacakges中;构造AbstractClassTestingTypeFilter,这是增加一个过滤条件,即标FeignClient注解的接口,必须在EnableFeignClients的clients中
  6. 遍历basePackages,获取每个package下的符合条件的类,得到对应的beanDefinition,6处得到FeignClient的configuration值,通过FeignClientSpecification其注册到spring容器中,有意思的是这里检查了FeignClient注解的类须是接口,不然会报错。
  7. 7处将FeignClient注解的接口封装到FeignClientFactoryBean中。FactoryBean大家懂的,Spring中接口都封装到这个里面。

综述

        上述代码中,首先定义了一个基于classpath的组件扫描器,然后组件扫描器会根据指定的扫描位置和@EnableFeignClients注解属性找到开发人员定义的所有Feign客户端,也就是所有添加了@FeignClient注解的所有接口,最后将注册Feign客户端的动作交给registerFeignClient()方法完成。

二、registerFeignClient()

registerFeignClient()方法的源码如下所示。


   
   
  1. private void registerFeignClient (BeanDefinitionRegistry registry,
  2. AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
  3. String className = annotationMetadata.getClassName(); //1
  4. BeanDefinitionBuilder definition = BeanDefinitionBuilder
  5. .genericBeanDefinition(FeignClientFactoryBean.class); //2
  6. validate(attributes);
  7. definition.addPropertyValue( "url", getUrl(attributes)); //3
  8. definition.addPropertyValue( "path", getPath(attributes));
  9. String name = getName(attributes);
  10. definition.addPropertyValue( "name", name);
  11. String contextId = getContextId(attributes);
  12. definition.addPropertyValue( "contextId", contextId);
  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 = contextId + "FeignClient";
  19. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
  20. boolean primary = (Boolean) attributes.get( "primary"); // has a default, won't be
  21. // null
  22. beanDefinition.setPrimary(primary);
  23. String qualifier = getQualifier(attributes);
  24. if (StringUtils.hasText(qualifier)) {
  25. alias = qualifier;
  26. }
  27. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
  28. new String[] { alias });
  29. // 动态注册
  30. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  31. }

1.1处:获取使用@FeignClient注解修饰的类名

2.2处:创建一个BeanDefinitionBuilder,指定待创建的Bean是FeignClientFactoryBean

3.3处:将@FeignClient注解上的属性值与FeignClientFactoryBean.class关联起来。

综述:

        首先会将使用@FeignClient注解修饰的类名及其注解信息获取出来,赋值给BeanDefinitionBuilder(此builder指定待创建的Bean是FeignClientFactoryBean),然后根据BeanDefinitionBuilder得到BeanDefinition,最后将BeanDefinition注入IoC容器。

三、注入时从FeignClientFactoryBean.class获取

其他网址

Feign源码分析之FeignClientFactoryBean - 克虏伯的个人空间 - OSCHINA - 中文开源技术交流社区
Feign详细构建过程及自定义扩展 - 极术社区 - 连接 AIoT 开发者与生态服务

FeignClientFactoryBean.class实现的接口

FactoryBean接口:getObject、getObjectType、isSingleton方法;

InitializingBean接口:afterPropertiesSet方法;

ApplicationContextAware:setApplicationContext方法。


   
   
  1. class FeignClientFactoryBean
  2. implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
  3. //xxx
  4. }

FactoryBean接口

实现接口 FactoryBean#getObject()后,由spring框架生产FeignClient(FeignClientFactoryBean通过Targeter生产FeignClient)。

FeignClientFactoryBean.class  //org.springframework.cloud.openfeign.FeignClientFactoryBean.java

以下代码片段去除了次要代码。 


   
   
  1. package org.springframework.cloud.openfeign;
  2. class FeignClientFactoryBean
  3. implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
  4. private Class<?> type;
  5. private String name;
  6. private ApplicationContext applicationContext;
  7. @Override
  8. public Object getObject () throws Exception {
  9. return getTarget();
  10. }
  11. /**
  12. * @param <T> the target type of the Feign client
  13. * @return a {@link Feign} client created with the specified data and the context information
  14. */
  15. <T> T getTarget () {
  16. // FeignContext是在FeignAutoConfiguration中注册到Spring容器中的。详见下方:src1
  17. FeignContext context = applicationContext.getBean(FeignContext.class);
  18. // 从FeignContext中获取Feign.Builder。(从Spring容器获取。bean定义见本文“简介”=> 整体配置
  19. Feign. Builder builder = feign(context);
  20. if (!StringUtils.hasText(url)) {
  21. if (!name.startsWith( "http")) {
  22. url = "http://" + name;
  23. }
  24. else {
  25. url = name;
  26. }
  27. url += cleanPath();
  28. return (T) loadBalance(builder, context,
  29. new HardCodedTarget<>(type, name, url));
  30. }
  31. if (StringUtils.hasText(url) && !url.startsWith( "http")) {
  32. url = "http://" + url;
  33. }
  34. String url = this.url + cleanPath();
  35. Client client = getOptional(context, Client.class);
  36. if (client != null) {
  37. if (client instanceof LoadBalancerFeignClient) {
  38. // not load balancing because we have a url,
  39. // but ribbon is on the classpath, so unwrap
  40. client = ((LoadBalancerFeignClient) client).getDelegate();
  41. }
  42. if (client instanceof FeignBlockingLoadBalancerClient) {
  43. // not load balancing because we have a url,
  44. // but Spring Cloud LoadBalancer is on the classpath, so unwrap
  45. client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
  46. }
  47. builder.client(client);
  48. }
  49. Targeter targeter = get(context, Targeter.class);
  50. return (T) targeter.target( this, builder, context,
  51. new HardCodedTarget<>(type, name, url));
  52. }
  53. @Override
  54. public Class<?> getObjectType() {
  55. return type;
  56. }
  57. @Override
  58. public boolean isSingleton () {
  59. return true;
  60. }
  61. @Override
  62. public void setApplicationContext (ApplicationContext context) throws BeansException {
  63. this.applicationContext = context;
  64. }
  65. @Override
  66. public void afterPropertiesSet () {
  67. Assert.hasText(contextId, "Context id must be set");
  68. Assert.hasText(name, "Name must be set");
  69. }
  70. }

setType被调用的位置:FeignClientBuilder.Builder#Builder(final ApplicationContext applicationContext, final Class<T> type, final String name)  //构造函数


   
   
  1. private Builder (final ApplicationContext applicationContext, final Class<T> type,
  2. final String name) {
  3. this.feignClientFactoryBean = new FeignClientFactoryBean();
  4. this.feignClientFactoryBean.setApplicationContext(applicationContext);
  5. this.feignClientFactoryBean.setType(type);
  6. this.feignClientFactoryBean.setName(FeignClientsRegistrar.getName(name));
  7. this.feignClientFactoryBean.setContextId(FeignClientsRegistrar.getName(name));
  8. // preset default values - these values resemble the default values on the
  9. // FeignClient annotation
  10. this.url( "").path( "").decode404( false).fallback( void.class)
  11. .fallbackFactory( void.class);
  12. }

src1

将Spring容器所有的FeignClientSpecification放入到FeignContext中,FeignClientSpecification在Feign源码分析之EnableFeignClients中讲过,即EnableFeignClients的defaultConfiguration。


   
   
  1. public class FeignAutoConfiguration {
  2. @Autowired(required = false)
  3. private List<FeignClientSpecification> configurations = new ArrayList<>();
  4. @Bean
  5. public HasFeatures feignFeature () {
  6. return HasFeatures.namedFeature( "Feign", Feign.class);
  7. }
  8. @Bean
  9. public FeignContext feignContext () {
  10. FeignContext context = new FeignContext();
  11. context.setConfigurations( this.configurations);
  12. return context;
  13. }
  14. }

Targeter

Targeter有两个实现类:HystrixTargeter和DefaultTargeter。配置使用HystrixFeign的方法:Feign系列--配置_feiying0canglang的博客-CSDN博客

  1. feign.hystrix.HystrixFeign类存在时,将 HystrixTargeter 注册为 Targeter 类型的 bean
  2. feign.hystrix.HystrixFeign类不存在时,使用 DefaultTargeter 。
  3. 看起来似乎可以使用自定义的Targeter代替Hystrix或默认的,这样就可以自定义各种功能了。实际上不行,因为 Targeter 是 package 访问级别的。

生成Feign

第三部分的FactoryBean接口处说到,FeignClientFactoryBean.class#getTarget最后调用到的是


   
   
  1. Targeter targeter = get(context, Targeter.class);
  2. return (T) targeter.target( this, builder, context,
  3.          new HardCodedTarget<>( this.type, this.name, url));

 本处以HystrixTargeter为例进行分析  // HystrixTargeter


   
   
  1. class HystrixTargeter implements Targeter {
  2. @Override
  3. public <T> T target (FeignClientFactoryBean factory, Feign.Builder feign,
  4. FeignContext context, Target.HardCodedTarget<T> 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,
  17. fallback);
  18. }
  19. Class<?> fallbackFactory = factory.getFallbackFactory();
  20. if (fallbackFactory != void.class) {
  21. return targetWithFallbackFactory(factory.getName(), context, target, builder,
  22. fallbackFactory);
  23. }
  24. //可发现:若同时定义fallBack和fallBackFactory,会用fallBack(fallBackFactory无效)
  25. return feign.target(target);
  26. }
  27. //其他代码
  28. }

追踪"feign.target(target)",发现在feign包的Feign.Builder里(HystrixFeign.Builder没有覆写此方法):

feign.target(target):  // Feign.Builder#target


   
   
  1. public abstract class Feign {
  2. public static class Builder {
  3. private InvocationHandlerFactory invocationHandlerFactory =
  4. new InvocationHandlerFactory.Default();
  5. public <T> T target (Target<T> target) {
  6. return build().newInstance(target);
  7. }
  8. public Feign build () {
  9. SynchronousMethodHandler. Factory synchronousMethodHandlerFactory =
  10. new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
  11. logLevel, decode404, closeAfterDecode, propagationPolicy);
  12. ParseHandlersByName handlersByName =
  13. new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
  14. errorDecoder, synchronousMethodHandlerFactory);
  15. return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
  16. }
  17. public Builder invocationHandlerFactory (InvocationHandlerFactory invocationHandlerFactory) {
  18. this.invocationHandlerFactory = invocationHandlerFactory;
  19. return this;
  20. }
  21. // xxx
  22. }
  23. // xxx
  24. }

build().newInstance(target):  // ReflectiveFeign#newInstance


   
   
  1. public class ReflectiveFeign extends Feign {
  2. private final ParseHandlersByName targetToHandlersByName;
  3. private final InvocationHandlerFactory factory;
  4. private final QueryMapEncoder queryMapEncoder;
  5. ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
  6. QueryMapEncoder queryMapEncoder) {
  7. this.targetToHandlersByName = targetToHandlersByName;
  8. this.factory = factory;
  9. this.queryMapEncoder = queryMapEncoder;
  10. }
  11. public <T> T newInstance (Target<T> target) {
  12. Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  13. Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  14. List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
  15. for (Method method : target.type().getMethods()) {
  16. if (method.getDeclaringClass() == Object.class) {
  17. continue;
  18. } else if (Util.isDefault(method)) {
  19. DefaultMethodHandler handler = new DefaultMethodHandler(method);
  20. defaultMethodHandlers.add(handler);
  21. methodToHandler.put(method, handler);
  22. } else {
  23. methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
  24. }
  25. }
  26. InvocationHandler handler = factory.create(target, methodToHandler);
  27. T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
  28. new Class<?>[] {target.type()}, handler);
  29. for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
  30. defaultMethodHandler.bindTo(proxy);
  31. }
  32. return proxy;
  33. }
  34. // xxx
  35. }

代理类(feign的核心)

上边有一行:InvocationHandler handler = factory.create(target, methodToHandler);

这个factory是什么呢?见:上边Feign.Builder#target

默认是InvocationHandlerFactory.Default():


   
   
  1. public interface InvocationHandlerFactory {
  2. InvocationHandler create (Target target, Map<Method, MethodHandler> dispatch);
  3. interface MethodHandler {
  4. Object invoke (Object[] argv) throws Throwable;
  5. }
  6. static final class Default implements InvocationHandlerFactory {
  7. @Override
  8. public InvocationHandler create (Target target, Map<Method, MethodHandler> dispatch) {
  9. return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
  10. }
  11. }
  12. }

但是,可以被覆盖的,若使用了hystrix,则会被覆盖:  //HystrixFeign.Builder#build


   
   
  1. Feign build (final FallbackFactory<?> nullableFallbackFactory) {
  2. super.invocationHandlerFactory( new InvocationHandlerFactory() {
  3. @Override
  4. public InvocationHandler create (Target target, Map<Method, MethodHandler> dispatch) {
  5. return new HystrixInvocationHandler(target, dispatch, setterFactory,
  6. nullableFallbackFactory);
  7. }
  8. });
  9. super.contract( new HystrixDelegatingContract(contract));
  10. return super.build();
  11. }

所以,使用了hystrix后的InvocationHandler是: HystrixInvocationHandler。

继续跟踪:ReflectiveFeign#newInstance确定了InvocationHandler后的操作:Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler);

结论

注入@FeignClient标记的类时,实际注入的是这个类的代理类,它的代理是:HystrixInvocationHandler

Targeter自动配置类


   
   
  1. @Configuration
  2. @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
  3. protected static class HystrixFeignTargeterConfiguration {
  4. @Bean
  5. @ConditionalOnMissingBean
  6. public Targeter feignTargeter () {
  7. return new HystrixTargeter();
  8. }
  9. }

   
   
  1. @Configuration
  2. @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
  3. protected static class DefaultFeignTargeterConfiguration {
  4. @Bean
  5. @ConditionalOnMissingBean
  6. public Targeter feignTargeter () {
  7. return new DefaultTargeter();
  8. }
  9. }

其他

添加拦截器

@FeignClient注解的类可以实现RequestInterceptor接口,之后交给Spring容器,feign会自动加上这个拦截器,这个的实现也在FeignClientFactoryBean#configureUsingConfiguration方法中,如下


   
   
  1. Map<String, RequestInterceptor> requestInterceptors = context
  2. .getInstances( this.contextId, RequestInterceptor.class);
  3. if (requestInterceptors != null) {
  4. builder.requestInterceptors(requestInterceptors.values());
  5. }

四、拦截@FeignClient修饰接口中的方法

        在本文:第三部分=> Targeter=> 结论  已经分析到,@FeignClient注解的类会被加入动态代理:HystrixInvocationHandler。本部分就来分析这个代理具体做了什么。 

//对动态代理不太熟悉的,可看下此文章:Java设计模式系列--静态代理与动态代理_feiying0canglang的博客-CSDN博客

 HystrixInvocationHandler#invoke


   
   
  1. final class HystrixInvocationHandler implements InvocationHandler {
  2. private final Map<Method, MethodHandler> dispatch;
  3. // 其他代码
  4. HystrixInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch,
  5. SetterFactory setterFactory, FallbackFactory<?> fallbackFactory) {
  6. this.dispatch = checkNotNull(dispatch, "dispatch");
  7. // 其他代码
  8. }
  9. @Override
  10. public Object invoke (final Object proxy, final Method method, final Object[] args)
  11. throws Throwable {
  12. // 其他代码
  13. HystrixCommand<Object> hystrixCommand =
  14. new HystrixCommand<Object>(setterMethodMap.get(method)) {
  15. @Override
  16. protected Object run () throws Exception {
  17. try {
  18. return HystrixInvocationHandler. this.dispatch.get(method).invoke(args);
  19. } catch (Exception e) {
  20. throw e;
  21. } catch (Throwable t) {
  22. throw (Error) t;
  23. }
  24. }
  25. @Override
  26. protected Object getFallback () {
  27. // 其他代码
  28. }
  29. };
  30. // 其他代码
  31. return hystrixCommand.execute();
  32. }

重点:HystrixInvocationHandler.this.dispatch.get(method).invoke(args)。而dispatch是构造函数传进来的,继续追踪

调用HystrixInvocationHandler构造函数:HystrixFeign.Builder#build


   
   
  1. Feign build (final FallbackFactory<?> nullableFallbackFactory) {
  2. super.invocationHandlerFactory( new InvocationHandlerFactory() {
  3. @Override
  4. public InvocationHandler create (Target target,
  5. Map<Method, MethodHandler> dispatch) {
  6. return new HystrixInvocationHandler(target, dispatch, setterFactory,
  7. nullableFallbackFactory);
  8. }
  9. });
  10. super.contract( new HystrixDelegatingContract(contract));
  11. return super.build();
  12. }

 create里边调用了构造方法将dispatch放进去的,追踪调用create的位置

调用create方法:ReflectiveFeign#newInstance


   
   
  1. public class ReflectiveFeign extends Feign {
  2. // 这是一个静态内部类,在下边
  3. private final ParseHandlersByName targetToHandlersByName;
  4. private final InvocationHandlerFactory factory;
  5. @Override
  6. public <T> T newInstance (Target<T> target) {
  7. Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  8. Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  9. List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
  10. for (Method method : target.type().getMethods()) {
  11. if (method.getDeclaringClass() == Object.class) {
  12. continue;
  13. } else if (Util.isDefault(method)) {
  14. DefaultMethodHandler handler = new DefaultMethodHandler(method);
  15. defaultMethodHandlers.add(handler);
  16. methodToHandler.put(method, handler);
  17. } else {
  18. // 重点。追踪nameToHandler
  19. methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
  20. }
  21. }
  22. InvocationHandler handler = factory.create(target, methodToHandler);
  23. T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
  24. new Class<?>[] {target.type()}, handler);
  25. for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
  26. defaultMethodHandler.bindTo(proxy);
  27. }
  28. return proxy;
  29. }
  30. static final class ParseHandlersByName {
  31. private final Contract contract;
  32. private final Options options;
  33. private final Encoder encoder;
  34. private final Decoder decoder;
  35. private final ErrorDecoder errorDecoder;
  36. private final QueryMapEncoder queryMapEncoder;
  37. private final SynchronousMethodHandler.Factory factory;
  38. ParseHandlersByName(
  39. Contract contract,
  40. Options options,
  41. Encoder encoder,
  42. Decoder decoder,
  43. QueryMapEncoder queryMapEncoder,
  44. ErrorDecoder errorDecoder,
  45. SynchronousMethodHandler.Factory factory) {
  46. this.contract = contract;
  47. this.options = options;
  48. this.factory = factory;
  49. this.errorDecoder = errorDecoder;
  50. this.queryMapEncoder = queryMapEncoder;
  51. this.encoder = checkNotNull(encoder, "encoder");
  52. this.decoder = checkNotNull(decoder, "decoder");
  53. }
  54. public Map<String, MethodHandler> apply (Target key) {
  55. List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
  56. Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
  57. for (MethodMetadata md : metadata) {
  58. BuildTemplateByResolvingArgs buildTemplate;
  59. if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
  60. buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
  61. } else if (md.bodyIndex() != null) {
  62. buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
  63. } else {
  64. buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
  65. }
  66. // 重点。追踪factory.create(即:SynchronousMethodHandler.Factory#create)
  67. result.put(md.configKey(), factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
  68. }
  69. return result;
  70. }
  71. }
  72. }

SynchronousMethodHandler.Factory#create


   
   
  1. import feign.InvocationHandlerFactory.MethodHandler;
  2. final class SynchronousMethodHandler implements MethodHandler {
  3. private final MethodMetadata metadata;
  4. private final Target<?> target;
  5. private final Client client;
  6. private final RequestTemplate.Factory buildTemplateFromArgs;
  7. private final List<RequestInterceptor> requestInterceptors;
  8. //其他代码
  9. private SynchronousMethodHandler (Target<?> target, Client client, Retryer retryer,
  10. List<RequestInterceptor> requestInterceptors, Logger logger,
  11. Logger.Level logLevel, MethodMetadata metadata,
  12. RequestTemplate.Factory buildTemplateFromArgs, Options options,
  13. Decoder decoder, ErrorDecoder errorDecoder, boolean decode404,
  14. boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy) {
  15. this.target = checkNotNull(target, "target");
  16. this.client = checkNotNull(client, "client for %s", target);
  17. this.requestInterceptors =
  18. checkNotNull(requestInterceptors, "requestInterceptors for %s", target);
  19. this.metadata = checkNotNull(metadata, "metadata for %s", target);
  20. this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target);
  21. //其他代码
  22. }
  23. @Override
  24. public Object invoke (Object[] argv) throws Throwable {
  25. RequestTemplate template = buildTemplateFromArgs.create(argv);
  26. Retryer retryer = this.retryer.clone();
  27. while ( true) {
  28. try {
  29. return executeAndDecode(template);
  30. } catch (RetryableException e) {
  31. try {
  32. retryer.continueOrPropagate(e);
  33. } catch (RetryableException th) {
  34. Throwable cause = th.getCause();
  35. if (propagationPolicy == UNWRAP && cause != null) {
  36. throw cause;
  37. } else {
  38. throw th;
  39. }
  40. }
  41. if (logLevel != Logger.Level.NONE) {
  42. logger.logRetry(metadata.configKey(), logLevel);
  43. }
  44. continue;
  45. }
  46. }
  47. }
  48. static class Factory {
  49. // 其他代码
  50. public MethodHandler create (Target<?> target,
  51. MethodMetadata md,
  52. RequestTemplate.Factory buildTemplateFromArgs,
  53. Options options,
  54. Decoder decoder,
  55. ErrorDecoder errorDecoder) {
  56. return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
  57. logLevel, md, buildTemplateFromArgs, options, decoder,
  58. errorDecoder, decode404, closeAfterDecode, propagationPolicy);
  59. }
  60. }
  61. Object executeAndDecode (RequestTemplate template) throws Throwable {
  62. Request request = targetRequest(template);
  63. Response response;
  64. //其他代码
  65. response = client.execute(request, options);
  66. //其他代码
  67. }
  68. }

当调用被@FeignClient修饰接口中的方法时,该方法会被SynchronousMethodHandler动态代理:生成一个RequestTemplate对象(也就是上边的invoke方法)。

executeAndDecode()方法会通过RequestTemplate 生成 Request对象。Request对象将交给Client#execute

五、调用Client组件

        追踪上边的接口:Client client(在feign包中定义的)。

        Feign最终发送request请求以及接收response响应,都是由Client组件完成的。Client的默认实现类是Client.Default(feign包里),该类由HttpURLConnnection实现网络请求,另外还支持HttpClient、Okhttp。

首先来看以下在FeignRibbonClient的自动配置类,FeignRibbonClientAutoConfiguration

主要在工程启动的时候注入一些bean 


   
   
  1. @ConditionalOnClass({ ILoadBalancer.class, Feign.class })
  2. @Configuration
  3. @AutoConfigureBefore(FeignAutoConfiguration.class)
  4. public class FeignRibbonClientAutoConfiguration {
  5. @Bean
  6. @ConditionalOnMissingBean
  7. public Client feignClient (CachingSpringLoadBalancerFactory cachingFactory,
  8. SpringClientFactory clientFactory) {
  9. return new LoadBalancerFeignClient( new Client.Default( null, null),
  10. cachingFactory, clientFactory);
  11. }
  12. }

在缺失配置feignClient的情况下,会自动注入Client.Default(feign包下)


   
   
  1. public interface Client {
  2. Response execute (Request request, Options options) throws IOException;
  3. class Default implements Client {
  4. private final SSLSocketFactory sslContextFactory;
  5. private final HostnameVerifier hostnameVerifier;
  6. public Default (SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) {
  7. this.sslContextFactory = sslContextFactory;
  8. this.hostnameVerifier = hostnameVerifier;
  9. }
  10. @Override
  11. public Response execute (Request request, Options options) throws IOException {
  12. HttpURLConnection connection = convertAndSend(request, options);
  13. return convertResponse(connection, request);
  14. }
  15. HttpURLConnection convertAndSend (Request request, Options options) throws IOException {
  16. final HttpURLConnection connection =
  17. (HttpURLConnection) new URL(request.url()).openConnection();
  18. // 若是https协议,设置ssl
  19. if (connection instanceof HttpsURLConnection) {
  20. HttpsURLConnection sslCon = (HttpsURLConnection) connection;
  21. if (sslContextFactory != null) {
  22. sslCon.setSSLSocketFactory(sslContextFactory);
  23. }
  24. if (hostnameVerifier != null) {
  25. sslCon.setHostnameVerifier(hostnameVerifier);
  26. }
  27. }
  28. //设置连接超时时间、读超时时间、请求方法等
  29. connection.setConnectTimeout(options.connectTimeoutMillis());
  30. connection.setReadTimeout(options.readTimeoutMillis());
  31. connection.setAllowUserInteraction( false);
  32. connection.setInstanceFollowRedirects(options.isFollowRedirects());
  33. connection.setRequestMethod(request.httpMethod().name());
  34. // 看header中是否有"Content-Encoding"项,若有则使用其编码("gzip"或"deflate")
  35. Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);
  36. boolean gzipEncodedRequest =
  37. contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);
  38. boolean deflateEncodedRequest =
  39. contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);
  40. // 设置请求头部
  41. boolean hasAcceptHeader = false;
  42. Integer contentLength = null;
  43. for (String field : request.headers().keySet()) {
  44. if (field.equalsIgnoreCase( "Accept")) {
  45. hasAcceptHeader = true;
  46. }
  47. for (String value : request.headers().get(field)) {
  48. if (field.equals(CONTENT_LENGTH)) {
  49. if (!gzipEncodedRequest && !deflateEncodedRequest) {
  50. contentLength = Integer.valueOf(value);
  51. connection.addRequestProperty(field, value);
  52. }
  53. } else {
  54. connection.addRequestProperty(field, value);
  55. }
  56. }
  57. }
  58. // Some servers choke on the default accept string.
  59. if (!hasAcceptHeader) {
  60. connection.addRequestProperty( "Accept", "*/*");
  61. }
  62. // 若有请求体,详细设置:选择gzip/deflater压缩等
  63. if (request.requestBody().asBytes() != null) {
  64. if (contentLength != null) {
  65. connection.setFixedLengthStreamingMode(contentLength);
  66. } else {
  67. connection.setChunkedStreamingMode( 8196);
  68. }
  69. connection.setDoOutput( true);
  70. OutputStream out = connection.getOutputStream();
  71. if (gzipEncodedRequest) {
  72. out = new GZIPOutputStream(out);
  73. } else if (deflateEncodedRequest) {
  74. out = new DeflaterOutputStream(out);
  75. }
  76. try {
  77. out.write(request.requestBody().asBytes());
  78. } finally {
  79. try {
  80. out.close();
  81. } catch (IOException suppressed) { // NOPMD
  82. }
  83. }
  84. }
  85. return connection;
  86. }
  87. Response convertResponse (HttpURLConnection connection, Request request) throws IOException {
  88. int status = connection.getResponseCode();
  89. String reason = connection.getResponseMessage();
  90. if (status < 0) {
  91. throw new IOException(format( "Invalid status(%s) executing %s %s", status,
  92. connection.getRequestMethod(), connection.getURL()));
  93. }
  94. Map<String, Collection<String>> headers = new LinkedHashMap<String, Collection<String>>();
  95. for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) {
  96. // response message
  97. if (field.getKey() != null) {
  98. headers.put(field.getKey(), field.getValue());
  99. }
  100. }
  101. Integer length = connection.getContentLength();
  102. if (length == - 1) {
  103. length = null;
  104. }
  105. InputStream stream;
  106. if (status >= 400) {
  107. stream = connection.getErrorStream();
  108. } else {
  109. stream = connection.getInputStream();
  110. }
  111. return Response.builder()
  112. .status(status)
  113. .reason(reason)
  114. .headers(headers)
  115. .request(request)
  116. .body(stream, length)
  117. .build();
  118. }
  119. }
  120. }

它使用的网络请求框架为HttpURLConnection。

怎么在feign中使用HttpClient

查看FeignRibbonClientAutoConfiguration的源码


   
   
  1. @ConditionalOnClass({ ILoadBalancer.class, Feign.class })
  2. @Configuration
  3. @AutoConfigureBefore(FeignAutoConfiguration.class)
  4. public class FeignRibbonClientAutoConfiguration {
  5. //省略代码
  6. @Configuration
  7. @ConditionalOnClass(ApacheHttpClient.class)
  8. @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
  9. protected static class HttpClientFeignLoadBalancedConfiguration {
  10. @Autowired(required = false)
  11. private HttpClient httpClient;
  12. @Bean
  13. @ConditionalOnMissingBean(Client.class)
  14. public Client feignClient (CachingSpringLoadBalancerFactory cachingFactory,
  15. SpringClientFactory clientFactory) {
  16. ApacheHttpClient delegate;
  17. if ( this.httpClient != null) {
  18. delegate = new ApacheHttpClient( this.httpClient);
  19. }
  20. else {
  21. delegate = new ApacheHttpClient();
  22. }
  23. return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
  24. }
  25. }
  26. //省略代码
  27. }

从代码@ConditionalOnClass(ApacheHttpClient.class)注解可知道,只需要在pom文件加上HttpClient的classpath就行了,另外需要在配置文件上加上feign.httpclient.enabled为true,从 @ConditionalOnProperty注解可知,这个可以不写,在默认的情况下就为true.

在pom文件加上:


   
   
  1. <dependency>
  2. <groupId>com.netflix.feign </groupId>
  3. <artifactId>feign-httpclient </artifactId>
  4. <version>RELEASE </version>
  5. </dependency>

同理,若想要feign使用Okhttp,则只需要在pom文件上加上feign-okhttp的依赖:


   
   
  1. <dependency>
  2. <groupId>com.netflix.feign </groupId>
  3. <artifactId>feign-okhttp </artifactId>
  4. <version>RELEASE </version>
  5. </dependency>

六、Feign的负载均衡

         通过上述的FeignRibbonClientAutoConfiguration类配置Client的类型(httpurlconnection,okhttp和httpclient)时候,可知最终向容器注入的是LoadBalancerFeignClient,即负载均衡客户端。

        “第四部分:拦截@FeignClient修饰接口中的方法”最后,可以看到最终调用Client#execute

LoadBalancerFeignClient


   
   
  1. @Override
  2. public Response execute (Request request, Request.Options options) throws IOException {
  3. try {
  4. URI asUri = URI.create(request.url());
  5. String clientName = asUri.getHost();
  6. URI uriWithoutHost = cleanUrl(request.url(), clientName);
  7. FeignLoadBalancer. RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
  8. this.delegate, request, uriWithoutHost);
  9. IClientConfig requestConfig = getClientConfig(options, clientName);
  10. return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
  11. requestConfig).toResponse();
  12. }
  13. catch (ClientException e) {
  14. IOException io = findIOException(e);
  15. if (io != null) {
  16. throw io;
  17. }
  18. throw new RuntimeException(e);
  19. }
  20. }

executeWithLoadBalancer()方法(AbstractLoadBalancerAwareClient#executeWithLoadBalancer),即通过负载均衡的方式请求:


   
   
  1. public T executeWithLoadBalancer (final S request, final IClientConfig requestConfig) throws ClientException {
  2. RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
  3. LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
  4. .withLoadBalancerContext( this)
  5. .withRetryHandler(handler)
  6. .withLoadBalancerURI(request.getUri())
  7. .build();
  8. try {
  9. return command.submit(
  10. new ServerOperation<T>() {
  11. @Override
  12. public Observable<T> call (Server server) {
  13. URI finalUri = reconstructURIWithServer(server, request.getUri());
  14. S requestForServer = (S) request.replaceUri(finalUri);
  15. try {
  16. return Observable.just(AbstractLoadBalancerAwareClient. this.execute(requestForServer, requestConfig));
  17. }
  18. catch (Exception e) {
  19. return Observable.error(e);
  20. }
  21. }
  22. })
  23. .toBlocking()
  24. .single();
  25. } catch (Exception e) {
  26. Throwable t = e.getCause();
  27. if (t instanceof ClientException) {
  28. throw (ClientException) t;
  29. } else {
  30. throw new ClientException(e);
  31. }
  32. }
  33. }

submit()  //LoadBalancerCommand#submit

需要注意的是: LoadBalancerCommand所在包为:ribbon-loadbalance-xxx.jar。说明,feign是通过调用了Ribbon的负载均衡。


   
   
  1. Observable<T> o = ( this.server == null ? this.selectServer() : Observable.just( this.server))
  2. .concatMap( new Func1<Server, Observable<T>>() {
  3. // Called for each server being selected
  4. public Observable<T> call (Server server) {
  5. context.setServer(server);
  6. final ServerStats stats = loadBalancerContext.getServerStats(server);
  7. // Called for each attempt and retry
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud是一个基于Spring Boot的开发工具集,提供了一系列用于构建分布式系统的解决方案。其中,Feign和Ribbon是Spring Cloud中常用的两个组件。 Feign是一个声明式的Web服务客户端,用于简化使用Spring Cloud的服务之间进行通信的过程。它通过支持注解方式来定义和使用服务接口,底层使用的是基于反射的动态代理技术,将接口和实际调用的服务进行映射。Feign通过集成Ribbon来实现负载均衡的功能。 Ribbon是一个负载均衡的组件,它根据一系列的负载均衡策略,从多个服务实例中选择一个要调用的实例。Ribbon通过监听Eureka注册中心上已注册的服务列表,并通过默认的轮询算法选择一个实例。在Feign中,Ribbon用于根据服务接口定义的URL和方法,选择一个具体的服务进行调用。 Feign和Ribbon的原理可以简单总结如下:首先,Feign通过使用@EnableFeignClients注解开启Feign功能,扫描包中带有@FeignClient注解的接口定义。接着,Feign将这些接口定义转化为动态代理对象,在调用接口方法时,实际上是通过动态代理对象进行了解析和转发,最终会调用到具体的服务实例上。此时,Ribbon会根据一定的策略从多个服务实例中选择一个实例进行调用,并返回调用结果。整个调用过程是通过HTTP协议进行通信的。 总之,Spring Cloud中的Feign和Ribbon组件能够实现微服务之间的通信和负载均衡功能。Feign简化了基于HTTP的服务接口定义和调用的过程,而Ribbon则负责根据一定的负载均衡策略选择合适的服务实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值