OpenFeign 框架适配模块及实现原理
适配 OpenFeign 框架
Feign 是 SpringCloud 组件中的一个轻量级 RESTful 的 HTTP 客户端,它可以简化接口调用,将 HTTP 调用转换为 RPC 调用,使调用远程接口像调用同进程应用内的接口一样简单。
OpenFeign 则是 SpringCloud 在 Feign 的基础上支持了 SpringMVC 的注解,如 @RequestMapping、@GetMapping、@PostMapping 等。
将 Sentinel 与 OpenFeign 整合使用的目的在于实现熔断降级。由于 OpenFeign 是 SpringCloud 微服务生态中的一个成员,因此 Sentinel 并没有直接提供适配 OpenFeign 的模块,但 SpringCloud Alibaba 提供了将 Sentinel 与 OpenFeign 整合使用的 SpringBoot Starter 包。
本节将介绍如何在微服务项目中使用 SpringCloudAlibaba 提供的可将 Sentinel 与 OpenFeign 整合使用的 SpringBootStarter 包,并分析实现原理。
使用步骤
在使用 OpenFeign 的项目中使用 Sentinel 实现熔断降级只需要进行以下 5 个步骤:
第一步,引入依赖。
借助 spring-cloud-starter-alibaba-sentinel 实现 Sentinel 与 OpenFeign 整合,添加依赖配置的代码如下:
第二步,启用 Sentinel 与 OpenFeign 整合的自动配置。
在 application.yaml 配置文件中添加如下配置,启用 Sentinel 与 OpenFeign 整合的自动配置。
第三步,配置熔断降级规则。
可以基于动态数据源实现,也可以直接调用 DegradeRuleManager 的 loadRules API 硬编码实现。
第四步,给 @FeignClient 注解配置异常回调。
给接口上的 @FeignClient 注解配置 fallback 属性,实现请求被拒绝后的处理,代码如下:
@FeignClient 注解的 fallback 属性要求配置一个类型,并由该类型的 Fallback 处理器实现降级处理。在本例中,Fallback 处理器的类型必须是实现 DemoService 接口的类,如 ServiceDegradeFallback 类,代码如下:
当调用 DemoService 的 getServices 方法发生熔断时,ServiceDegradeFallback 类的 getServices 方法会被调用。在 ServiceDegradeFallback 中可以处理接口降级逻辑。在本例中,当发生熔断时,由 ServiceDegradeFallback 响应一个状态码以告知消费端因服务降级使得本次接口调用失败。
第五步,将 Fallback 处理器注册到 OpenFeign 的 Client 环境隔离的容器中。
编写 Spring 配置类,在 Spring 配置类中注册 Fallback 处理器,代码如下:
将编写的 Spring 配置类添加到 @FeignClient 注解的 configuration 属性中,代码如下:
当满足熔断条件时,Sentinel 会抛出一个 DegradeException,如果 @FeignClient 注解配置了 fallback 属性为 ServiceDegradeFallback 类,则 Sentinel 会从 Bean 工厂中获取 @FeignClient 注解中 fallback 属性配置的 ServiceDegradeFallback 类的实例,并调用 ServiceDegradeFallback 类实现的 DemoService 接口对应的方法。
Sentinel 与 OpenFeign 整合的实现原理
Sentinel 适配 OpenFeign 框架是借助 OpenFeign 框架提供的方法拦截器实现的,通过方法拦截器在目标方法被调用之前、调用之后及发生异常时,分别调用 ContextUtil#enter 方法、SphU#entry 方法、Tracer#trace 方法、Entry#exit 方法和 ContextUtil#exit 方法。
当 Sentinel 与 OpenFeign、Ribbon 整合时,客户端向服务端发起一次请求的过程如图所示:
客户端向服务端发起一次请求的过程的解析如下:
当调用注释了 @FeignClient 注解的 RPC 接口的方法时,由 Sentinel 提供的方法调用拦截器拦截方法的执行,根据接口方法上注解的 URL 生成资源名称,然后调用 Sentinel 的 ContextUtil#enter 方法和 SphU#entry 方法。
在非熔断降级的情况下,继续将请求交给 OpenFeign 的 MethodHandler 处理。
OpenFeign 从 Ribbon 中获取一个服务提供者节点。
OpenFeign 使用 HttpClient 发起 HTTP 请求。
OpenFeign 请求成功时,调用 Sentinel 的 Entry#exit 方法和 ContextUtil#exit 方法,并且在发生异常时,调用 Tracer#trace 方法。
Sentinel 处在接口调用的最前端,Sentinel 统计的指标数据既不会受 Ribbon 的重试影响,也不会受 OpenFeign 的重试影响。
Sentinel 用自己提供的 InvocationHandler 替换 OpenFeign 的 InvocationHandler,从而实现请
求拦截。SentinelInvocationHandler 源码调试如图所示:
InvocationHandler 是 OpenFeign 为接口生成 JDK 动态代理类时所需要的,是接口的方法拦截处理器,Sentinel 通过替换 OpenFeign 的 InvocationHandler 来拦截方法的执行,在 OpenFeign 处理接口调用之前完成熔断降级的检查。
那么,Sentinel 是如何将原本的 FeignInvocationHandler 替换为 SentinelInvocationHandler 的呢?
由于 OpenFeign 通过 Feign.Builder 类创建接口的代理类,因此 Sentinel 直接将 Feign.Builder 替换成了 SentinelFeign.Builder,并由 SentinelFeignAutoConfiguration 自动配置类向 Spring 的 Bean 容器中注入 SentinelFeign.Builder,代码如下:
SentinelFeign.Builder 继承 Feign.Builder 并重写了 build 方法,源码如下:
如源码所示,使用 SentinelFeign.Builder 替换了 InvocationHandlerFactory,所以 OpenFeign 调用 InvocationHandlerFactory#create 方法创建的 InvocationHandler 就变成了 SentinelInvocationHandler。
InvocationHandlerFactory#create 方法负责创建 SentinelInvocationHandler,部分源码如下:
如源码所示,InvocationHandlerFactory 在创建 SentinelInvocationHandler 之前,通过反射从 FeignClientFactoryBean 拿到 RPC 接口上的 @FeignClient 注解的 fallback 属性值,然后根据 fallback 属性配置的 Fallback 处理器类型从 Bean 工厂取得实例,将该 Fallback 处理器传递给 SentinelInvocationHandler,当触发熔断时,SentinelInvocationHandler 就能取得该 Fallback 处理器并调用。
评论