OpenFeign是一个近程客户端申请代理,它的根本作用是让开发者可能以面向接口的形式来实现近程调用,从而屏蔽底层通信的复杂性,它的具体原理如下图所示。

在明天的内容中,咱们须要详细分析OpenFeign它的工作原理及源码,咱们持续回到这段代码。

@Slf4j@RestController@RequestMapping("/order")public class OrderController {    @Autowired    IGoodsServiceFeignClient goodsServiceFeignClient;    @Autowired    IPromotionServiceFeignClient promotionServiceFeignClient;    @Autowired    IOrderServiceFeignClient orderServiceFeignClient;    /**     * 下单     */    @GetMapping    public String order(){        String goodsInfo=goodsServiceFeignClient.getGoodsById();        String promotionInfo=promotionServiceFeignClient.getPromotionById();        String result=orderServiceFeignClient.createOrder(goodsInfo,promotionInfo);        return result;    }}

从这段代码中,先引出对于OpenFeign性能实现的思考。

  1. 申明@FeignClient注解的接口,如何被解析和注入的?
  2. 通过@Autowired依赖注入,到底是注入一个什么样的实例
  3. 基于FeignClient申明的接口被解析后,如何存储?
  4. 在发动办法调用时,整体的工作流程是什么样的?
  5. OpenFeign是如何集成Ribbon做负载平衡解析?

带着这些疑难,开始去逐项剖析OpenFeign的外围源码

OpenFeign注解扫描与解析

思考, 一个被申明了@FeignClient注解的接口,应用@Autowired进行依赖注入,而最终这个接口可能失常被注入实例。

从这个后果来看,能够失去两个论断

  1. @FeignClient申明的接口,在Spring容器启动时,会被解析。
  2. 因为被Spring容器加载的是接口,而接口又没有实现类,因而Spring容器解析时,会生成一个动静代理类。

EnableFeignClient

@FeignClient注解是在什么时候被解析的呢?基于咱们之前所有积攒的常识,无非就以下这几种

  • ImportSelector,批量导入bean
  • ImportBeanDefinitionRegistrar,导入bean申明并进行注册
  • BeanFactoryPostProcessor , 一个bean被装载的前后处理器

在这几个选项中,仿佛ImportBeanDefinitionRegistrar更适合,因为第一个是批量导入一个bean的string汇合,不适宜做动静Bean的申明。 而BeanFactoryPostProcessor 是一个Bean初始化之前和之后被调用的处理器。

而在咱们的FeignClient申明中,并没有Spring相干的注解,所以天然也不会被Spring容器加载和触发。

那么@FeignClient是在哪里被申明扫描的呢?

在集成FeignClient时,咱们在SpringBoot的main办法中,申明了一个注解@EnableFeignClients(basePackages = "com.gupaoedu.ms.api")。这个注解须要填写一个指定的包名。

嗯,看到这里,基本上就能猜测出,这个注解必然和@FeignClient注解的解析有莫大的关系。

上面这段代码是@EnableFeignClients注解的申明,果然看到了一个很相熟的脸孔FeignClientsRegistrar

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

FeignClientsRegistrar

FeignClientRegistrar,次要性能就是针对申明@FeignClient注解的接口进行扫描和注入到IOC容器。

class FeignClientsRegistrar      implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {    }

果然,这个类实现了ImportBeanDefinitionRegistrar接口

public interface ImportBeanDefinitionRegistrar {    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {        this.registerBeanDefinitions(importingClassMetadata, registry);    }    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {    }}

这个接口有两个重载的办法,用来实现Bean的申明和注册。

简略给大家演示一下ImportBeanDefinitionRegistrar的作用。

gpmall-portal这个我的项目的com.gupaoedu目录下,别离创立

  1. HelloService.java
  2. GpImportBeanDefinitionRegistrar.java
  3. EnableGpRegistrar.java
  4. TestMain
  1. 定义一个须要被装载到IOC容器中的类HelloService
public class HelloService {}
  1. 定义一个Registrar的实现,定义一个bean,装载到IOC容器
public class GpImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {    @Override    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,                                        BeanDefinitionRegistry registry) {        BeanDefinition beanDefinition=new GenericBeanDefinition();        beanDefinition.setBeanClassName(HelloService.class.getName());        registry.registerBeanDefinition("helloService",beanDefinition);    }}
  1. 定义一个注解类
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(GpImportBeanDefinitionRegistrar.class)public @interface EnableGpRegistrar {}
  1. 写一个测试类
@Configuration@EnableGpRegistrarpublic class TestMain {    public static void main(String[] args) {        ApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestMain.class);        System.out.println(applicationContext.getBean(HelloService.class));    }}
  1. 通过后果演示能够发现,HelloService这个bean 曾经装载到了IOC容器。

这就是动静装载的性能实现,它相比于@Configuration配置注入,会多了很多的灵活性。 ok,再回到FeignClient的解析中来。

FeignClientsRegistrar.registerBeanDefinitions

  • registerDefaultConfiguration 办法外部从 SpringBoot 启动类上查看是否有 @EnableFeignClients, 有该注解的话, 则实现Feign框架相干的一些配置内容注册。
  • registerFeignClients 办法外部从 classpath 中, 扫描取得 @FeignClient 润饰的类, 将类的内容解析为 BeanDefinition , 最终通过调用 Spring 框架中的 BeanDefinitionReaderUtils.resgisterBeanDefinition 将解析解决过的 FeignClient BeanDeifinition 增加到 spring 容器中
//BeanDefinitionReaderUtils.resgisterBeanDefinition @Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,                                    BeanDefinitionRegistry registry) {    //注册@EnableFeignClients中定义defaultConfiguration属性下的类,包装成FeignClientSpecification,注册到Spring容器。    //在@FeignClient中有一个属性:configuration,这个属性是示意各个FeignClient自定义的配置类,前面也会通过调用registerClientConfiguration办法来注册成FeignClientSpecification到容器。//所以,这里能够齐全了解在@EnableFeignClients中配置的是做为兜底的配置,在各个@FeignClient配置的就是自定义的状况。    registerDefaultConfiguration(metadata, registry);    registerFeignClients(metadata, registry);}
这外面须要重点剖析的就是registerFeignClients办法,这个办法次要是扫描类门路下所有的@FeignClient注解,而后进行动静Bean的注入。它最终会调用registerFeignClient办法。
public void registerFeignClients(AnnotationMetadata metadata,            BeanDefinitionRegistry registry) {    registerFeignClient(registry, annotationMetadata, attributes);}

FeignClientsRegistrar.registerFeignClients

registerFeignClients办法的定义如下。

//# FeignClientsRegistrar.registerFeignClientspublic void registerFeignClients(AnnotationMetadata metadata,      BeanDefinitionRegistry registry) {      LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();   //获取@EnableFeignClients注解的元数据   Map<String, Object> attrs = metadata         .getAnnotationAttributes(EnableFeignClients.class.getName());    //获取@EnableFeignClients注解中的clients属性,能够配置@FeignClient申明的类,如果配置了,则须要扫描并加载。   final Class<?>[] clients = attrs == null ? null         : (Class<?>[]) attrs.get("clients");   if (clients == null || clients.length == 0) {       //默认TypeFilter失效,这种模式会查问出许多不合乎你要求的class名      ClassPathScanningCandidateComponentProvider scanner = getScanner();      scanner.setResourceLoader(this.resourceLoader);      scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class)); //增加蕴含过滤的属性@FeignClient。      Set<String> basePackages = getBasePackages(metadata); //从@EnableFeignClients注解中获取basePackages配置。      for (String basePackage : basePackages) {          //scanner.findCandidateComponents(basePackage) 扫描basePackage下的@FeignClient注解申明的接口         candidateComponents.addAll(scanner.findCandidateComponents(basePackage)); //增加到candidateComponents,也就是候选容器中。      }   }   else {//如果配置了clients,则须要增加到candidateComponets中。      for (Class<?> clazz : clients) {         candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));      }   }   //遍历候选容器列表。   for (BeanDefinition candidateComponent : candidateComponents) {       if (candidateComponent instanceof AnnotatedBeanDefinition) { //如果属于AnnotatedBeanDefinition实例类型         // verify annotated class is an interface          //失去@FeignClient注解的beanDefinition         AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;         AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();  //获取这个bean的注解元数据         Assert.isTrue(annotationMetadata.isInterface(),               "@FeignClient can only be specified on an interface");          //获取元数据属性         Map<String, Object> attributes = annotationMetadata               .getAnnotationAttributes(FeignClient.class.getCanonicalName());         //获取@FeignClient中配置的服务名称。         String name = getClientName(attributes);         registerClientConfiguration(registry, name,               attributes.get("configuration"));         registerFeignClient(registry, annotationMetadata, attributes);      }   }}

FeignClient Bean的注册

这个办法就是把FeignClient接口注册到Spring IOC容器,

FeignClient是一个接口,那么这里注入到IOC容器中的对象是什么呢?
private void registerFeignClient(BeanDefinitionRegistry registry,      AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {   String className = annotationMetadata.getClassName();  //获取FeignClient接口的类全门路   Class clazz = ClassUtils.resolveClassName(className, null); //加载这个接口,失去Class实例    //构建ConfigurableBeanFactory,提供BeanFactory的配置能力   ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory          ? (ConfigurableBeanFactory) registry : null;    //获取contextId   String contextId = getContextId(beanFactory, attributes);   String name = getName(attributes);    //构建一个FeignClient FactoryBean,这个是工厂Bean。   FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();    //设置工厂Bean的相干属性   factoryBean.setBeanFactory(beanFactory);   factoryBean.setName(name);   factoryBean.setContextId(contextId);   factoryBean.setType(clazz);        //BeanDefinitionBuilder是用来构建BeanDefinition对象的建造器   BeanDefinitionBuilder definition = BeanDefinitionBuilder         .genericBeanDefinition(clazz, () -> {             //把@FeignClient注解配置中的属性设置到FactoryBean中。            factoryBean.setUrl(getUrl(beanFactory, attributes));            factoryBean.setPath(getPath(beanFactory, attributes));            factoryBean.setDecode404(Boolean                  .parseBoolean(String.valueOf(attributes.get("decode404"))));            Object fallback = attributes.get("fallback");            if (fallback != null) {               factoryBean.setFallback(fallback instanceof Class                     ? (Class<?>) fallback                     : ClassUtils.resolveClassName(fallback.toString(), null));            }            Object fallbackFactory = attributes.get("fallbackFactory");            if (fallbackFactory != null) {               factoryBean.setFallbackFactory(fallbackFactory instanceof Class                     ? (Class<?>) fallbackFactory                     : ClassUtils.resolveClassName(fallbackFactory.toString(),                           null));            }            return factoryBean.getObject();  //factoryBean.getObject() ,基于工厂bean发明一个bean实例。         });   definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); //设置注入模式,采纳类型注入   definition.setLazyInit(true); //设置提早华   validate(attributes);    //从BeanDefinitionBuilder中构建一个BeanDefinition,它用来形容一个bean的实例定义。   AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();   beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);   beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);   // has a default, won't be null   boolean primary = (Boolean) attributes.get("primary");   beanDefinition.setPrimary(primary);   String[] qualifiers = getQualifiers(attributes);   if (ObjectUtils.isEmpty(qualifiers)) {      qualifiers = new String[] { contextId + "FeignClient" };   }      BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,         qualifiers);   BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); //把BeanDefinition的这个bean定义注册到IOC容器。}

综上代码剖析,其实实现逻辑很简略。

  1. 创立一个BeanDefinitionBuilder。
  2. 创立一个工厂Bean,并把从@FeignClient注解中解析的属性设置到这个FactoryBean中
  3. 调用registerBeanDefinition注册到IOC容器中

对于FactoryBean

在上述的逻辑中,咱们重点须要关注的是FactoryBean,这个大家可能接触得会比拟少一点。

首先,须要留神的是FactoryBean和BeanFactory是不一样的,FactoryBean是一个工厂Bean,能够生成某一个类型Bean实例,它最大的一个作用是:能够让咱们自定义Bean的创立过程。

而,BeanFactory是Spring容器中的一个根本类也是很重要的一个类,在BeanFactory中能够创立和治理Spring容器中的Bean。

上面这段代码是FactoryBean接口的定义。

public interface FactoryBean<T> {    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";    @Nullable    T getObject() throws Exception;    @Nullable    Class<?> getObjectType();    default boolean isSingleton() {        return true;    }}

从下面的代码中咱们发现在FactoryBean中定义了一个Spring Bean的很重要的三个个性:是否单例、Bean类型、Bean实例,这也应该是咱们对于Spring中的一个Bean最直观的感触。

FactoryBean自定义应用

上面咱们来模仿一下@FeignClient解析以及工厂Bean的构建过程。

  1. 先定义一个接口,这个接口能够类比为咱们下面形容的FeignClient.
public interface IHelloService {    String say();}
  1. 接着,定义一个工厂Bean,这个工厂Bean中次要是针对IHelloService生成动静代理。
public class DefineFactoryBean implements FactoryBean<IHelloService> {    @Override    public IHelloService getObject()  {       IHelloService helloService=(IHelloService) Proxy.newProxyInstance(IHelloService.class.getClassLoader(),               new Class<?>[]{IHelloService.class}, (proxy, method, args) -> {           System.out.println("begin execute");           return "Hello FactoryBean";       });        return helloService;    }    @Override    public Class<?> getObjectType() {        return IHelloService.class;    }}
  1. 通过实现ImportBeanDefinitionRegistrar这个接口,来动静注入Bean实例
public class GpImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {    @Override    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,                                        BeanDefinitionRegistry registry) {        DefineFactoryBean factoryBean=new DefineFactoryBean();        BeanDefinitionBuilder definition= BeanDefinitionBuilder.genericBeanDefinition(                IHelloService.class,()-> factoryBean.getObject());        BeanDefinition beanDefinition=definition.getBeanDefinition();        registry.registerBeanDefinition("helloService",beanDefinition);    }}
  1. 申明一个注解,用来示意动静bean的注入导入。
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(GpImportBeanDefinitionRegistrar.class)public @interface EnableGpRegistrar {}
  1. 编写测试类,测试IHelloService这个接口的动静注入
@Configuration@EnableGpRegistrarpublic class TestMain {    public static void main(String[] args) {        ApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestMain.class);        IHelloService is=applicationContext.getBean(IHelloService.class);        System.out.println(is.say());    }}
  1. 运行上述的测试方法,能够看到IHelloService这个接口,被动静代理并且注入到了IOC容器。

FeignClientFactoryBean

由上述案例可知,Spring IOC容器在注入FactoryBean时,会调用FactoryBean的getObject()办法取得bean的实例。因而咱们能够依照这个思路去剖析FeignClientFactoryBean

@Overridepublic Object getObject() {   return getTarget();}

构建对象Bean的实现代码如下,这个代码的实现较长,咱们分为几个步骤来看

Feign上下文的构建

先来看上下文的构建逻辑,代码局部如下。

<T> T getTarget() {   FeignContext context = beanFactory != null         ? beanFactory.getBean(FeignContext.class)         : applicationContext.getBean(FeignContext.class);   Feign.Builder builder = feign(context);}

两个要害的对象阐明:

  1. FeignContext是全局惟一的上下文,它继承了NamedContextFactory,它是用来来对立保护feign中各个feign客户端互相隔离的上下文,FeignContext注册到容器是在FeignAutoConfiguration上实现的,代码如下!
//FeignAutoConfiguration.java@Autowired(required = false)private List<FeignClientSpecification> configurations = new ArrayList<>();@Beanpublic FeignContext feignContext() {FeignContext context = new FeignContext();context.setConfigurations(this.configurations);return context;}

在初始化FeignContext时,会把configurations放入到FeignContext中。configuration示意以后被扫描到的所有@FeignClient。

  1. Feign.Builder用来构建Feign对象,基于builder实现上下文信息的构建,代码如下。
protected Feign.Builder feign(FeignContext context) {    FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);    Logger logger = loggerFactory.create(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)); //contract协定,用来实现模版解析(前面再详细分析)    // @formatter:on    configureFeign(context, builder);    applyBuildCustomizers(context, builder);    return builder;}

从代码中能够看到,feign办法,次要是针对不同的服务提供者生成Feign的上下文信息,比方loggerencoderdecoder等。因而,从这个剖析过程中,咱们不难猜测到它的原理构造,如下图所示

父子容器隔离的实现形式如下,当调用get办法时,会从context中去获取指定type的实例对象。

//FeignContext.javaprotected <T> T get(FeignContext context, Class<T> type) {    T instance = context.getInstance(contextId, type);    if (instance == null) {        throw new IllegalStateException(            "No bean found of type " + type + " for " + contextId);    }    return instance;}

接着,调用NamedContextFactory中的getInstance办法。

//NamedContextFactory.javapublic <T> T getInstance(String name, Class<T> type) {    //依据`name`获取容器上下文    AnnotationConfigApplicationContext context = this.getContext(name);    try {        //再从容器上下文中获取指定类型的bean。        return context.getBean(type);    } catch (NoSuchBeanDefinitionException var5) {        return null;    }}

getContext办法依据namecontexts容器中取得上下文对象,如果没有,则调用createContext创立。

protected AnnotationConfigApplicationContext getContext(String name) {    if (!this.contexts.containsKey(name)) {        synchronized(this.contexts) {            if (!this.contexts.containsKey(name)) {                this.contexts.put(name, this.createContext(name));            }        }    }    return (AnnotationConfigApplicationContext)this.contexts.get(name);}

生成动静代理

第二个局部,如果@FeignClient注解中,没有配置url,也就是不走相对申请门路,则执行上面这段逻辑。

因为咱们在@FeignClient注解中应用的是name,所以须要执行负载平衡策略的分支逻辑。

<T> T getTarget() {    //省略.....     if (!StringUtils.hasText(url)) { //是@FeignClient中的一个属性,能够定义申请的相对URL      if (LOG.isInfoEnabled()) {         LOG.info("For '" + name               + "' URL not provided. Will try picking an instance via load-balancing.");      }      if (!name.startsWith("http")) {         url = "http://" + name;      }      else {         url = name;      }      url += cleanPath();      //      return (T) loadBalance(builder, context,            new HardCodedTarget<>(type, name, url));   }    //省略....}

loadBalance办法的代码实现如下,其中Client是Spring Boot主动拆卸的时候实现的,如果替换了其余的http协定框架,则client则对应为配置的协定api。

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,                            HardCodedTarget<T> target) {    //Feign发送申请以及承受响应的http client,默认是Client.Default的实现,能够批改成OkHttp、HttpClient等。    Client client = getOptional(context, Client.class);    if (client != null) {        builder.client(client); //针对以后Feign客户端,设置网络通信的client        //targeter示意HystrixTarger实例,因为Feign能够集成Hystrix实现熔断,所以这里会一层包装。        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 or spring-cloud-starter-loadbalancer?");}

HystrixTarget.target代码如下,咱们没有集成Hystrix,因而不会触发Hystrix相干的解决逻辑。

//HystrixTarget.java@Overridepublic <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,                    FeignContext context, Target.HardCodedTarget<T> target) {    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { //没有配置Hystrix,则走这部分逻辑        return feign.target(target);    }   //省略....    return feign.target(target);}

进入到Feign.target办法,代码如下。

//Feign.javapublic <T> T target(Target<T> target) {    return this.build().newInstance(target);  //target.HardCodedTarget}public Feign build() {    //这里会构建一个LoadBalanceClient    Client client = (Client)Capability.enrich(this.client, this.capabilities);    Retryer retryer = (Retryer)Capability.enrich(this.retryer, this.capabilities);    List<RequestInterceptor> requestInterceptors = (List)this.requestInterceptors.stream().map((ri) -> {        return (RequestInterceptor)Capability.enrich(ri, this.capabilities);    }).collect(Collectors.toList());    //OpenFeign Log配置    Logger logger = (Logger)Capability.enrich(this.logger, this.capabilities);    //模版解析协定(这个接口十分重要:它决定了哪些注解能够标注在接口/接口办法上是无效的,并且提取出无效的信息,组装成为MethodMetadata元信息。)    Contract contract = (Contract)Capability.enrich(this.contract, this.capabilities);    //封装Request申请的 连贯超时=默认10s ,读取超时=默认60    Options options = (Options)Capability.enrich(this.options, this.capabilities);    //编码器    Encoder encoder = (Encoder)Capability.enrich(this.encoder, this.capabilities);    //解码器    Decoder decoder = (Decoder)Capability.enrich(this.decoder, this.capabilities);        InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);    QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);    //synchronousMethodHandlerFactory, 同步办法调用处理器(很重要,后续要用到)    Factory synchronousMethodHandlerFactory = new Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);    //ParseHandlersByName的作用就是咱们传入Target(封装了咱们的模仿接口,要拜访的域名),返回这个接口下的各个办法,对应的执行HTTP申请须要的一系列信息    ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);        return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);}

build办法中,返回了一个ReflectiveFeign的实例对象,先来看ReflectiveFeign中的newInstance办法。

public <T> T newInstance(Target<T> target) {    //润饰了@FeignClient注解的接口办法封装成办法处理器,把指定的target进行解析,失去须要解决的办法汇合。    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);    //定义一个用来保留须要解决的办法的汇合    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();    //JDK8当前,接口容许默认办法实现,这里是对默认办法进行封装解决。    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();        //遍历@FeignClient接口的所有办法    for (Method method : target.type().getMethods()) {        //如果是Object中的办法,则间接跳过        if (method.getDeclaringClass() == Object.class) {            continue;                    } else if (Util.isDefault(method)) {//如果是默认办法,则把该办法绑定一个DefaultMethodHandler。            DefaultMethodHandler handler = new DefaultMethodHandler(method);            defaultMethodHandlers.add(handler);            methodToHandler.put(method, handler);        } else {//否则,增加MethodHandler(SynchronousMethodHandler)。            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;}

上述代码,其实也不难理解。

  1. 解析@FeignClient接口申明的办法,依据不同办法绑定不同的处理器。

    1. 默认办法,绑定DefaultMethodHandler
    2. 近程办法,绑定SynchronousMethodHandler
  2. 应用JDK提供的Proxy创立动静代理

MethodHandler,会把办法参数、办法返回值、参数汇合、申请类型、申请门路进行解析存储,如下图所示。

FeignClient接口解析

接口解析也是Feign很重要的一个逻辑,它能把接口申明的属性转化为HTTP通信的协定参数。

执行逻辑RerlectiveFeign.newInstance
public <T> T newInstance(Target<T> target) {    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); //here}

targetToHandlersByName.apply(target);会解析接口办法上的注解,从而解析出办法粒度的特定的配置信息,而后生产一个SynchronousMethodHandler
而后须要保护一个<method,MethodHandler>的map,放入InvocationHandler的实现FeignInvocationHandler中。

public Map<String, MethodHandler> apply(Target target) {    List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());    Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();    for (MethodMetadata md : metadata) {        BuildTemplateByResolvingArgs buildTemplate;        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {            buildTemplate =                new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);        } else if (md.bodyIndex() != null) {            buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);        } else {            buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);        }        if (md.isIgnored()) {            result.put(md.configKey(), args -> {                throw new IllegalStateException(md.configKey() + " is not a method handled by feign");            });        } else {            result.put(md.configKey(),                       factory.create(target, md, buildTemplate, options, decoder, errorDecoder));        }    }    return result;}

为了更好的了解上述逻辑,咱们能够借助上面这个图来了解!

阶段性小结

通过上述过程剖析,被申明为@FeignClient注解的类,在被注入时,最终会生成一个动静代理对象FeignInvocationHandler。

当触发办法调用时,会被FeignInvocationHandler#invoke拦挡,FeignClientFactoryBean在实例化过程中所做的事件如下图所示。

总结来说就几个点:

  1. 解析Feign的上下文配置,针对以后的服务实例构建容器上下文并返回Feign对象
  2. Feign依据高低围配置把 log、encode、decoder、等配置项设置到Feign对象中
  3. 对指标服务,应用LoadBalance以及Hystrix进行包装
  4. 通过Contract协定,把FeignClient接口的申明,解析成MethodHandler
  5. 遍历MethodHandler列表,针对须要近程通信的办法,设置SynchronousMethodHandler处理器,用来实现同步近程调用。
  6. 应用JDK中的动静代理机制构建动静代理对象。

近程通信实现

在Spring启动过程中,把所有的筹备工作准备就绪后,就开始执行近程调用。

在后面的剖析中,咱们晓得OpenFeign最终返回的是一个#ReflectiveFeign.FeignInvocationHandler的对象。

那么当客户端发动申请时,会进入到FeignInvocationHandler.invoke办法中,这个大家都晓得,它是一个动静代理的实现。

//FeignInvocationHandler.java@Overridepublic 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);}

接着,在invoke办法中,会调用this.dispatch.get(method)).invoke(args)this.dispatch.get(method)会返回一个SynchronousMethodHandler,进行拦挡解决。

this.dispatch,其实就是在初始化过程中创立的,private final Map<Method, MethodHandler> dispatch;实例。

SynchronousMethodHandler.invoke

这个办法会依据参数生成实现的RequestTemplate对象,这个对象是Http申请的模版,代码如下,代码的实现如下:

@Overridepublic Object invoke(Object[] argv) throws Throwable {    //argv,示意调用办法传递的参数。        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;        }    }}

上述代码的执行流程中,须要先结构一个Request对象,而后调用client.execute办法执行网络通信申请,整体实现流程如下。

executeAndDecode

通过上述的代码,咱们曾经将RequestTemplate拼装实现,下面的代码中有一个executeAndDecode()办法,该办法通过RequestTemplate生成Request申请对象,而后利用Http Client获取response,来获取响应信息。

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {  Request request = targetRequest(template);  //把template转化为申请报文      if (logLevel != Logger.Level.NONE) { //设置日志level          logger.logRequest(metadata.configKey(), logLevel, request);   }      Response response;     long start = System.nanoTime();    try {         //发动申请,此时client是LoadBalanceFeignClient,须要先对服务名称进行解析负载        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);    }    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);     if (decoder != null) //如果设置了解码器,这须要对响应数据做解码        return decoder.decode(response, metadata.returnType());     CompletableFuture<Object> resultFuture = new CompletableFuture<>();    asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,        metadata.returnType(),        elapsedTime);    try {      if (!resultFuture.isDone())        throw new IllegalStateException("Response handling not done");     return resultFuture.join();     } catch (CompletionException e) {         Throwable cause = e.getCause();       if (cause != null)          throw cause;        throw e;    } }

LoadBalanceClient

@OverridepublicResponse execute(Request request, Request.Options options) throws IOException {   try {       URI asUri = URI.create(request.url()); //获取申请uri,此时的地址还未被解析。     String clientName = asUri.getHost(); //获取host,实际上就是服务名称      URI uriWithoutHost = cleanUrl(request.url(), clientName);        FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(this.delegate, request, uriWithoutHost);        //加载客户端的配置信息         IClientConfig requestConfig = getClientConfig(options, clientName);       //创立负载平衡客户端(FeignLoadBalancer),执行申请       return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();     } catch (ClientException e) {    IOException io = findIOException(e);     if (io != null) {           throw io;        }         throw new RuntimeException(e);   }}

从下面的代码能够看到,lbClient(clientName) 创立了一个负载平衡的客户端,它实际上就是生成的如下所述的类:

public class FeignLoadBalancer extends        AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {

整体总结

OpenFeign的整体通信原理解析,如下图所示。

版权申明:本博客所有文章除特地申明外,均采纳 CC BY-NC-SA 4.0 许可协定。转载请注明来自 Mic带你学架构
如果本篇文章对您有帮忙,还请帮忙点个关注和赞,您的保持是我一直创作的能源。欢送关注同名微信公众号获取更多技术干货!