Feign 进行rpc 调用时使用ribbon负载均衡源码解析

转载请注明出处:

Feign客户端接口的动态代理生成是基于JDK的动态代理来实现的,那么在所有的方法调用的时候最终都会走InvocationHandler接口的实现,默认就是ReflectiveFeign.FeignInvocationHandler,那我们接下来就来看看,FeignInvocationHandler是如何实现rpc调用的。

FeignInvocationHandler对于invoke方法的实现。

实现rpc 调用的方法是 this.dispatch.get(method)).invoke(args)

从dispatch获取要调用的方法对应的MethodHandler,然后调用MethodHandler的invoke方法。那MethodHandler是什么时候生成的呢?MethodHandler是在构建动态代理的时候生成的, 那MethodHandler作用是什么呢?你可以理解为最终rpc的调用都是基于这个MethodHandler来实现的,每个方法都有对应MethodHandler来实现rpc调用,接下来我们就来看一下MethodHandler的invoke方法的实现。

MethodHandler是个接口,有两个实现类,一个是DefaultMethodHandler,这个是处理接口中的默认方法的,另一个是SynchronousMethodHandler,这个是实现rpc调用的方法。接下来我们就看看SynchronousMethodHandler关于invoke方法的实现。

查看 executeAndDecode 方法实现

Client是发送http请求的关键类 ; 当Feign客户端在构建动态代理的时候,填充很多组件到Feign.Builder中,其中有个组件就是Client的实现 ;这个组件的实现是要依赖负载均衡的,也就是这个组件是Feign用来整合Ribbon的入口。

Feign是如何通过ribbon实现负载均衡的

查看 FeignRibbonClientAutoConfiguration 配置类:

@Impot注解导入了三个配置类。

  • HttpClientFeignLoadBalancedConfiguration:基于HttpClient实现http调用的。
  • OkHttpFeignLoadBalancedConfiguration:基于OkHttp实现http调用的。
  • DefaultFeignLoadBalancedConfiguration:默认的,也就是Feign原生的发送http的实现。

看一下DefaultFeignLoadBalancedConfiguration配置类,因为默认就是这,HttpClientFeignLoadBalancedConfiguration和OkHttpFeignLoadBalancedConfiguration都需要有引入HttpClient和OkHttp依赖才会有用

这个配置类很简单,声明了LoadBalancerFeignClient到spring容器,传入了三个参数,一个Client的实现,一个CachingSpringLoadBalancerFactory和一个SpringClientFactory。LoadBalancerFeignClient这个类实现了Client接口,也就数说我们在构建Feign.Builder填充的就是这个对象,也就是上面说feign的执行流程最后用来执行请求的Client的实现。

Client.Default:就是Feign自己实现的Client,里面封装了真正发送http发送请求的功能,LoadBalancerFeignClient虽然也实现了Client接口,但是这个实现其实是为了整合Ribbon用的,并没有发送http的功能,所以需要有个可以发送http功能的实现。

这里就说完了Feign整合ribbon的配置类FeignRibbonClientAutoConfiguration,我们也找到了构造Feign.Builder的实现LoadBalancerFeignClient,接下来就来剖析LoadBalancerFeignClient的实现。

在动态代理调用的那里我们得出一个结论,那就是最后会调用Client接口的execute方法的实现,所以我们就看一下execute方法的实现,这里就是一堆操作,从请求的URL中拿到了clientName,也就是服务名。

为什么可以拿到服务名?

其实很简单,OpenFeign构建动态代理的时候,传入了一个HardCodedTarget,当时说在构建HardCodedTarget的时候传入了一个url,那个url当时说了其实就是http://服务名,所以到这里,虽然有具体的请求接口的路径,但是还是类似 http://服务名/api/sayHello这种,所以可以通过路径拿到你锁请求的服务名。

拿到服务名之后,再拿到了一个配置类IClientConfig,最后调用lbClient,我们看一下lbClient的方法实现。

3.查看 FeignLoadBalancer 源码

核心源码:

FeignLoadBalancer继承自AbstractLoadBalancerAwareClient,AbstractLoadBalancerAwareClient类主要作用是通过ILoadBalancer组件获取一个Server,然后基于这个Server重构了URI,也就是将你的请求路径http://服务名/api/sayHello转换成类似http://192.168.1.101:8088/api/sayHello这种路径,也就是将原服务名替换成服务所在的某一台机器ip和端口,替换之后就交由子类实现的exceut方法来发送http请求。

所以我们知道调用executeWithLoadBalancer之后,就会重构请求路径,将服务名替换成某个具体的服务器所在的ip和端口,之后交给子类execute来处理,对于这里来说,也就是FeignLoadBalancer的execute方法,因为FeignLoadBalancer继承AbstractLoadBalancerAwareClient。

直接定位到execute方法最核心的一行代码

request.client()就会拿到构建LoadBalancerFeignClient传入的那个Client的实现,这个Client的实现是具体发送请求的实现,默认的就是Client.Default类(不是默认就有可能是基于HttpClient或者是OkHttp的实现)。所以这行代码就是基于这个Client就成功的发送了Http请求,拿到响应,然后将这个Response 封装成一个RibbonResponse返回,最后就返回给MethodHandler,然后解析响应,封装成方法的返回值返回给调用者。

其实到这里就完全知道Feign是如何整合Ribbon的,LoadBalancerFeignClient其实是OpenFeign适配Ribbon的入口,FeignLoadBalancer才是真正实现选择负载均衡,发送http请求的组件,因为他继承了AbstractLoadBalancerAwareClient。

参考文章:

https://blog.csdn.net/u010342147/article/details/123669493

https://blog.csdn.net/weixin_45630885/article/details/124391934

https://blog.csdn.net/weixin_45630885/article/details/124533413

Original: https://www.cnblogs.com/zjdxr-up/p/16357764.html
Author: 香吧香
Title: Feign 进行rpc 调用时使用ribbon负载均衡源码解析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/589723/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Elasticsearch—DSL搜索实践

    POST http://192.168.2.223:9200/shop/_mapping { "properties": { "id": {…

    Java 2023年6月13日
    086
  • Fiddler如何设置接口并发

    Fiddler — 并发 可以对接口测试是否做了限制,比如我进行抽奖,我将 抽奖的接口进行并发,如果开发没有做限制同一秒内我进入10个请求都被响 应,那恭喜了你有了是个奖品 1….

    Java 2023年6月6日
    078
  • (转)SpringBoot实现MultipartFile文件上传

    转:SpringBoot实现MultipartFile文件上传 – 云+社区 – 腾讯云 (tencent.com) 1、SpringBoot采用FileU…

    Java 2023年5月29日
    079
  • 前端(DOM、BOM和事件) 4

    js包含ECMAScript、DOM、BOM。BOM(browser object model)浏览器对象模型,使js获得了操纵浏览器的能力。DOM(document object…

    Java 2023年6月5日
    096
  • rocketMQ 双机房部署

    posted on2020-05-08 21:43 偶尔发呆 阅读(707 ) 评论() 编辑 Original: https://www.cnblogs.com/allenwas…

    Java 2023年5月30日
    088
  • 如何搭建一个WEB服务器项目(三)—— 实现安卓端联网登录

    安卓端调用服务器登录函数进行验证登录 观前提示:本系列文章有关服务器以及后端程序这些概念,我写的全是自己的理解,并不一定正确,希望不要误人子弟。欢迎各位大佬来评论区提出问题或者是指…

    Java 2023年6月8日
    0101
  • 在Spring中测试CGLIB动态代理遇到ClassNotFoundException: org.objectweb.asm.Type问题

    在Spring项目中进行简单的cglib动态代理的学习测试,主要代码如下: 一个目标类HaveDinnerService public class HaveDinnerServic…

    Java 2023年6月9日
    086
  • Redis持久化

    Redis持久化都有哪些类型? Redis持久化分为两种: RDB持久化与AOF持久化 两种持久化的异同? (1)RDB持久化:RDB持久化是将我们运行过程中Redis数据库中的对…

    Java 2023年6月6日
    092
  • 2、spring+mybatis注解(无mapper实现类)+idea+maven

    1、在idea中配置database 连接数据库,用来在idea中编写sql脚本操作数据库 2、sql详细脚本如下: 1 –1.创建部门表 2 create table dept…

    Java 2023年6月13日
    068
  • axios的封装

    Vue中axios的封装 request.js请求封装、 import axios from ‘axios’ import { getToken } from ‘@/utils’ …

    Java 2023年6月5日
    092
  • JAVA 读取文件(收集)

    1、按字节读取文件内容 2、按字符读取文件内容 3、按行读取文件内容 4、随机读取文件内容 5、将内容追加到文件尾部 Original: https://www.cnblogs.c…

    Java 2023年5月29日
    084
  • Okhttp3 网络请求框架与 Gson

    <span class="hljs-params"><dependency> <span class="hljs-par…

    Java 2023年6月9日
    079
  • 多线程编程(2) – 一切从 CreateThread 开始

    产生一个线程 产生一个线程,是以 CreateThread() 作为一切行动的开始。此函数的原型如下: function CreateThread( lpThreadAttribu…

    Java 2023年5月30日
    066
  • 《回炉重造》——集合(容器)

    整体框架 绿色代表接口/抽象类;蓝色代表类。 主要由两大接口组成,一个是「Collection」接口,另一个是「Map」接口。 前言 以前刚开始学习「集合」的时候,由于没有好好预习…

    Java 2023年6月10日
    074
  • 逻辑存储单元

    dbspace dbspace&#x662F;&#x4E00;&#x4E2A;&#x6216;&#x8005;&#x591A;&am…

    Java 2023年6月9日
    099
  • 到底什么是.NET?

    ​.NET 概念比较庞大,本文只讨论基础知识,只用简单语言描述。 我们是NET程序员, 但是我们有没有思考过到底什么是.NET ? 官方定义 .NET是微软推出来的一个致力于敏捷开…

    Java 2023年6月9日
    082
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球