SpringCloudRPC 远程调用核心原理:Feign 远程调用的执行流程 - 今日头条

本文由 简悦 SimpRead 转码, 原文地址 www.toutiao.com

Feign 远程调用的执行流程由于 Feign 中生成 RPC 接口 JDK 动态代理实例涉及的 InvocationHandler 调用处理器有多种,导致 Fei

由于 Feign 中生成 RPC 接口 JDK 动态代理实例涉及的 InvocationHandler 调用处理器有多种,导致 Feign 远程调用的执行流程稍微有所区别,但是远程调用执行流程的主要步骤是一致的。这里主要介绍与两类 InvocationHandler 调用处理器相关的 RPC 执行流程:

(1)与默认的调用处理器 FeignInvocationHandler 相关的 RPC 执行流程。

(2)与 Hystrix 调用处理器 HystrixInvocationHandler 相关的 RPC 执行流程。

还是以 uaa-provider 启动过程中的 DemoClient 接口的动态代理实例的执行过程为例演示和分析远程调用的执行流程。

FeignInvocationHandler 是默认的调用处理器,如果进行特殊的配置,那么 Feign 将默认使用此调用处理器。

结合 uaa-provider 服务中 DemoClient 的动态代理实例的 hello() 方法远程调用执行过程,这里详细介绍与 FeignInvocationHandler 相关的远程调用执行流程,如图 3-25 所示。

https://p3.toutiaoimg.com/origin/pgc-image/107fd8471d564a79b57c554df286edb8?from=pc

图 3-25 与 FeignInvocationHandler 相关的远程调用执行流程

整体的远程调用执行流程大致分为 4 步,具体如下:

(1)通过 Spring IOC 容器实例完成动态代理实例的装配。

前文讲到,Feign 在启动时会为加上了 @FeignClient 注解的所有远程接口(包括 DemoClient 接口)创建一个 FactoryBean 工厂实例,并注册到 Spring IOC 容器。然后在 uaa-provider 的 DemoRPCController 控制层类中,通过 @Resource 注解从 Spring IOC 容器找到 FactoryBean 工厂实例,通过其 getObject() 方法获取到动态代理实例,装配给 DemoRPCController 实例的成员变量 demoClient。

在需要进行 hello() 远程调用时,直接通过 demoClient 成员变量调用 JDK 动态代理实例的 hello() 方法。

(2)执行 InvocationHandler 调用处理器的 invoke(…)方法。

前面讲到,JDK 动态代理实例的方法调用过程是通过委托给 InvocationHandler 调用处理器完成的,故在调用 demoClient 的 hello() 方法时,会调用到它的调用处理器 FeignInvocationHandler 实例的 invoke(…)方法。

大家知道,FeignInvocationHandler 实例内部保持了一个远程调用方法反射实例和方法处理器的 dispatch 映射。FeignInvocationHandle 在它的 invoke(…)方法中会根据 hello() 方法的 Java 反射实例在 dispatch 映射对象中找到对应的 MethodHandler 方法处理器,然后由后者完成实际的 HTTP 请求和结果的处理。

(3)执行 MethodHandler 方法处理器的 invoke(…)方法。

通过前面关于 MethodHandler 方法处理器的组件介绍,大家都知道,feign 默认的方法处理器为 SynchronousMethodHandler 同步调用处理器,它的 invoke(…)方法主要通过内部 feign。Client 类型的 client 成员实例完成远程 URL 请求执行和获取远程结果。

feign.Client 客户端有多种类型,不同的类型完成 URL 请求处理的具体方式不同。(4)通过 feign.Client 客户端成员完成远程 URL 请求执行和获取远程结果。

如果 MethodHandler 方法处理器 client 成员实例是默认的 feign.Client.Default 实现类,就通过 JDK 自带的 HttpURLConnnection 类完成远程 URL 请求执行和获取远程结果。

如果 MethodHandler 方法处理器实例的 client 客户端是 ApacheHttpClient 客户端实现类,就使用 ApacheHttpClient 开源组件完成远程 URL 请求执行和获取远程结果。

如果 MethodHandler 方法处理器实例的 client 客户端是 LoadBalancerFeignClient 负载均衡客户端实现类,就使用 Ribbon 结算出最佳的 Provider 节点,然后由内部的 delegate 委托客户端成员去请求 Provider 服务,完成 URL 请求处理。

以上 4 步基本上就是 Spring Cloud 中的 Feign 远程调用的执行流程。

然而,默认的基于 FeignInvocationHandler 调用处理器的执行流程在运行机制和调用性能上都满足不了生产环境的要求,大致原因有以下两点:

(1)在远程调用过程中没有异常的熔断监测和恢复机制。

(2)没有用到高性能的 HTTP 连接池技术。

接下来将为大家介绍一种结合 Hystrix 进行 RPC 保护的远程调用处理流程。在该流程中所使用的 InvocationHandler 调用处理器叫作 HystrixInvocationHandler 调用处理器。

这里作为铺垫,首先为大家介绍 HystrixInvocationHandler 调用处理器本身的具体实现。

HystrixInvocationHandler 调用处理器类位于 feign.hystrix 包中,其字节码文件不是处于 feign 核心包 feign-core- .jar 中,而是在扩展包 feignhystrix- .jar 中。这里的 * 表示的是与 Spring Cloud 版本配套的版本号,当 Spring Cloud 的版本为 Finchley.RELEASE 时,feign-core 和 feign-hystrix 两个 JAR 包的版本号都为 9.5.1。

HystrixInvocationHandler 是具备 RPC 保护能力的调用处理器,它实现了 InvocationHandler 接口,对接口的 invoke(…)抽象方法的实现如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package feign.hystrix;
//省略import
final class HystrixInvocationHandler implements InvocationHandler {
...
//... Map映射:Key为RPC方法的反射实例,value为方法处理器
private final Map<Method, MethodHandler> dispatch;
...
public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
 //创建一个HystrixCommand命令,对同步方法调用器进行封装
 HystrixCommand<Object> hystrixCommand =
 new HystrixCommand<Object>
 ( (Setter)this.setterMethodMap.get(method) )
{
 protected Object run() throws Exception {
 try {
SynchronousMethodHandler handler=HystrixInvocationHandler.this.dispatch.get(method)
 return handler.invoke(args);
 } catch (Exception var2) {
 throw var2;
 } catch (Throwable var3) {
 throw (Error)var3;
 }
 }
 protected Object getFallback() {
 //省略HystrixCommand的异常回调
 }
 };
 //根据method的返回值类型,或返回hystrixCommand,或直接执行
 if (this.isReturnsHystrixCommand(method)) {
 return hystrixCommand;
 } else if (this.isReturnsObservable(method)) {
 return hystrixCommand.toObservable();
 } else if (this.isReturnsSingle(method)) {
 return hystrixCommand.toObservable().toSingle();
 } else {
 //直接执行
 return this.isReturnsCompletable(method) ?
 hystrixCommand.toObservable().toCompletable() : hystrixCommand.execute();
 }
 ...
}

HystrixInvocationHandler 调用处理器与默认调用处理器 FeignInvocationHandler 有一个共同点:都有一个非常重要的 Map 类型成员 dispatch 映射,保存着 RPC 方法反射实例到 MethodHandler 方法处理器的映射。

在源码中,HystrixInvocationHandler 的 invoke(…)方法会创建 hystrixCommand 命令实例,对从 dispatch 获取的 SynchronousMethodHandler 实例进行封装,然后对 RPC 方法实例 method 进行判断,判断是直接返回 hystrixCommand 命令实例,还是立即执行其 execute() 方法。默认情况下,都是立即执行它的 execute() 方法。

HystrixCommand 具备熔断、隔离、回退等能力,如果它的 run() 方法执行发生异常,就会执行 getFallback() 失败回调方法,这一点后面会详细介绍。

回到 uaa-provider 服务中 DemoClient 动态代理实例的 hello() 方法的具体执行过程,在执行命令处理器 hystrixCommand 实例的 run() 方法时,步骤如下:

(1)根据 RPC 方法 DemoClient.hello() 的反射实例在 dispatch 映射对象中找到对应的方法处理器 MethodHandler 实例。

(2)调用 MethodHandler 方法处理器的 invoke(…)方法完成实际的 hello() 方法所配置的远程 URL 的 HTTP 请求和结果的处理。

如果 MethodHandler 内的 RPC 调用出现异常,比如远程 server 宕机、网络延迟太大而导致请求超时、远程 server 来不及响应等,hystrixCommand 命令器就会调用失败回调方法 getFallback() 返回回退结果。

而 hystrixCommand 的 getFallback() 方法最终会调用配置在 RPC 接口 @FeignClient 注解的 fallback 属性上的失败回退类中对应的回退方法,执行业务级别的失败回退处理。

使用 HystrixInvocationHandler 方法处理器进行远程调用,总体流程与使用默认的方法处理器 FeignInvocationHandler 进行远程调用大致是相同的。

以 uaa-provider 模块的 DemoClient 中 hello() 方法的远程调用执行过程为例,进行整体流程的展示,具体的时序图如图 3-26 所示。

https://p3.toutiaoimg.com/origin/pgc-image/55de1e9278d0444782c1858b8460153a?from=pc

图 3-26 与 HystrixInvocationHandler 相关的远程调用执行流程

总体来说,使用 HystrixInvocationHandler 处理器的执行流程与使用 FeignInvocationHandler 默认的调用处理器相比大致是相同的。不同的是,HystrixInvocationHandler 增加了 RPC 的保护机制。

Feign 是一个声明式的 RPC 调用组件,它整合了 Ribbon 和 Hystrix,使得服务调用更加简单。Feign 提供了 HTTP 请求的模板,通过编写简单的接口和方法注解就可以定义好 HTTP 请求的参数、格式、地址等信息。

Feign 极大地简化了 RPC 远程调用,大家只需要像调用普通方法一样就可以完成 RPC 远程调用。

Feign 远程调用的核心是通过一系列封装和处理,将以 JAVA 注解方式定义的 RPC 方法最终转换成 HTTP 请求,然后将 HTTP 请求的响应结果解码成 POJO 对象返回给调用者。

Feign 远程调用的完整流程如图 3-27 所示。

https://p3.toutiaoimg.com/origin/pgc-image/9061f4fe6ca8449aa1ac8245d6eac620?from=pc

图 3-27 Feign 远程调用的完整流程

从图 3-27 可以看到,Feign 通过对 RPC 注解的解析将请求模板化。当实际调用时传入参数,再根据参数应用到请求模板上,进而转化成真正的 Request 请求。通过 Feign 及其动态代理机制,Java 开发人员不用再通过 HTTP 框架封装 HTTP 请求报文的方式完成远程服务的 HTTP 调用。

Spring Cloud Feign 具有如下特性:

(1)可插拔的注解支持,包括 Feign 注解和 Spring MVC 注解。

(2)支持可插拔的 HTTP 编码器和解码器。

(3)支持 Hystrix 和它的 RPC 保护机制。

(4)支持 Ribbon 的负载均衡。

(5)支持 HTTP 请求和响应的压缩。

总体来说,使用 Spring Cloud Feign 组件本身整合了 Ribbon 和 Hystrix,可设计一套稳定可靠的弹性客户端调用方案,避免整个系统出现雪崩效应。

  1. 下篇文章给大家讲解的是 SpringCloudRPC 远程调用核心原理:HystrixFeign 动态代理实例的创建流程;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!