feign原理+源码解析

1 架构图。

feign原理+源码解析_第1张图片

2 feign的初始化扫包原理。

(1)feign使用,是main上的@EnableFeignClients 和 api的@FeignClient(value = "serviceXXX") 搭配使用。
(2)@FeignClient 是个自定义注解没啥东西只是个feign定义,
(3)@EnableFeignClients核心是@Import(FeignClientsRegistrar.class),在这里完成对@FeignClient的扫描和beanDefination的定义。

3 feign扫包和注册细节

feign原理+源码解析_第2张图片
(1)FeignClientsRegistrar的核心方法 registerBeanDefinitions(),包含
registerDefaultConfiguration(metadata, registry); 拿到@EnableFeignClients的属性冰注册配置bean,
 registerFeignClients (metadata, registry); 扫@feignClient并注册,是核心

(2)registerFeignClients细节,
    a 得到scanner:ClassPathScanningCandidateComponentProvider
    b 根据getBasePackages()得到包,默认是SpringbootApplication同路径的包。
    c 声明FeignClient.class的AnnotationTypeFilter,加入到scanner
    d 遍历packages,findCandidateComponents(),通过判断isCandidateComponent拿到class,判断依据是是否满足注解filter(已经加入FeignClient条件)
    e registerFeignClient 注册得到的feign的bd,是用factorybean,FeignClientFactoryBean,扩展了很多feign属性。beanname是feign类的全类名。最终register到容器。

4 FeignClientFactoryBean 解析

feign原理+源码解析_第3张图片
(1)通过getObject()返回bean,调用getTarget()

(2)首先FeignContext context = applicationContext.getBean(FeignContext.class); 拿到feign环境,bean定义是在org.springframework.cloud.openfeign.FeignAutoConfiguration, 核心this.configurations.put(client.getName(), client) 是为每个feign维护了一个环境,放在map中。每个feign实例都能从feignContext里拿到独立的spring容器。T instance = context.getInstance(this.name, type);这种,name就是feignclient注解的 “servicexxxx”。

(3)Feign.Builder builder = get(context, Feign.Builder.class),结合环境生成一个builder,

这个builder也是从spring容器拿的,FeignClientsConfiguration中有两个,一个hystrix的,一个retryer(默认)的。从feignContext里拿到servicename对应的容器,再根据类型拿到Encoder、Decoder、Contract设置builder。这些bean都在FeignClientsConfiguraiton中定义:SpringEncoder、ResponseEntityDecoder、SpringMvcContract

configureFeign(),把application.yml的属性配置进builder中,

(4)核心,处理url后,手动写一个HardCoudeTarget,包含服务名称、接口类名、url,调用loadBalance(target),在这里面通过容器拿到一个Client,client定义在DefaultFeignLoadBalancedConfiguration,实际类型是LoadBalancerFeignClient。

(5)targeter.target(this, builder, context, target) 实现代理。

5 代理的实现过程。

feign原理+源码解析_第4张图片
Feign.Builder和HardCodedTarget,来最终基于feign的动态代理。

(1) build().newInstance(target);

build()融合了FeignBuilder所有内容创建了RefrectiveFeign,先创建SynchronousMethodHandler.Factory,包含LoadBalancerFeignClient、Retryer(负责请求重试)、请求拦截器、日志打印等,用来创建methodHandler。

(2)Map nameToHandler = targetToHandlersByName.apply(target);
根据contract,解析target里面所有方法,包含属性和各种springmvc注解解析,形成SynchronousMethodHandler

(3)Map methodToHandler = new LinkedHashMap();
遍历target的method,形成method和SynchronousMethodHandler的对应map。

(4)InvocationHandler handler = factory.create(target, methodToHandler)-》ReflectiveFeign.FeignInvocationHandler(target, dispatch)  形成代理。最终是SynchronousMethodHandler的invoke方法,实现最终Proxy.newProxyInstance代理过程。

(5)invoke方法: dispatch.get(method).invoke(args) 根据方法本身,拿到MethodHandler,调用MethodHandler的invoke,包含了ribbon的RequestTemplate,负载均衡,融合重试机制。

6 代理invoke的实际执行过程

feign原理+源码解析_第5张图片
(1)核心在FeignInvocationHandler的invoke,dispatch.get(method).invoke(args);实际是SynchronousMethodHandler的invoke,进而executeAndDecode(template);

(2)    Request request = targetRequest(template); 得到request,其实就是target的feignService信息(service名称,类型等)和请求的springmvc方法,结合出
“GET http://service2/serviceInfo HTTP/1.1”这种请求信息。

(3)response = client.execute(request, options);是LoadBalanceFeignClient,先创建一个RibbonRequest,在拿到ribbon的IClientConfig。

(4)lbClient(clientName),从CachingSpringLoadBalancerFactory中拿到FeignLoadBalancer,FeignLoadBalancer里通过spring容器拿到并封装了ribbon的ILoadBalancer(ribbon用的ZoneAwareLoadBalancer)。CachingSpringLoadBalancerFactory采用了缓存。

(5)lbClient(clientName).executeWithLoadBalancer(ribbonRequest,requestConfig).toResponse() 的第二步
    executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();  
    调用路线:submit()->selectServer()->ServerOption.call() 发送http请求。selectServer使用了ribbon的select过程。

7 feign的编解码方式

HttpMessageConverters 默认使用jackson2方式进行序列化和反序列化。但是如果是微服务间频频通信,使用jackson2序列化和反序列化会占用不少系统资源,并且效率较差。可以手动扩展编解码方式,继承AbstractHttpMessageConverter ,使用@FeignClient(value = "service",path ="/nafosRemoteCall/test" , configuration = ProtoFeignConfiguration.class) 进行配置。

你可能感兴趣的:(springcloud)