1 架构图。
2 feign的初始化扫包原理。
(1)feign使用,是main上的@EnableFeignClients 和 api的@FeignClient(value = "serviceXXX") 搭配使用。
(2)@FeignClient 是个自定义注解没啥东西只是个feign定义,
(3)@EnableFeignClients核心是@Import(FeignClientsRegistrar.class),在这里完成对@FeignClient的扫描和beanDefination的定义。
3 feign扫包和注册细节
(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 解析
(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.Builder和HardCodedTarget,来最终基于feign的动态代理。
(1) build().newInstance(target);
build()融合了FeignBuilder所有内容创建了RefrectiveFeign,先创建SynchronousMethodHandler.Factory,包含LoadBalancerFeignClient、Retryer(负责请求重试)、请求拦截器、日志打印等,用来创建methodHandler。
(2)Map
根据contract,解析target里面所有方法,包含属性和各种springmvc注解解析,形成SynchronousMethodHandler
(3)Map
遍历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的实际执行过程
(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