当前位置: 首页 > news >正文

OpenFeign原理整理【Java面试】

OpenFeign

核心流程

1.在 Spring 项目启动阶段,服务 A 的OpenFeign 框架会发起一个主动的扫包流程。
2.从指定的目录下扫描并加载所有被 @FeignClient 注解修饰的接口,然后将这些接口转换成 Bean,统一交给 Spring 来管理。
3.根据这些接口会经过 MVC Contract 协议解析,将方法上的注解都解析出来,放到 MethodMetadata 元数据中。
4.基于上面加载的每一个 FeignClient 接口,会生成一个动态代理对象,指向了一个包含对应方法的 MethodHandler 的 HashMap。MethodHandler 对元数据有引用关系。生成的动态代理对象会被添加到 Spring 容器中,并注入到对应的服务里。
5.服务 A 调用接口,准备发起远程调用。
6.从动态代理对象 Proxy 中找到一个 MethodHandler 实例,生成 Request,包含有服务的请求 URL(不包含服务的 IP)。
7.经过负载均衡算法找到一个服务的 IP 地址,拼接出请求的 URL。
8.服务 B 处理服务 A 发起的远程调用请求,执行业务逻辑后,返回响应给服务 A。

OpenFeign 包扫描原理

@EnableFeignClients 这个注解使用 Spring 框架的 Import 注解导入了 FeignClientsRegistrar 类

// 启动类加上这个注解
@EnableFeignClients(basePackages = "com.test.feign")// EnableFeignClients 类还引入了 FeignClientsRegistrar 类
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {...
}

FeignClientsRegistrar 负责 Feign 接口的加载。

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 注册配置registerDefaultConfiguration(metadata, registry);// 注册 FeignClientregisterFeignClients(metadata, registry);
}

registerFeignClients 会扫描指定包。

Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);

只保留带有 @FeignClient 的接口。

// 判断是否是带有注解的 Bean。
if (candidateComponent instanceof AnnotatedBeanDefinition) {// 判断是否是接口AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();// @FeignClient 只能指定在接口上。Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");

注册 FeignClient 到 Spring 的原理

  • 解析 @FeignClient 定义的属性。
  • 将注解@FeignClient 的属性 + 接口 StudyTimeFeignService的信息构造成一个 StudyTimeFeignService 的 beanDefinition。
  • 然后将 beanDefinition 转换成一个 holder,这个 holder 就是包含了 beanDefinition, alias, beanName 信息。
  • 最后将这个 holder 注册到 Spring 容器中。

OpenFeign 动态代理原理

// 省略部分代码
public class ReflectiveFeign extends Feign {// 为 feign client 接口中的每个接口方法创建一个 methodHandlerpublic <T> T newInstance(Target<T> target) {for(...) {methodToHandler.put(method, handler);}// 基于 JDK 动态代理的机制,创建了一个 passjava-study 接口的动态代理,所有对接口的调用都会被拦截,然后转交给 handler 的方法。InvocationHandler handler = factory.create(target, methodToHandler);T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);
}

ReflectiveFeign 做的工作就是为带有 @FeignClient 注解的接口,创建出接口方法的动态代理对象。

  • 解析 FeignClient 接口上各个方法级别的注解,比如远程接口的 URL、接口类型(Get、Post 等)、各个请求参数等。这里用到了 MVC Contract 协议解析,后面会讲到。
  • 然后将解析到的数据封装成元数据,并为每一个方法生成一个对应的 MethodHandler 类作为方法级别的代理。相当于把服务的请求地址、接口类型等都帮我们封装好了。这些 MethodHandler 方法会放到一个 HashMap 中。
  • 然后会生成一个 InvocationHandler 用来管理这个 hashMap,其中 Dispatch 指向这个 HashMap。
  • 然后使用 Java 的 JDK 原生的动态代理,实现了 FeignClient 接口的动态代理 Proxy 对象。这个 Proxy 会添加到 Spring 容器中。
  • 当要调用接口方法时,其实会调用动态代理 Proxy 对象的 methodHandler 来发送请求。

OpenFeign 如何与 Ribbon 整合的原理

  • 根据服务名从缓存中找 FeignLoadBalancer,如果缓存中没有,则创建一个 FeignLoadBalancer。
  • FeignLoadBalancer 会创建出一个 command,这个 command 会执行一个 sumbit 方法。
  • submit 方法里面就会用 Ribbon 的负载均衡算法选择一个 server。
  • 然后将 IP 地址和之前剔除掉服务名称的 URL 进行拼接,生成最后的服务地址。
  • 最后 FeignLoadBalancer 执行 execute 方法发送请求。

Ribbon 的核心组件 ServerListUpdater,用来同步注册表的,它有一个实现类 PollingServerListUpdater ,专门用来做定时同步的。默认1s 后执行一个 Runnable 线程,后面就是每隔 30s 执行 Runnable 线程。这个 Runnable 线程就是去获取注册中心的注册表的。

OpenFeign 处理响应的原理

这个里面做的事情就是调用 ResponseEntityDecoder 的 decode 方法,将 Json 字符串转化为 Bean 对象。

说明

参考链接

相关文章:

  • Gitlab CI---could not read username for xxx: no such device or address
  • flutter 打包成web应用后怎么通过url跳转页面
  • Chrome 插件 tabs API 解析
  • uniApp使用XR-Frame创建3D场景(8)粒子系统
  • 目标检测+车道线识别+追踪
  • pulsar: kafka on pulsar之把pulsar当kafka用
  • 【直播课】2024年PostgreSQL CM认证实战培训课程于4月27日开课!
  • 持续集成流水线介绍(CI)
  • 大语言模型中的强化学习与迁移学习技术
  • helm 部署 Kube-Prometheus + Grafana + 钉钉告警部署 Kube-Prometheus
  • Unity照片墙简易圆形交互效果总结
  • 免费软件“蓝莓投屏”:支持多个Airplay同时镜像的投屏软件。
  • Tomcat 启动闪退问题解决方法
  • 考研复试细胞生物学3.细胞骨架(交通网络)
  • Mybatis的动态SQL~
  • Git的一些常用操作
  • JavaScript设计模式之工厂模式
  • node学习系列之简单文件上传
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • vagrant 添加本地 box 安装 laravel homestead
  • 翻译:Hystrix - How To Use
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 免费小说阅读小程序
  • 物联网链路协议
  • 正则学习笔记
  • 1.Ext JS 建立web开发工程
  • ​ssh免密码登录设置及问题总结
  • #1014 : Trie树
  • #DBA杂记1
  • #define 用法
  • #pragma pack(1)
  • #WEB前端(HTML属性)
  • #Z2294. 打印树的直径
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (阿里云万网)-域名注册购买实名流程
  • (二)springcloud实战之config配置中心
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • (转载)Linux网络编程入门
  • *p++,*(p++),*++p,(*p)++区别?
  • .NET delegate 委托 、 Event 事件
  • .net 微服务 服务保护 自动重试 Polly
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装
  • .考试倒计时43天!来提分啦!
  • /dev下添加设备节点的方法步骤(通过device_create)
  • [Android] Upload package to device fails #2720
  • [AUTOSAR][诊断管理][ECU][$37] 请求退出传输。终止数据传输的(上传/下载)
  • [BUG] Authentication Error
  • [C# 开发技巧]实现属于自己的截图工具
  • [C#]C# winform部署yolov8目标检测的openvino模型
  • [C++][基础]1_变量、常量和基本类型
  • [CC2642R1][VSCODE+Embedded IDE+IAR Build+Cortex-Debug] TI CC2642R1基于VsCode的开发环境
  • [CSS]盒子模型