上一节我们了解了feign的主要功能,它就像一个自拍杆一样,方便了Eureka的远程调用。可是怎么看都觉得Feign的功能很简单嘛,职责也比较单一,那它和Ribbon一样,都是大闲人了?
正所谓麻雀虽小五脏俱全,HTTP调用看着简单,实则下面隐藏的是一套非常复杂的流程。从上古时代jsp+servlet,到后面的SpringMVC,在HTTP请求解析和封装上同样是煞费苦心。
Feign体系架构解析-武装到牙齿 - 图1
我们在学习中经常会碰到这种case,有些开源组件不显山来不露水,乍一看功能很简单,配置起来也不麻烦,让人感觉实现起来也不难。实际上我们所看到的只是冰山上的一角,在冰山下面隐藏的巨大基座才是这套技术的全貌。
Ribbon :喂~老师这是在形容我吗?
Feign : 滚~
就像Feign一样,往往以一个注解开场的项目,背后的故事都不简单。接下来,我们就潜入深海,看看Feign这座冰山的架构全景。

武装到牙齿 - Feign体系架构

Feign体系架构解析-武装到牙齿 - 图2
图片来自于电影《黑衣人》,图片版权归属原作者
大家有没有看过一部叫做《黑衣人》的电影?这部电影讲述了搞笑特工联手对抗外星生物,维护世界和平的故事。里面有一句经典台词叫做武装到牙齿,意思是几位特工身上任何部位都被武装到位,甚至牙齿也不放过。
Feign就是这样一位被武装到牙齿的特工,Feign的每个运作流程都包含了复杂的业务处理,Netflix对Feign更是关爱有加,甚至还给配备了两件重武器:Ribbon和Hystrix。由于Feign的调用链路比较长,所以我删减了很多支线剧情,只玩主线剧情,我们分为上下半场两张图给大家介绍Feign的架构全貌。
如果用一句话来介绍Feign,那就是:声明一个代理接口,服务调用者通过调用这个代理接口的方式来调用远程服务。这样一来,调用远程方法就如同调用本地接口一样方便。

上半场 - 构建请求

Feign体系架构解析-武装到牙齿 - 图3

  1. 左右护法:大伙现在看出Feign是个什么腕儿了吗?看那身旁站着Ribbon和Hystrix,左青龙右白虎,给Feign保驾护航。没错,Feign自己兜里就揣着Ribbon和Hystrix两把重武器,引入Feign依赖的同时这两个组件也会被一同引入。
    • Ribbon:利用负载均衡策略选定目标机器
    • Hystrix:根据熔断器的开启状态,决定是否发起此次调用
  2. 动态代理:Feign是通过一个代理接口进行远程调用,这一步就是为了构造接口的动态代理对象,用来代理远程服务的真实调用,这样你就可以像调用本地方法一样发起HTTP请求,不需要像Ribbon或者Eureka那样在方法调用的地方提供服务名。在Feign中动态代理是通过Feign.build返回的构造器来装配相关参数,然后调用ReflectFeign的newInstance方法创建的。这里就应用到了Builder设计模式,稍后番外篇会给大家说一个实现Builder模式的简单方法。
  3. Contract:协议,顾名思义,就像HTTP协议,RPC协议一样,Feign也有自己的一套协议的规范,只不过他解析的不是HTTP请求,而是上一步提到的动态代理类。通过解析动态代理接口+Builder模式,Contract协议会构造复杂的元数据对象MethodMetadata,这里面包含了动态代理接口定义的所有特征。接下来,根据这些元数据生成一系列MethodHandler对象用来处理Request和Response请求。

    • Contract具有高度可扩展性,可以经由对Contract的扩展,将Feign集成到其他开源组件之中。

      番外篇 - 关于Builder模式

      Builder是设计模式中的一种,用来简化复杂组件的装配过程,假如用传统方式构建一个House类,那应该是这样写:
      1. House house = ne House();
      2. house.setWindow("open");
      3. house.setDoor("close");
      而Builder模式是用链式构造的方式创建复杂对象,比如这种形式
      1. House.builder().window("open").door("close").build()
      这里教大家一个简单的实现方式,那就是lombok小工具的@Builder注解,只要在pom中添加lombok依赖,并且在IDE中添加lombok的插件,就可以用注解的方法,不用写一行代码就能实现Builder模式。

      下半场 - 发起调用

      Feign体系架构解析-武装到牙齿 - 图4
  4. 拦截器 :拦截器是Spring处理网络请求的经典方案,Feign这里也沿用了这个做法,通过一系列的拦截器对Request和Response对象进行装饰,比如通过RequestInterceptor给Request对象构造请求头。整装待发之后,就是正式发起调用的时候了。

  5. 发起请求:又到了左右护法的出场镜头了。这哼哈二将绝不放过开头和结尾两处重要镜头,正所谓从头到尾都参与了进来。
    • 重试:Feign这里借助Ribbon的配置重试器实现了重试操作,可以指定对当前服务节点发起重试,也可以让Feign换一个服务节点重试。
    • 降级:Feign接口在声明时可以指定Hystrix的降级策略实现类,如果达到了Hystrix的超时判定,或得到了异常结果,将执行指定的降级逻辑。Hystrix降级熔断的内容,将在下一个大章节和大家见面。

      小结

      学习Tips:当面试官问你“拦截器可以应用到哪些场景”的时候,你会怎么回答呢?我们经常会在不同的开源项目中看到相近的功能,比如说动态代理、拦截器等,这些功能点在各大开源项目中都有广泛应用。建议同学们可以做横向比较,看看一项技术在不同的开源项目中都应用到了哪些业务场景中,加深对技术的理解,这样一来面试官再也难不倒你了。
      这一节带大家了解了Feign的架构全貌,下一节我们趁热打铁,直接创建一个Feign的消费者,然后再来回顾一下Eureka、Ribbon和Feign在方法调用时的不同编程体验。