调用FeignClient的时候,SpringCloud到底干了哪些事情?——OpenFeign原理分析
1. 前言
调用FeignClient的时候,SpringCloud到底干了哪些事情?
这块工作是SpringCloud的OpenFeign模块做的,了解之后可以有几个用处:
- 高效排查各种问题。
- 定制Feign的各个子模块。
- 考察候选人,了解候选人的技术深度。
2. 摘要
- Spring容器中注入Client对应的Bean
- 扫描@FeignClient 对应的Bean,生成BeanDefinition 放进spring容器
- 扫描到对应的BeanDefinition的时候,生成Client对应的Bean到spring容器中
- 调用Client进行远程调用
- 调用Client对应的bean(实际上是动态代理)
- feign.ReflectiveFeign
- 动态代理调用FeignClient
- feign.SynchronousMethodHandler#invoke
- feign.SynchronousMethodHandler#executeAndDecode
- FeignClient执行对应后续操作
- org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute
- 负载均衡
- 获取对应的LoadBalanceClient实例
- org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#lbClient
- 分配服务器,进行请求
- com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(S, com.netflix.client.config.IClientConfig)
- org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute
- 其他附带知识
- tsf根据泳道进行服务过滤 com.tencent.cloud.tsf.lane.instrument.loadbalancer.TsfRibbonLaneLoadbalancer#filterAllServer
- 获取对应的LoadBalanceClient实例
- HttpClient进行请求
- feign.httpclient.ApacheHttpClient#execute
- org.apache.http.impl.client.CloseableHttpClient#execute(org.apache.http.client.methods.HttpUriRequest)
- 调用Client对应的bean(实际上是动态代理)
3. 流程详解
3.1. Spring容器中注入Client对应的Bean
3.1.1. 扫描@FeignClient 对应的Bean,生成BeanDefinition 放进spring容器
- @EnableFeignClients开启openfeign功能
- @EnableFeignClients 会import FeignClientsRegistrar
- FeignClientsRegistrar 找到所有FeignClient注解的Class,生成对应的BeanDefinition供下一步使用
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 省略代码……
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
// 省略代码……
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// 省略代码……
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
// 省略代码……
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
3.1.2. 扫描到对应的BeanDefinition的时候,生成Client对应的Bean到spring容器中
- 代码位置:org.springframework.cloud.openfeign.FeignClientFactoryBean
- 说明:
- 因为是FactoryBean所以在spring的生成bean阶段,会调用getObject方法生成bean丢进容器。
- getObject 主要做的事情是生成一个动态代理对象
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
// 省略代码……
@Override
public Object getObject() throws Exception {
return getTarget();
}
<T> T getTarget() {
// 省略代码……
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
3.2. 调用Client进行远程调用
3.2.1. 调用Client对应的bean(实际上是动态代理)
- 代码位置:feign.ReflectiveFeign
3.2.2. 动态代理调用FeignClient
- feign.SynchronousMethodHandler#invoke
final class SynchronousMethodHandler implements MethodHandler {
// 省略代码……
@Override
public Object invoke(Object[] argv) throws Throwable {
// 省略代码……
response = client.execute(request, options);
// 省略代码……
- client对象在以下configuration中实例化(这里以 ribbon+httpclient的配置为例)
- org.springframework.cloud.openfeign.ribbon.HttpClientFeignLoadBalancedConfiguration#feignClient
3.2.3. FeignClient执行对应后续操作
- org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute
3.2.4. 负载均衡
- 获取对应的LoadBalanceClient实例
- org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#lbClient
- 这里的设计也很巧妙。
- 每个feignClient对应的ribbon配置分别放在一个springContext中
- 不过,感觉有点过度设计,为了不同client的配置隔离,大大提升了学习成本。
- 具体实现参考:springcloud @RibbonClients 与NamedContextFactory
- 分配服务器,进行请求
- com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(S, com.netflix.client.config.IClientConfig)
- org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute
- 其他附带知识
- tsf根据泳道进行服务过滤 com.tencent.cloud.tsf.lane.instrument.loadbalancer.TsfRibbonLaneLoadbalancer#filterAllServer
3.2.5. HttpClient进行请求
- feign.httpclient.ApacheHttpClient#execute
- org.apache.http.impl.client.CloseableHttpClient#execute(org.apache.http.client.methods.HttpUriRequest)
4. 参考
Spring Cloud OpenFeign 原理浅析
springcloud @RibbonClients 与NamedContextFactory
Spring Cloud组件那么多超时设置,如何理解和运用?