Feign集成Hystrix源码分析以及扩展(feign实例化过程)

总括:

feign集成hystrix的时候没有集成request cache部分

(一) 装备好参数工厂

1,建立builder--feign

2,setterfacyory放入builder中

3,根据fallback类,有的话返回feign工厂代理

4,3买有返回就根据fallbackfactory类,有的话返回feign工厂代理

上述调用时机==feign中的target方法调用时机

先从缓存中获取,获取不到就实例化,getObjetc()实例化的时候,先生成feign,然后生成loadblance,loadbalance中会调用target方法(即上述),target中对feign设置生成fallback的代理,设置路由选好的客户端

(二)注册类,实例化feign注解类

注解中的注册类

   registerDefaultConfiguration 注册默认的配置,注册FeignClientSpecification

   注册所有的feignclient注解的类,注册不等于实例化,注册是将类的名字,参数存入,然后factoryBean调用getObject获取

实例化注册类:

DefaultListableBeanFactory 存储创建的实例---用map

实例化过程:

注册相关的实例时候,根据bean name到cache获取,没有获取到就调用factoryBean---getObject()

(三)设置类的各个feign实例的属性值

getobject():为feign实例设置属性值

获取feigncontext,

获取feignbuilder,---builder中有所有的参数对象,以及由参数对象转化来的执行链  其中fallback是用监听者模式

  类似这种注解的实例化可以用工厂模式将注解的各种配置实例获取到然后设置给注解实例

实现负载均衡

  负载均衡其实也是一个属性这里最后单独设置一次,集成ribbon用builder属性的方式

实现fallback

   集成fallback的方式是采用动态代理的方式

前言

在《Hystrix源码浅析》中分析过request cache的相关源码实现,而在Feign中集成了Ribbon与Hystrix两个重要组件,但很遗憾其默认设计中并没有对hystrix request cache的实现。故而本章节将从源码出发,了解Feign是如何集成Hystrix组件的,以便后续我们扩展Hystrix在Feign中的应用。正文部分将分为4个阶段分析,为何是4个阶段呢,看了就明白啦。

正文

第一阶段

直接切入正题,找到FeignClient的自动配置类FeignClientsConfiguration,编解码器均在当前配置类中设定,我们关注的feign(此处Feign.Builder最终将帮助我们创建feign实例)客户端定义如下

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

如上图,可以看到其中有一个内部类HystrixFeignConfiguration,其注册有两个条件:

在classpath中找到HystrixCommand、HystrixFeign类;

同时在feignHystrixBuilder方法上可以看到其还需要feign.hystrix.enabled配置为true(如果没有配置,默认值为false);

只有满足了以上两个条件的,方会注册含有ystrix的feign客户端实例,在一个应用中我们会定义多个feignClient,我们需要设定Bean的scope为prototype。

反之,如果没有同时满足上述两个条件,将会默认注册一个常规的FeignBuilder实例。

在源码中还有FeignAutoConfiguration作为feign的自动配置,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

红色框中已经标出了重点,很明显两个自动装配过程通过“feign.hystrix.HystrixFeign”类是否在classpath中作为判断依据形成了一个互斥约定,贴合本章重点,我们已经加入了Hystrix相关的依赖,故其会自动装配HystrixTargeter实例。进入HystrixTargeter类中,其核心实现在target方法中,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

通过4个红色框标记了我们需要分析的四点:

根据之前的分析,我们当前装载的builder为HystrixFeign.builder,可直接转型;

先记住此处的setterfactory设定,其将在构造HystrixCommand时起到关键作用,如下是一个Default实现:

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

在Feign类中定义了生成commandKey的实现,其主要依据feignclient实例和method生成,由‘类名称#方法名+参数列表中参数类型’组成;

优先获取FeignClientFactoryBean中fallback定义,如果获取到则直接通过fallback定义类生成包含回退方法的feignClient代理;

如果没有定义fallback类,继续获取FeignClientFactoryBean中fallbackFactory定义,如果获取到则直接通过fallbackFactory定义类生成包含回退方法的feignClient代理;

通过上述的回退方法逻辑我们可以看到fallback、fallbackFactory的优先级,fallback优先级高于fallbackFactory。

第二阶段

问题来了,target方法又是何时被调用的呢?我们从启动类中添加的@EnableFeignClients入手,通过@EnableFeignClients注解启用声明式客户端应用,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,从而具备了手动注册Bean的能力,从名称上来看所有的FeignClient实例将通过FeignClientsRegistrar来实现注册,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

如上图核心方法registerBeanDefinitions在接口中的描述,其中还提及了BeanDefinitionRegistryPostProcessor,该接口同样可以实现bean的注册以及对已经注册完成的bean进行调整(其主要作用是对已注册bean实现扩展)。直接来看看重写的核心方法registerBeanDefinitions吧,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

其定义了两个步骤,

首先通过registerDefaultConfiguration方法设定默认的配置,可以看到其会注册FeignClientSpecification类型实例,究竟这部分会在何时使用呢,后续将会有涉及,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

其次通过registerFeignClients方法来注册声明式客户端feignClient,核心代码如下:

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

根据上述的断言,我们也可以明白为何声明式客户端的定义是通过接口来完成的,通过对指定的package扫描获取到所有的@FeignClient注解定义的beanDefinition,如本工程目前配置了6个如下:

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

继续关注红色框部分,

通过@FeignClient注解获取到所有的配置属性以Map方式存储在attributes中。

通过registerClientConfiguration(registry, name,attributes.get("configuration"));方法根据configuration属性获取到每个feignClient设定的配置类。

通过registerFeignClient(registry, annotationMetadata, attributes);方法注册每个feignClient;

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

在registerFeignClient方法中可以看到@FeignClient注解中所有的参数定义,均将作为后续注册bean实例的属性,并设定自动装配模式为类型装配。

通过最后一个红色框即可完成bean的注册(注册并不代表实例化完成),先来看看其定义,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

registerBeanDefinition方法即可完成最后一步注册工作,通过下图来看看definitionHolder的定义

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

beanName为接口全名称,并根据上游registerFeignClient方法获取到了别名,需要关注此处的beanDefinition中class定义为FeignClientFactoryBean,这正是FactoryBean的作用,FactoryBean将通过getObject生产指定的bean实例,是工厂模式的一个实现。

第三阶段

通过以上完成了每个feignClient对应的FeignClientFactoryBean注册。既然是通过工厂bean来实例化对应的实例,那么下面我们来看看getObject()方法又是如何被调用,并生成真实的Bean实例的呢,先来看一段代码

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

在Controller中我们通过@Autowired注入了Service1FeignClient,在服务启动过程中,DefaultListableBeanFactory(最核心的bean实例化实现)将实例化FeignConsumerController的bean实例,其调用链路如下(由下往上)

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

找到preInstantiateSingletons方法,被实例化bean描述如下

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

其中调用了AbstractBeanFactory的getBean方法获取当前的feignConsumerController的bean实例。

DefaultListableBeanFactory类是整个IOC容器初始化的核心,为了后续更好的理解类、方法间的关系,通过下图来了解下DefaultListableBeanFactory类关系图

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

接上getBean()方法继续往下走,找到DefaultSingletonBeanRegistry中getSingleton()方法获取单例,可以看到其中有一些cache判断,具体cache定义如下,以map存储为主,beanname作为key值,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

如果已经创建对应的实例则会保存在cache中,直接通过singletonObjects.get(beanName)获取返回即可,首次不存在则需要执行后续逻辑完成创建,找到AbstractAutowireCapableBeanFactory中populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw)方法,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

其中定义了依赖注入的类型的判断,通过调用的方法堆栈可以看到,在注册完成当前bean后会继续分析其依赖的实例,这也正是依赖注入的核心,在DefaultListableBeanFactory类中resolveDependency方法调用findAutowireCandidates方法找到依赖的候选者,然后对依赖的候选者进行注册实例过程,最终在AbstractBeanFactory类的getObjectForBeanInstance方法中通过一系列判断后调用父类FactoryBeanRegistrySupport中的getObjectFromFactoryBean方法-->doGetObjectFromFactoryBean方法,调用如下(其中factory即为FeignClientFactoryBean实例),

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

第四阶段

此处终于回到上面提到的问题,getObject方法是何时被调用的呢?其是在注册相关调用bean实例后,分析依赖bean实例,然后至cache中查找是否已经完成实例化,如果没有实例化则继续通过getBean()方法完成其对应的实例化过程,此处是通过FactoryBean方式获取实例,继续来看看FeignClientFactoryBean中是如何实现的呢?

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

终于看到最开始我们注册实例化的Feign.Builder实例,feign方法定义如下

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

红色框中的内容来自于@FeignClient注解中configuration配置类中的定义,其定义在FeignAutoConfiguration自动装配类中

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

而FeignClientSpecification正是来源于FeignClientsRegistrar中的registerDefaultConfiguration(metadata, registry);方法实现,继续来看看loadBalance方法的定义

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

此时的target实例即为我们最初装配实例化的HystrixTargeter,target方法实现如下

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

第一个红色框很好理解,强转为我们的HystrixFeign.Builder实例;

第二个红色框可能看着比较默认,我们来看下其又是如何定义的:

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

实现了一个默认的Default内部类,通过实现的create方法可以看到,其主要是定义了HystrixCommand的Setter参数。这里我们可以再理解下FeignContext的作用,其协助我们实现了多个FeignClient的config配置隔离,故类似的SetterFactory对应的bean实例均会配置在@FeignClient的configuration参数对应的配置类中,从而实现隔离。如果没有对应的配置将采用默认值,默认值设定在HystrixFeign类中

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

通过第三个和第四个红色框可以看到,其定义了fallback的实现优先级,如果没有定义对应的fallback则直接进入默认实现。

我们来看看fallbackFactory对应的target实现

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

如上在有fallback定义下,通过设定动态代理实现集成Hystrix应用,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

可以看到上述配置定义的SetterFactory在此处通过构造函数传入HystrixInvocationHandler实例中,HystrixInvocationHandler是JDK动态代理的实现,通过切面技术对待执行方法实现HystrixCommand的应用,重写实现fallback功能,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

无论是否定义fallback,均会执行父类的build,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

如果没有fallback定义则invocationHandlerFactory采用默认定义,

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

真正的返回值在ReflectiveFeign中的newInstance被返回,其值为我们定义的FeignClient,继续来看看源码定义

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

第一个红色框methodToHandler正是后续传入invocationHandler中的重要参数,其定义了feignClient下所有符合条件的method,如下dispatch就是此处的methodToHandler参数,其对每个方法设定了对应的回退方法和setterFactory参数,用于实例化HystrixCommand以及调用fallback方法:

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

第二个红色框中根据一定条件判断遍历载入当前接口中定义的方法至methodToHandler中;

第三个红色框使用JDK动态代理构建了代理对象proxy;

如果我们定义了多个FeignClient接口,其均会在@Autowired注入时重复通过FeignClientFactoryBean创建。

总结

通过上面的源码分析,可以看到,其核心源于spring ioc的实现原理。通过动态代理实现对方法的切面处理,从而集成Hystrix。

扩展

正如前言中描述,本章源码的跟踪的目的是为了在Feign中实现request cache。可以看到在HystrixInvocationHandler的invoke方法实现中,其通过定义HystrixCommand完成Hystrix的集成

Feign集成Hystrix源码分析以及扩展(feign实例化过程)

其实现了run()方法执行核心业务,实现了getFallback()方法实现降级回退。通过之前Hystrix中Cache的应用,我们知道此处需要重写getCacheKey()方法即可启用Cache功能应用。

剧透:后续会专门写一章来介绍如何实现Feign中的request cache应用,部分源码参考如下:

CacheInvocationContextFactory :注册了cache实现的相关action事件,如获取cachekey方法等

CacheInvocationContext :将源方法参数进行了一定逻辑处理,便于后续cachekey生成时应用

HystrixCacheKeyGenerator :提供了cachekey的生成

原文:https://blog.csdn.net/songhaifengshuaige/article/details/80489460 

相关推荐