本文主要讲述 Feign 是如何注册到 Spring 容器、融合 Ribbon进行负载均衡、进行 RPC 调用。
简单提一下项目中一般都是如何使用 Feign 的,首先声明一个@FeignClient
,定义 RPC 调用方法
,然后像调用本地方法一样,调用远程服务的方法
// 定义 FeignClient
@FeignClient(value = "service-order",path = "/order")
public interface OrderFeignService {
@RequestMapping("/findOrderByUserId/{userId}")
R findOrderByUserId(@PathVariable("userId") Integer userId);
}
// 调用远程服务
@Autowired
OrderFeignService orderFeignService;
R findOrderByUserId(@PathVariable("id") Integer id) {
//feign调用
R result = orderFeignService.findOrderByUserId(id);
return result;
}
这样一来,我们省去了自己去配置 RestTemplate 或其他 HTTPClient 的麻烦,但是简单方便的同时你是否会有一些疑惑:
我们使用@Autowired注入的 OrderFeignService,那么它一定是一个 Spring Bean,它是什么时候,如何被注入到 Spring 容器的?
- Feign 是如何执行 findOrderByUserId()的?
- @RequestMapping 是 SpringMVC 的注解呀,怎么在 Feign 中也会生效呢?
- Feign 是如何整合 Ribbon 的?
- Ribbon 是如何获取注册中心的服务的?
- Ribbon 是如何进行负载均衡的?
理解了上面的这些问题,我们也就明白了 Feign 是如何进行调用的,那么带着这些问题我们来一步步分析
@FeignClient 注册
@EnableFeignClients 注解
根据 SpringBoot 自动装配的思想,先猜想下一定会有@Enablexxx ,然后再有@Import(xxx.class),来进行 Feign 的自动注入
要使用 Feign 首先要
- 引入 Feign 的 Maven 依赖,接着一定要在启动类上添加注解
@EnableFeignClients
@EnableFeignClients @Import(FeignClientsRegistrar.class)
- 注册
@FeignClient
FeignClientsRegistrar.class
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
this.registerDefaultConfiguration(metadata, registry);
this.registerFeignClients(metadata, registry);
}
1. 扫描并注册 FeignClient 为 BeanDefinition
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// ...,省略了部分源码
扫描所有 @FeignClient 标注的类
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class);
// ...
for (String basePackage : basePackages) {
// ...
Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
// ...
// 注册 FeignClient 为 BeanDefinition
registerFeignClient(registry, annotationMetadata, attributes);
} } }
2. 将 FeignClient 包裹成 FeignClientFactoryBean
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
// 将 FeignClient 包裹成 FeignClientFactoryBean
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class); // ... 一堆definition.addPropertyValue()
definition.addPropertyValue("fallbackFactory",attributes.get("fallbackFactory")); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
// ...
// 注册
FeignClientFactoryBean BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] {
alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}