热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【SpringCloud技术专题】「Feign」从源码层面让你认识Feign工作流程和运作机制

springcloud,技术,专题,feign,从,源,码,层面,让你,认识,feign,工作,

Feign工作流程源码解析

什么是feign:一款基于注解和动态代理的声明式restful http客户端。

原理

Feign发送请求实现原理

  • 微服务启动类上标记@EnableFeignClients注解,然后Feign接口上标记@FeignClient注解。@FeignClient注解有几个参数需要配置,这里不再赘述,都很简单。

  • Feign框架会扫描注解,然后通过Feign类来处理注解,并最终生成一个Feign对象。

解析@FeignClient注解,生成MethodHandler

具体的解析类是ParseHandlerByName。这个类是ReflectiveFeign的内部类。

// 解析注解元数据,使用Contract解析 List metadata = this.contract.parseAndValidateMetadata(key.type()); 

拿到注解元数据以后,循环处理注解元数据,创建每个方法对应的MethodHandler,这个MethodHandler最终会被代理对象调用。最终MethodHandler都会保存到下面这个集合中,然后返回。

Map result = new LinkedHashMap(); 
解析完成以后,调用ReflectiveFeign.newInstance()生成代理类。

MethodHandler是feign的一个接口,这个接口的invoke方法,是动态代理调用者InvocationHandler的invoke()方法最终调用的方法。

重新表述一遍:InvocationHandler的invoke()方法最终回调MethodHandler的invoke()来发送http请求。这就是Feign动态代理的具体实现。

ReflectiveFeign类的newInstance()方法的第57行:
// 创建动态代理调用者 InvocationHandler handler = this.factory.create(target, methodToHandler); // 反射生成feign接口代理 T proxy = Proxy.newProxyInstance(加载器, 接口数组, handler); 

InvocationHandler.invoke()的具体实现在FeignInvocationHandler.invoke(),FeignInvocationHandler也是ReflectiveFeign的一个内部类。里面有很多细节处理这里不再赘述,我们直接进入核心那一行代码,以免影响思路,我们是理Feign的实现原理的!不要在意这些细节!

// InvocationHandler的invoke()方法最终回调MethodHandler的invoke()来发送http请求

ReflectiveFeign类的invoke()方法,第323行,代码的后半段,如下:
(MethodHandler)this.dispatch.get(method). invoke(args); 
  • this.dispatch:这是一个map,就是保存所有的MethodHandler的集合。参考创建InvocationHandler的位置:ReflectiveFeign类的newInstance()方法的第57行。

  • this.dispatch.get(method):这里的method就是我们开发者写的feign接口中定义的方法的方法名!这段代码的意思就是从MethodHandler集合中拿到我们需要调用的那个方法。

  • this.dispatch.get(method). invoke(args):这里的invoke就是调用的MethodHandler.invoke()!动态代理回调代理类,就这样完成了,oh my god,多么伟大的创举!

MethodHandler.invoke()的具体实现:SynchronousMethodHandler.invoke()

到了这里,就是发送请求的逻辑了。发送请求前,首先要创建请求模板,然后调用请求拦截器RequestInterceptor进行请求处理。

// 创建RequestTemplate RequestTemplate template = this.buildTemlpateFromArgs.create(argv); // 创建feign重试器,进行失败重试 Retryer retryer = this.retryer.clone(); while(true){     try{         // 发送请求         return this.executeAndDecode(template);     } catch(RetryableException var5) {         // 失败重试,最多重试5次         retryer.continueOrPropagate();     } } 
RequestTemplate处理

RequestTemplate模板需要经过一系列拦截器的处理,主要有以下拦截器:

  • BasicAuthRequestInterceptor:授权拦截器,主要是设置请求头的Authorization信息,这里是base64转码后的用户名和密码。

  • FeignAcceptGzipEncodingInterceptor:编码类型拦截器,主要是设置请求头的Accept-Encoding信息,默认值{gzip, deflate}。

  • FeignContextGzipEncodingInterceptor:压缩格式拦截器,该拦截器会判断请求头中Context-Length属性的值,是否大于请求内容的最大长度,如果超过最大长度2048,则设置请求头的Context-Encoding信息,默认值{gzip, deflate}。注意,这里的2048是可以设置的,可以在配置文件中进行配置:

feign.compression.request.enabled=true feign.compression.request.min-request-size=2048 

min-request-size是通过FeignClientEncodingProperties来解析的,默认值是2048。

我们还可以自定义请求拦截器,我们自定义的拦截器,也会在此时进行调用,所有实现了RequestTemplate接口的类,都会在这里被调用。比如我们可以自定义拦截器把全局事务id放在请求头里。

使用feign.Request把RequestTemplate包装成feign.Request

feign.Request由5部分组成:

  • method

  • url

  • headers

  • body

  • charset

http请求客户端

Feign发送http请求支持下面几种http客户端:

  • JDK自带的HttpUrlConnection

  • Apache HttpClient

  • OkHttpClient

// 具体实现有2个类Client.Default 和LoadBalancerFeignClient respOnse= this.client.execute(request, this.options); Client接口定义了execute()的接口,并且通过接口内部类实现了Client.execute()。 HttpURLConnection cOnnection= this.convertAndSend(request, options); return this.convertResponse(connection).toBuilder(). request(request).build(); 
  • 这里的Options定义了2个参数:

    • connectTimeoutMillis:连接超时时间,默认10秒。

    • readTimeoutMillis:读取数据超时时间,默认60秒。

这种方式是最简单的实现,但是不支持负载均衡,Spring Cloud整合了Feign和Ribbon,所以自然会把Feign和Ribbon结合起来使用。也就是说,Feign发送请求前,会先把请求再经过一层包装,包装成RibbonRequest。

也就是发送请求的另一种实现LoadBalancerFeignClient。

// 把Request包装成RibbonRequest RibbonRequest ribbOnRequest= new (this.delegate, request, uriWithoutHost); // 配置超时时间 IClientConfig requestCOnfig= this.getClientConfig(options, clientName); // 以负载均衡的方式发送请求 return ((RibbonResponse)this.IbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse(); 
以负载均衡的方式发送请求
  • this.IbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig))的具体实现在AbstractLoadBalancerAwareClient类中。

  • executeWithLoaderBalancer()方法的实现也参考了响应式编程,通过LoadBalancerCommand提交请求,然后使用Observable接收响应信息。

AbstractLoadBalancerAwareClient类的executeWithLoadBalancer()方法的第54行:

Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));

AbstractLoadBalancerAwareClient实现了IClient接口,该接口定义了execute()方法,

  • AbstractLoadBalancerAwareClient.this.execute()的具体实现有很多种:

    • OkHttpLoadBalancingClient

    • RetryableOkHttpLoadBalancingClient

    • RibbonLoadBalancingHttpClient

    • RetryableRibbonLoadBalancingHttpClient

我们以RibbonLoadBalancingHttpClient为例来说明,RibbonLoadBalancingHttpClient.execute()

第62行代码:

// 组装HttpUriRequest HttpUriRequest httpUriRequest = request.toRequest(requestConfig); // 发送http请求 HttpResponse httpRespOnse= ((HttpClient)this.delegate).execute(httpUriRequest); // 使用RibbonApacheHttpResponse包装http响应信息 return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI()); RibbonApacheHttpResponse由2部分组成: httpResponse uri 

处理http相应

http请求经过上面一系列的转发以后,最终还会回到SynchronousMethodHandler,然后SynchronousMethodHandler会进行一系列的处理,然后响应到浏览器。

  • 注册Feign客户端bean到IOC容器

  • 查看Feign框架源代码,我们可以发现,FeignClientsRegistar的registerFeignClients()方法完成了feign相关bean的注册。

Feign架构图

  • 第一步:基于JDK动态代理生成代理类。

  • 第二步:根据接口类的注解声明规则,解析出底层MethodHandler

  • 第三步:基于RequestBean动态生成request。

  • 第四步:Encoder将bean包装成请求。

  • 第五步:拦截器负责对请求和返回进行装饰处理。

  • 第六步:日志记录。

  • 第七步:基于重试器发送http请求,支持不同的http框架,默认使用的是HttpUrlConnection。


推荐阅读
  • 如何使用 `org.eclipse.rdf4j.query.impl.MapBindingSet.getValue()` 方法及其代码示例详解 ... [详细]
  • Spring框架中枚举参数的正确使用方法与技巧
    本文详细阐述了在Spring Boot框架中正确使用枚举参数的方法与技巧,旨在帮助开发者更高效地掌握和应用枚举类型的数据传递,适合对Spring Boot感兴趣的读者深入学习。 ... [详细]
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • 在Java Web服务开发中,Apache CXF 和 Axis2 是两个广泛使用的框架。CXF 由于其与 Spring 框架的无缝集成能力,以及更简便的部署方式,成为了许多开发者的首选。本文将详细介绍如何使用 CXF 框架进行 Web 服务的开发,包括环境搭建、服务发布和客户端调用等关键步骤,为开发者提供一个全面的实践指南。 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 原文网址:https:www.cnblogs.comysoceanp7476379.html目录1、AOP什么?2、需求3、解决办法1:使用静态代理4 ... [详细]
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
  • Java Socket 关键参数详解与优化建议
    Java Socket 的 API 虽然被广泛使用,但其关键参数的用途却鲜为人知。本文详细解析了 Java Socket 中的重要参数,如 backlog 参数,它用于控制服务器等待连接请求的队列长度。此外,还探讨了其他参数如 SO_TIMEOUT、SO_REUSEADDR 等的配置方法及其对性能的影响,并提供了优化建议,帮助开发者提升网络通信的稳定性和效率。 ... [详细]
  • 利用 Python Socket 实现 ICMP 协议下的网络通信
    在计算机网络课程的2.1实验中,学生需要通过Python Socket编程实现一种基于ICMP协议的网络通信功能。与操作系统自带的Ping命令类似,该实验要求学生开发一个简化的、非标准的ICMP通信程序,以加深对ICMP协议及其在网络通信中的应用的理解。通过这一实验,学生将掌握如何使用Python Socket库来构建和解析ICMP数据包,并实现基本的网络探测功能。 ... [详细]
  • 本文详细介绍了一种利用 ESP8266 01S 模块构建 Web 服务器的成功实践方案。通过具体的代码示例和详细的步骤说明,帮助读者快速掌握该模块的使用方法。在疫情期间,作者重新审视并研究了这一未被充分利用的模块,最终成功实现了 Web 服务器的功能。本文不仅提供了完整的代码实现,还涵盖了调试过程中遇到的常见问题及其解决方法,为初学者提供了宝贵的参考。 ... [详细]
  • 本文介绍了如何利用 Delphi 中的 IdTCPServer 和 IdTCPClient 控件实现高效的文件传输。这些控件在默认情况下采用阻塞模式,并且服务器端已经集成了多线程处理,能够支持任意大小的文件传输,无需担心数据包大小的限制。与传统的 ClientSocket 相比,Indy 控件提供了更为简洁和可靠的解决方案,特别适用于开发高性能的网络文件传输应用程序。 ... [详细]
  • SQL 查询实体优化与实战技巧分享 ... [详细]
  • 本文介绍了如何利用ObjectMapper实现JSON与JavaBean之间的高效转换。ObjectMapper是Jackson库的核心组件,能够便捷地将Java对象序列化为JSON格式,并支持从JSON、XML以及文件等多种数据源反序列化为Java对象。此外,还探讨了在实际应用中如何优化转换性能,以提升系统整体效率。 ... [详细]
  • Java中不同类型的常量池(字符串常量池、Class常量池和运行时常量池)的对比与关联分析
    在研究Java虚拟机的过程中,笔者发现存在多种类型的常量池,包括字符串常量池、Class常量池和运行时常量池。通过查阅CSDN、博客园等相关资料,对这些常量池的特性、用途及其相互关系进行了详细探讨。本文将深入分析这三种常量池的差异与联系,帮助读者更好地理解Java虚拟机的内部机制。 ... [详细]
  • 深入解析C#中app.config文件的配置与修改方法
    在C#开发过程中,经常需要对系统的配置文件进行读写操作,如系统初始化参数的修改或运行时参数的更新。本文将详细介绍如何在C#中正确配置和修改app.config文件,包括其结构、常见用法以及最佳实践。此外,还将探讨exe.config文件的生成机制及其在不同环境下的应用,帮助开发者更好地管理和维护应用程序的配置信息。 ... [详细]
author-avatar
谁的围脖搞笑排行榜
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有