沂水网站建设,东莞网站制作网络建设公司,北碚区建设银行网站,博兴建设局网站正所谓麻雀虽小五脏俱全#xff0c;HTTP 调用看着简单#xff0c;实则下面隐藏的是一套非常复杂的流程。
从上古时代 jspservlet#xff0c;到后面的 SpringMVC#xff0c;在 HTTP 请求解析和封装上同样是煞费苦心。 我们在学习中经常会碰到这种 case#xff0c;有些开源…正所谓麻雀虽小五脏俱全HTTP 调用看着简单实则下面隐藏的是一套非常复杂的流程。
从上古时代 jspservlet到后面的 SpringMVC在 HTTP 请求解析和封装上同样是煞费苦心。 我们在学习中经常会碰到这种 case有些开源组件不显山来不露水乍一看功能很简单配置起来也不麻烦让人感觉实现起来也不难。实际上我们所看到的只是冰山上的一角在冰山下面隐藏的巨大基座才是这套技术的全貌。
就像 Feign 一样往往以一个注解开场的项目背后的故事都不简单。接下来我们就潜入深海看看 Feign 这座冰山的架构全景。
武装到牙齿 - Feign 体系架构 大家有没有看过一部叫做《黑衣人》的电影这部电影讲述了搞笑特工联手对抗外星生物维护世界和平的故事。里面有一句经典台词叫做武装到牙齿意思是几位特工身上任何部位都被武装到位甚至牙齿也不放过。
Feign 就是这样一位被武装到牙齿的特工Feign 的每个运作流程都包含了复杂的业务处理Netflix 对 Feign 更是关爱有加甚至还给配备了两件重武器Ribbon 和 Hystrix。由于Feign 的调用链路比较长所以我删减了很多支线剧情只玩主线剧情我们分为上下半场两张图给大家介绍 Feign 的架构全貌。
如果用一句话来介绍 Feign那就是声明一个代理接口服务调用者通过调用这个代理接口的方式来调用远程服务。这样一来调用远程方法就如同调用本地接口一样方便。
上半场 - 构建请求
左右护法大伙现在看出 Feign 是个什么腕儿了吗看那身旁站着 Ribbon和 Hystrix左青龙右白虎给 Feign 保驾护航。没错Feign 自己兜里就揣着Ribbon 和 Hystrix 两把重武器引入Feign依赖的同时这两个组件也会被一同引入。
Ribbon利用负载均衡策略选定目标机器。Hystrix根据熔断器的开启状态决定是否发起此次调用
动态代理Feign 是通过一个代理接口进行远程调用这一步就是为了构造接口的动态代理对象用来代理远程服务的真实调用这样你就可以像调用本地方法一样发起 HTTP 请求不需要像 Ribbon 或者 Eureka 那样在方法调用的地方提供服务名。在 Feign 中动态代理是通过Feign.build返回的构造器来装配相关参数然后调用 ReflectFeign 的newInstance 方法创建的。这里就应用到了Builder设计模式。Contract协议顾名思义就像HTTP协议RPC 协议一样Feign 也有自己的一套协议的规范只不过他解析的不是 HTTP 请求而是上一步提到的动态代理类。通过解析动态代理接口Builder 模式Contract 协议会构造复杂的元数据对象 MethodMetadata这里面包含了动态代理接口定义的所有特征。接下来根据这些元数据生成一系列 MethodHandler 对象用来处理 Request和 Response 请求。Contract 具有高度可扩展性可以经由对 Contract 的扩展将 Feign 集成到其他开源组件之中。
番外篇 - 关于 Builder 模式
Builder 是设计模式中的一种用来简化复杂组件的装配过程假如用传统方式构建一个House 类那应该是这样写:
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 模式。
下半场 - 发起调用
拦截器 拦截器是 Spring 处理网络请求的经典方案Feign 这里也沿用了这个做法通过一系列的拦截器对 Request 和 Response 对象进行装饰比如通过 RequestInterceptor 给 Request 对象构造请求头。整装待发之后就是正式发起调用的时候了。发起请求又到了左右护法的出场镜头了。这哼哈二将绝不放过开头和结尾两处重要镜头正所谓从头到尾都参与了进来。重试Feign 这里借助Ribbon 的配置重试器实现了重试操作可以指定对当前服务节点发起重试也可以让 Feign 换一个服务节点重试。降级Feign 接口在声明时可以指定 Hystrix 的降级策略实现类如果达到了 Hystrix 的超时判定或得到了异常结果将执行指定的降级逻辑。
Feign 之动态代理
动态代理 是面试场景里的高频问题从 Spring 中 AOP 的实现方式到让自己手写一个动“态代理实例这个话题仿佛成了面试中很有仪式感的一个问题。面试官开口问到 请说出你对 ” aop ”的理解 感觉就像汪峰导师在问 你的梦想是什么 。
可是代理就代理好了为什么要加个 “动态” 二字呢难道还有静态代理一说简单的说所谓动态是相对于 “静态编译” 来说的。在 java 中假如我们在编译期不知道这个对象是何方神圣只能等待程序执行的时候也就在是运行期才能知道那么我们就称之为 “动态” 获取对象比如通过类名 反射创建一个实例。而所谓 “动态代理” 就是指在运行期指定一个代理对象以接管的方式执行后续的任务。就是这么简单。
而 Feign 的 动态代理 是个偷天换日的过程。我们把目标服务看做一个新娘子当服务调用请求发出后一伙迎亲车队浩浩荡荡地出发去迎亲。这时候一伙打着 Feign 名号的抢亲小队出现了他们利用 “动态代理” 的方式截胡了迎亲车队自个儿当起了新郎官去接新娘。我们这就来看看这伙抢亲小队是怎么工作的。
抢亲小队 - 截胡方法调用
问截胡迎亲小队总共分几步总共分四步
我们来一起看下Feign 的源码。
·GetObject· 原配的迎亲小队出发了一路喊着 “接对象咯”getObject成功吸引到了抢亲小队的注意力。
这一步是 FeignClientFactoryBean 的 getObject 方法发起的为了获取一个可以发起远程调用的实体方法只是这时它还不知道getObject ()方法获取到的其实是一个代理对象。我们知道 Feign 实际上是调用了FeignClient 注解所修饰的接口FeignClientFactoryBean 封装了这个接口中所包含的配置信息比如Eureka服务名称服务调用的路径降级逻辑的处理类等等。
创建代理对象一伙抢亲小队听到风声立马开始着手准备埋伏。在上一步的getObject 方法的最后做好了埋伏开始了偷天换日的过程。
上一步中 getObject 最后一行经由 Targeter 类的转发抢亲小队登场了。下面就是创建代理对象的时候了Feign 的所有代理实例均通过ReflectiveFeign.newInstance 创建他的底层是采用 Builder 模式将FeignClient 接口的特征方法名参数等等一系列信息提取出来拼装成Java 反射机制中通用的 Method 类。偷天换日这一步是整个动态代理机制中的核心操作。在newInstance·的创建过程中Feign 通过实现 JDK 的 InvocationHandler接口所有动态代理方案几乎都和它有关联将自己的 Handler 和上一步组装的 Method 进行了关联这样一来所有对这个接口方法的调用都将被 Feign 自定义的InvocationHandler 给接管。这种动态代理的方式我们叫做 JDK 动态代理所有一切就绪就等截胡方法调用了
拦截请求这时迎亲车队经过了因为我们在前一步已经做了埋伏这个方法调用立马被我们自己人也就是上一步中自定义的InvocationHandler 截胡了。
SynchronousMethodHandler 这时接管了 invoke 方法。构造 Request 请 求装模作样当起了新郎官。在构造 Request 请求的同时还会涉及一系列的参数拼装和加密等步骤
发起调用最后一步借助 LoadBalancerFeignClient 发起了真正的HTTP 请求。从这个类的名字大家可以看到似乎和负载均衡有点关系没错这个就是 Feign 和 Ribbon组合而成的一个 Client 类它会利用 Ribbon 实现超时重试等操作。前面讲到过Feign 是武装到牙齿的组件每一步的背后都有非常复杂的处理流程。
Spring 的动态代理
Spring 的 AOP 有两种动态代理方式其中一种就是前面讲到的Feign采用的方式JDK 动态代理。在 Spring 中通过 JdkDynamicAopProxy 实现。它有两个特点
实现 InvocationHandler 接口接管invoke方法实现自己的业务逻辑所有调用都会被传递到 InvocationHandler 的 invoke 方法通过 Proxy.newProxyInstance 获取动态代理对象被代理的对象必须实现了某个接口不能代理无接口的类。Spring 还有一种动态代理的方式那就是 CGLIB它并不强制代理类实现某个接口。在实际使用中CGLIB 在代理对象的性能方面比 JDKDynamic 要快很多但是在创建代理对象上的时间花费也相当长。所以如果你的类并没有实现接口或者是单例模式的类不需要重复创建建议使用 CGLIB 的方式。 本文已收录至我的个人网站程序员波特主要记录Java相关技术系列教程共享电子书、Java学习路线、视频教程、简历模板和面试题等学习资源让想要学习的你不再迷茫。