本文基于 JDK8 和 SpringBoot 2.1.5.RELEASE,SpringCloud Greenwich.SR1
一般我们使用 openfeign 都使用 spring-cloud 来引入,使用起来也很简单。这里主要记录下它是如何引入和一些注意的地方。
1. Feign 是如何被引入 Sping 并发挥作用的
首先对于只有接口的调用我们肯定想到动态代理,没错 openfeign 使用的就是 Jdk 动态代理。
引入:
- 加入依赖:
org.springframework.cloud
spring-cloud-starter-openfeign
- 加入注解 @EnableFeignClients,同时
可以指定扫描包,也可以指定特定的 FeignClient。
到这里引入就结束了,是不是很简单,那如果使用呢?别急,下面会讲到。
1. 自动配置
涉及无负载的自动配置类:FeignAutoConfiguration,FeignClientsConfiguration。
涉及负载均衡的自动配置类:FeignRibbonClientAutoConfiguration,HttpClientFeignLoadBalancedConfiguration,HttpClientFeignLoadBalancedConfiguration,OkHttpFeignLoadBalancedConfiguration
2. 具体的分析
当引入 @EnableFeignClients SpringBoot 会自动引入 @Import(FeignClientsRegistrar.class),那我们来看下 FeignClientsRegistrar。它 实现了 ImportBeanDefinitionRegistrar 类。
FeignClientsRegistrar 如何被引入?
FeignClientsRegistrar 在 Spring 启动时 AbstractApplicationContext#refresh 方法的 invokeBeanFactoryPostProcessors(beanFactory); 这一步被 ConfigurationClassPostProcessor 引入并在 ConfigurationClassBeanDefinitionReader 中调用。
ConfigurationClassPostProcessor 何时被引入
在 SpringApplication#run(String... args) -> createApplicationContext() ->AnnotationConfigApplicationContext 构造方法 -> AnnotatedBeanDefinitionReader 构造方法引入
我们继续看 FeignClientsRegistrar 类,registerBeanDefinitions 方法会把 @FeignClient 注解的接口封装成 FeignClientFactoryBean 并注册为 bean 定义。供后续的初始化使用。FeignClientFactoryBean 继承了 FactoryBean。熟悉的同学应该都知道,它主要用来实例化一些一些比较复杂的类,通过它的 getObject 方法,就像我们开头讲到的,它返回的就是一个 JDK 动态类,具体的 InvocationHandler 由 InvocationHandlerFactory 生成(如果使用 熔断器,则熔断器会有自己的 InvocationHandler,例如: Sentinel 有 SentinelInvocationHandler)。有兴趣的同学可以自己下代码,这里不做具体分析了。
2. feign 整合熔断器
feign 提供了 Feign.Builder 来整合熔断器,目前 Spring 的熔断器都支持。因为 Hystrix 已经不更新了,可以选择使用阿里的 Sentinel 来做熔断器。一个重要原因是有中文文档,学习成本会比较低,而且阿里的熔断器肯定经历过千锤百炼了。
2.1 整合 Sentinel
加入依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
2.1.0.RELEASE
application.yml 加入配置:
feign:
sentinel:
enabled: true
这样整合就可以了。
2.2 如何使用?
示例:
@FeignClient(name = "ActivityClient", url = "${sys.profile-url}", path = "activity/", fallbackFactory = ActivityClient.ActivityClientHystrix.class)
public interface ActivityClient {
@PostMapping("dispatchRewordRedPaper")
CommonResponse dispatchRewordRedPaper(@RequestBody OrderRewordPaperRequest request);
@Component
class ActivityClientHystrix implements FallbackFactory {
@Resource
private SysConfig sysConfig;
@Override
public ActivityClient create(Throwable cause) {
return request -> {
if (cause instanceof FeignException) {
FeignException cause1 = (FeignException) cause;
if (cause1.status() == HttpStatus.UNAUTHORIZED.value()) {
JwtUtils.resetJwtInSystem(BaseConstants.JWT_INNER, sysConfig.getJwtSecret());
}
return new CommonResponse(cause1.status());
} else if(cause instanceof HystrixTimeoutException){
}{
return new CommonResponse();
}
};
}
}
}
只需要实现 FallbackFactory 或 Fallback 然后在 @FeignClient 中指定就可以了。其实 Fallback 内部也会被封装成 FallbackFactory。
3. Contract
Contract 表示契约,主要用来解析方法请求的注解。这也是之所以能使用 SpringMvc (例如:@RequestMapping) 注解来定义 feign 的原因。
4. 指定具体的请求框架
Feign 目前支持 HttpClient,OkHttpClient 和 LoadBalancerFeignClient(负载),具体可以看 feign.Client 的实现类。通过 Feign.Builder#client 放入注入(当然 Spring 会帮你做这些)具体请看 FeignClientFactoryBean 。
5. 注意:
- @FeignClient 如果不填 url,默认会使用 ribbon 负载均衡,如果没有引入 ribbon 则会报错。