前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Feign 体系架构解析

Feign 体系架构解析

作者头像
程序员波特
发布2024-01-29 09:02:46
890
发布2024-01-29 09:02:46
举报
文章被收录于专栏:魔法书魔法书

正所谓麻雀虽小五脏俱全,HTTP 调用看着简单,实则下面隐藏的是一套非常复杂的流程。

从上古时代 jsp+servlet,到后面的 SpringMVC,在 HTTP 请求解析和封装上同样是煞费苦心。

在这里插入图片描述
在这里插入图片描述

我们在学习中经常会碰到这种 case,有些开源组件不显山来不露水,乍一看功能很简单,配置起来也不麻烦,让人感觉实现起来也不难。实际上我们所看到的只是冰山上的一角,在冰山下面隐藏的巨大基座才是这套技术的全貌。

就像 Feign 一样,往往以一个注解开场的项目,背后的故事都不简单。接下来,我们就潜入深海,看看 Feign 这座冰山的架构全景。

武装到牙齿 - Feign 体系架构

图片来自于电影《黑衣人》
图片来自于电影《黑衣人》

大家有没有看过一部叫做《黑衣人》的电影?这部电影讲述了搞笑特工联手对抗外星生物,维护世界和平的故事。里面有一句经典台词叫做武装到牙齿,意思是几位特工身上任何部位都被武装到位,甚至牙齿也不放过。

Feign 就是这样一位被武装到牙齿的特工,Feign 的每个运作流程都包含了复杂的业务处理,NetflixFeign 更是关爱有加,甚至还给配备了两件重武器:RibbonHystrix。由于Feign 的调用链路比较长,所以我删减了很多支线剧情,只玩主线剧情,我们分为上下半场两张图给大家介绍 Feign 的架构全貌。

如果用一句话来介绍 Feign,那就是:声明一个代理接口,服务调用者通过调用这个代理接口的方式来调用远程服务。这样一来,调用远程方法就如同调用本地接口一样方便。

上半场 - 构建请求

左右护法:大伙现在看出 Feign 是个什么腕儿了吗?看那身旁站着 Ribbon Hystrix,左青龙右白虎,给 Feign 保驾护航。没错,Feign 自己兜里就揣着Ribbon Hystrix 两把重武器,引入Feign依赖的同时这两个组件也会被一同引入。

  • Ribbon:利用负载均衡策略选定目标机器。
  • Hystrix:根据熔断器的开启状态,决定是否发起此次调用
  1. 动态代理:Feign 是通过一个代理接口进行远程调用,这一步就是为了构造接口的动态代理对象,用来代理远程服务的真实调用,这样你就可以像调用本地方法一样发起 HTTP 请求,不需要像 Ribbon 或者 Eureka 那样在方法调用的地方提供服务名。在 Feign 中动态代理是通过Feign.build返回的构造器来装配相关参数,然后调用 ReflectFeignnewInstance 方法创建的。这里就应用到了Builder设计模式。
  2. Contract:协议,顾名思义,就像HTTP协议,RPC 协议一样,Feign 也有自己的一套协议的规范,只不过他解析的不是 HTTP 请求,而是上一步提到的动态代理类。通过解析动态代理接口+Builder 模式,Contract 协议会构造复杂的元数据对象 MethodMetadata,这里面包含了动态代理接口定义的所有特征。接下来,根据这些元数据生成一系列 MethodHandler 对象用来处理 RequestResponse 请求。Contract 具有高度可扩展性,可以经由对 Contract 的扩展,将 Feign 集成到其他开源组件之中。
番外篇 - 关于 Builder 模式

Builder 是设计模式中的一种,用来简化复杂组件的装配过程,假如用传统方式构建一个House 类,那应该是这样写:

代码语言:javascript
复制
House house = ne House();
house.setWindow("open");
house.setDoor("close");

Builder 模式是用链式构造的方式创建复杂对象,比如这种形式House.builder().window("open").door("close").build()这里教大家一个简单的实现方式,那就是 lombok 小工具的@Builder 注解,只要在 pom中添加 lombok 依赖,并且在 IDE 中添加 lombok 的插件,就可以用注解的方法,不用写一行代码就能实现 Builder 模式。

下半场 - 发起调用
  1. 拦截器 :拦截器是 Spring 处理网络请求的经典方案,Feign 这里也沿用了这个做法,通过一系列的拦截器对 Request 和 Response 对象进行装饰,比如通过 RequestInterceptorRequest 对象构造请求头。整装待发之后,就是正式发起调用的时候了。
  2. 发起请求:又到了左右护法的出场镜头了。这哼哈二将绝不放过开头和结尾两处重要镜头,正所谓从头到尾都参与了进来。重试:Feign 这里借助Ribbon 的配置重试器实现了重试操作,可以指定对当前服务节点发起重试,也可以让 Feign 换一个服务节点重试。
  3. 降级Feign 接口在声明时可以指定 Hystrix 的降级策略实现类,如果达到了 Hystrix 的超时判定,或得到了异常结果,将执行指定的降级逻辑。

Feign 之动态代理

动态代理 是面试场景里的高频问题,从 Spring 中 AOP 的实现方式,到让自己手写一个动“态代理实例,这个话题仿佛成了面试中很有仪式感的一个问题。面试官开口问到 请说出你对 ” aop ”的理解 ,感觉就像汪峰导师在问 你的梦想是什么 。

可是代理就代理好了,为什么要加个 “动态” 二字呢,难道还有静态代理一说?简单的说,所谓动态是相对于 “静态编译” 来说的。在 java 中,假如我们在编译期不知道这个对象是何方神圣,只能等待程序执行的时候,也就在是运行期才能知道,那么我们就称之为 “动态” 获取对象(比如通过类名+ 反射创建一个实例)。而所谓 “动态代理” ,就是指在运行期指定一个代理对象,以接管的方式执行后续的任务。就是这么简单。

Feign 的 动态代理 是个偷天换日的过程。我们把目标服务看做一个新娘子,当服务调用请求发出后,一伙迎亲车队浩浩荡荡地出发去迎亲。这时候,一伙打着 Feign 名号的抢亲小队出现了,他们利用 “动态代理” 的方式,截胡了迎亲车队,自个儿当起了新郎官去接新娘。我们这就来看看这伙抢亲小队是怎么工作的。

抢亲小队 - 截胡方法调用

问:截胡迎亲小队总共分几步?总共分四步:

在这里插入图片描述
在这里插入图片描述

我们来一起看下Feign 的源码。

  1. ·GetObject· :原配的迎亲小队出发了,一路喊着 “接对象咯”(getObject),成功吸引到了抢亲小队的注意力。
  • 这一步是 FeignClientFactoryBeangetObject 方法发起的,为了获取一个可以发起远程调用的实体方法,只是这时它还不知道,getObject ()方法获取到的其实是一个代理对象。
  • 我们知道 Feign 实际上是调用了@FeignClient 注解所修饰的接口,FeignClientFactoryBean 封装了这个接口中所包含的配置信息,比如Eureka服务名称,服务调用的路径,降级逻辑的处理类,等等。
  1. 创建代理对象:一伙抢亲小队听到风声,立马开始着手准备埋伏。在上一步的getObject 方法的最后做好了埋伏,开始了偷天换日的过程。
  • 上一步中 getObject 最后一行,经由 Targeter 类的转发,抢亲小队登场了。
  • 下面就是创建代理对象的时候了,Feign 的所有代理实例均通过ReflectiveFeign.newInstance 创建,他的底层是采用 Builder 模式,将@FeignClient 接口的特征,方法名,参数等等一系列信息提取出来,拼装成Java 反射机制中通用的 Method 类。
  • 偷天换日:这一步是整个动态代理机制中的核心操作。在newInstance·的创建过程中,Feign 通过实现 JDK 的 InvocationHandler接口(所有动态代理方案几乎都和它有关联),将自己的 Handler 和上一步组装的 Method 进行了关联,这样一来,所有对这个接口方法的调用,都将被 Feign 自定义的InvocationHandler 给接管。这种动态代理的方式,我们叫做 JDK 动态代理
  • 所有一切就绪,就等截胡方法调用了
  1. 拦截请求:这时迎亲车队经过了,因为我们在前一步已经做了埋伏,这个方法调用立马被我们自己人,也就是上一步中自定义的InvocationHandler 截胡了。
  • SynchronousMethodHandler 这时接管了 invoke 方法。构造 Request 请 求,装模作样当起了新郎官。(在构造 Request 请求的同时还会涉及一系列的参数拼装和加密等步骤)
  1. 发起调用:最后一步,借助 LoadBalancerFeignClient 发起了真正的HTTP 请求。从这个类的名字大家可以看到,似乎和负载均衡有点关系?没错,这个就是 Feign 和 Ribbon组合而成的一个 Client 类,它会利用 Ribbon 实现超时重试等操作。前面讲到过,Feign 是武装到牙齿的组件,每一步的背后都有非常复杂的处理流程。

Spring 的动态代理

Spring 的 AOP 有两种动态代理方式,其中一种就是前面讲到的Feign采用的方式:JDK 动态代理。在 Spring 中通过 JdkDynamicAopProxy 实现。它有两个特点

  • 实现 InvocationHandler 接口,接管invoke方法实现自己的业务逻辑,所有调用都会被传递到 InvocationHandlerinvoke 方法,通过 Proxy.newProxyInstance 获取动态代理对象
  • 被代理的对象必须实现了某个接口,不能代理无接口的类。Spring 还有一种动态代理的方式,那就是 CGLIB,它并不强制代理类实现某个接口。在实际使用中,CGLIB 在代理对象的性能方面比 JDKDynamic 要快很多,但是在创建代理对象上的时间花费也相当长。所以,如果你的类并没有实现接口,或者是单例模式的类不需要重复创建,建议使用 CGLIB 的方式。

本文已收录至我的个人网站:程序员波特,主要记录Java相关技术系列教程,共享电子书、Java学习路线、视频教程、简历模板和面试题等学习资源,让想要学习的你,不再迷茫。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-01-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 武装到牙齿 - Feign 体系架构
    • 上半场 - 构建请求
      • 番外篇 - 关于 Builder 模式
        • 下半场 - 发起调用
        • Feign 之动态代理
          • 抢亲小队 - 截胡方法调用
          • Spring 的动态代理
          相关产品与服务
          负载均衡
          负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档