feign原理

  1. Feign远程调用,核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成为HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,放回给调用者。

  2. 流程

    1. @FeignClient远程调用接口
    2. JDK Proxy 动态代理实例
      1. 实现了远程调用接口,通过InvokeHandler调用处理器分发远程调用
    3. InvokeHandler
      1. 根据远程API的Method方法实例,进行MethodHandler方法处理器的分发
    4. MethodHandler
    5. ReuqestTemplate
      1. 根据实参,构造Request实例
    6. Encoder
      1. Reuqest请求的编码
    7. Interceptors,Logger
      1. 负责对请求和返回进行拦截处理,日志记录
    8. feign.Client
      1. 基于负载均衡,重试器,以及不同的HTTP请求框架,发送HTTP请求
    9. http request
    10. 远程server
    11. http response
    12. feign.Client
    13. Decoder
      1. response的解码
    14. Interceptors,Logger
    15. MethodHandler
    16. InvokeHandler
    17. JDK Proxy动态代理实例
    18. Resonse Bean
  3. 即Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数在应用到请求上,进而转化成真正的Request请求,通过Feign以及JAVA的动态代理机制,使得Java开发人员,可以不通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用

  4. 在微服务启动时,Feign会进行包扫描,对加@FeignClient注解的接口,按照注解的规则,创建远程接口的本地JDK Proxy代理实例,然后将这些本地Proxy代理实例,注入到Spring IOC容器中,当远程接口的方法调用,由Proxy代理实例去完成真正的远程访问,并且返回结果

  5. 远程接口的本地JDK Proxy代理实例,有以下特点:

    1. Proxy代理实例,实现了一个加@FeignClient注解的远程调用接口
    2. Proxy代理实例,能在内部进行HTTP请求的封装,以及发送HTTP请求
    3. Proxy代理实例,能处理远程HTTP请求的响应,并且完成结果的解码,然后返回给调用者
  6. 使用:

    1. 加上@FeignClient注解,也就是说,Feign在启动时,会为其创建一个本地JDK Proxy代理实例,并注册到Spring IOC容器
    2. 通过@Resource注解,按照类型匹配,从Spring IOC容器找到这个代理实例,并且装配给需要的成员变量
    3. 为了创建Feign的远程接口的代理实现类,Feign提供了自己的一个默认的调用处理器,叫做FeignInvocationHandler类,该类处于feign-core核心包中,当然,调用处理器可以进行替换,如果Feign与Hystrix结合使用,则会替换成HystrixInvocationHandler调用处理器类,类处于feign-hystrix的jar包中。
  7. FeignInvocationHandler是一个相对简单的类,有一个非常重要的Map类型成员dispatch映射(private final Map<Method, MethodHandler> dispatch;),保存着远程接口方法到MethodHandler方法处理器的映射

  8. 默认的FeignInvocationHandler,在处理远程方法调用的时候,会根据Java反射的方法实例,在dispatch映射对象中,找到对应的MethodHandler方法处理器,然后交给MethodHandler,完成实际的HTTP请求和结果的处理,远程调用接口中有几个方法,其代理实现类的调用处理器FeignInvocationHandler的dispatch成员,就有几个key-value键值对

  9. invoke方法

    1. 首先,根据方法实例,从方法实例对象和方法处理器的映射中,取得方法处理器,然后调用方法处理器的invoke方法
    2. dispatch.get(method).invoke(args)
    3. 根据java反射的方法实例,在dispatch映射对象中,找到对应的MethodHandler方法处理器
    4. 调用MethodHandler方法处理器的invoke()方法,完成实际的Http请求和结果的处理
    5. MethodHandler方法处理器,和JDK动态代理机制中位于java.lang.reflect包的InvocationHandler调用处理器接口,没有任何继承和实现关系,MethodHandler仅仅是Feign自定义的,一个非常简单的接口
  10. 方法处理器MethodHandler

    1. Feign的方法处理器MethodHandler是一个独立的接口,定义在InvocationHandlerFactory接口中,仅仅拥有一个invoke()方法

    2. MethodHandler的invoke()方法,主要职责是完成实际远程URL请求,然后返回解码后的远程URL的响应结果,Feign提供了默认的SynchronousMethodHandler实现类,提供了基本的远程URL的同步请求处理,

    3. final class SynchronousMethodHandler implements MethodHandler {
        //…
        // 执行Handler 的处理
      public Object invoke(Object[] argv) throws Throwable {
              RequestTemplate requestTemplate = this.buildTemplateFromArgs.create(argv);
              Retryer retryer = this.retryer.clone();
      
              while(true) {
                  try {
                      return this.executeAndDecode(requestTemplate);
                  } catch (RetryableException var5) {
                     //…省略不相干代码
                  }
              }
      }
      
        //执行请求,然后解码结果
      Object executeAndDecode(RequestTemplate template) throws Throwable {
              Request request = this.targetRequest(template);
              long start = System.nanoTime();
              Response response;
              try {
                  response = this.client.execute(request, this.options);
                  response.toBuilder().request(request).build();
              }
      }
      }
      
    4. SynchronousMethodHandler的invoke方法,调用了自己的executeAndDecode()请求执行结果解码方法

      1. 首先通过RequestTemplate请求模板实例,生成远程URL请求实例request
      2. 然后用自己的feign客户端client成员,execute()执行请求,并且获取response响应
      3. 对response响应进行结果解码
  11. Feign客户端组件feign.Client

    1. 客户端组件是Feign中一个非常重要的组件,负责端到端的执行URL请求,其核心逻辑:发送request请求到服务器,并接收response响应后进行解码

    2. feign.Client类,是代表客户端的顶层接口,只有一个抽象方法

    3. public interface Client {
        //提交HTTP请求,并且接收response响应后进行解码
        Response execute(Request request, Options options) throws IOException;
      
      }
      
    4. 由于不同的feign.Client实现类,内部完成HTTP请求的组件和技术不同,因此,feign.Client有多个不同的实现,

      1. Client.Default类:默认的feign.Client客户端实现类,内部使用HttpURLConnection完成URL请求处理
      2. ApacheHttpClient类:内部使用Apache httpclient开源组件完成URL请求处理的feign.Client客户端实现类
      3. OkHttpClient类:内部使用OkHttp3开源组件完成URL请求处理的feign.Client客户端实现类
      4. LoadBalancerFeignClient类:内部使用Ribbon负载均衡技术完成URL请求处理的feign.Client客户端实现类
  12. Client.Default类:

    1. 作为默认的Client接口实现类,在Client.Default内部使用JDK自带的HttpURLConnection类实现URL网络请求
    2. 在JDK1.8中,虽然在HttpURLConnection底层,使用了非常简单的HTTP连接池技术,但是其HTTP连接的复用能力,实际是非常弱的,性能当然也很低
  13. ApacheHttpClient类

    1. ApacheHttpClient客户端类的内部,使用Apache HttpClient开源组件完成URL请求的处理
    2. 从代码开发的角度而言,Apache HttpClient相比较传统JDK自带的URLConnection,增加了易用性和灵活性,他不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口,即提高了开发效率,也方便提高代码的健壮性
    3. 从性能角度而言,ApacheHttpClient带有连接池的功能,具备优秀的HTTP连接的复用能力
    4. ApacheHttpClient类处于feign-httpclient的专门的jar包中,如果使用,还需要通过Maven依赖或者其他的方式,导入配套版本的专门的jar包
  14. OkHttpClient类

    1. OkHttpClient客户端类的内部,使用OkHttp3开源组件完成URL请求处理,OkHttp3开源组件由Square公司开发,用于替代HttpUrlConnection和ApacheHttpClient,由于OkHttp3较好的支持SPDY协议(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验),从Android4.4开始,google已经开始将Android源码中的HttpURLConnection请求类使用OkHttp进行了替换,也就是说,对于Android移动端APP开发来说,OkHttp3组件,世纪初的开发组件之一
  15. LoadBalancerFeignClient

    1. LoadBalancerFeignClient内部使用了Ribbon客户端负载均衡技术完成URL请求处理,在原理上,简单的使用了delegate包装代理模式,Ribbom负载均衡组件计算出合适的服务端server后,由内部包装类delegate代理客户端完成到服务端server的HTTP请求,所封装的delegate客户端代理实例的类型,可以是Client.Default默认客户端,也可以是ApacheHttpClient客户端类或OkHttpClient高性能客户端类,还可以其他的定制的feign.Client客户端实现类型
  16. Feign远程调用的执行流程

    1. 由于Feign远程调用接口的JDK Proxy实例的InvokeHandler调用处理器有很多种,导致Feign远程调用的执行流程,也稍微有所区别,但是远程调用执行流程的主要步骤,是一致的
    2. 与FeignInvocationHandler相关的远程调用执行流程
      1. FeignInvocationHandler是默认的调用处理器,如果不对Feign做特殊的配置,则Feign将使用此调用处理器,
      2. 第一步:通过Spring IOC容器实例,装配代理实例,然后进行远程调用
        1. Feign在启动时,会为加上了@FeignClient注解的所有远程接口,创建一个本地JDK Proxy代理实例,并注册到Spring IOC容器
        2. 然后本实例的controller代用代码中,通过@Resouce注解,按照类型或者名称进行匹配,从Spring IOC容器找到这个代理实例,并且装配给@Resource注解所在的成员变量,调用JDK Proxy动态代理实例的比如hello()方法
      3. 第二步:执行InvokeHandler调用处理器的invoke方法
        1. JDK Proxy动态代理实例的真正的方法调用过程,具体是通过InvkeHandler调用处理器完成的,所以这里的clientProxy代理实例,会调用到默认的FeignInvocationHandler调用处理器实例的invoke方法
        2. 默认的调用处理器FeignInvocationHandler,内部保持了一个远程调用方法实例和方法处理器的一个key-value键值对的Map映射,FeignInvocationHandler在其invoke方法中,会根据java反射的方法实例,在dispatch映射对象中,找到对应的MethodHandler方法处理器,然后由后者完成实际的HTTP请求和结果的处理
        3. 所以,FeignInvocationHandler会从自己的dispatch映射中,找到hello()方法所对应的MethodHandler方法处理器,然后调用invoke方法
      4. 第三步:执行MethodHandler方法处理器的invoke方法
        1. feign默认的方法处理器为SynchronousMethodHandler,其invoke方法主要是通过内部成员feign客户端成员client,完成远程URL请求执行和获取远程结果
        2. feign.Client客户端有多种类型,不同的类型,完成URL请求处理的具体方式不同
      5. 第四步:通过feign.Client客户端成员,完成远程URL请求执行和获取远程结果
        1. 如果MethodHandler·方法处理器实例中declient客户端,是默认的feign.Client.Default实现类,则使用JDK自带的HttpURLConnection类,完成远程URL请求执行和获取远程结果
        2. 如果MethodHandler方法处理器实例中的client客户端,是ApacheHttpClient客户端实现类,则使用ApacheHttpClient开源组件,完成远程URL请求执行和获取远程结果
    3. SynchronousMethodHandler并不是直接完成远程URL的请求,而是通过负载均衡机制,定位到合适的远程server服务器,然后再完成真正的远程URL请求,换句话说,SynchronousMethodHandler实例的client成员,其实例不是feign.Client.Default类型,而是LoadBalancerFeignClient客户端负载均衡类型,因此,上面的第三步:如果进一步细分的话,大致如下:
      1. 首先通过SynchronousMethodHandler内部的client实例,实质为负责客户端负载均衡LoadBalancerFeignClient实例,首先查找到远程的server服务端,
      2. 然后再由LoadBalancerFeignClient实例内部包装的feign.Client.Default内部类实例,去请求server端服务器,完成URL请求处理
    4. 默认的FeignInvocationHandler相关的远程调用执行流程,在运行机制以及调用性能上,满足不了生产环境的要求
      1. 没有远程调用过程中的熔断检测和恢复机制
      2. 也没有用到高性能的HTTP连接池技术
  • 23
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值