OpenFeign 整合 Nacos负载均衡原理

article/2025/10/10 11:42:02

一.OpenFeign介绍

        OpenFeign是实现微服务间调用的工具,功能包括编解码、构造http请求等。同时OpenFeign又集成了ribbon功能实现客户端负载均衡能力,Bibbon默认的客户端负载均衡能力是与Eurake集成,Nacos通过重写ribbon的ServerList功能实现ribbon与Nacos集成。

二.OpenFeign初始化原理

OpenFeign入口在@EnableFeignClients注解,同时它又导入了FeignClientsRegistrar类

 

 FeignClientsRegistrar类实现了ImportBeanDefinitionRegistrar类,这个类是Spring的一个扩展点,可以注册BeanDefinition,对外暴露了一个接口

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {registerDefaultConfiguration(metadata, registry);registerFeignClients(metadata, registry);
}

再进入registerFeignClients(metadata, registry)方法

public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);Set<String> basePackages;Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);final Class<?>[] clients = attrs == null ? null: (Class<?>[]) attrs.get("clients");if (clients == null || clients.length == 0) {scanner.addIncludeFilter(annotationTypeFilter);basePackages = getBasePackages(metadata);}else {final Set<String> clientClasses = new HashSet<>();basePackages = new HashSet<>();for (Class<?> clazz : clients) {basePackages.add(ClassUtils.getPackageName(clazz));clientClasses.add(clazz.getCanonicalName());}AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {@Overrideprotected boolean match(ClassMetadata metadata) {String cleaned = metadata.getClassName().replaceAll("\\$", ".");return clientClasses.contains(cleaned);}};scanner.addIncludeFilter(new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));}for (String basePackage : basePackages) {Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());String name = getClientName(attributes);registerClientConfiguration(registry, name,attributes.get("configuration"));registerFeignClient(registry, annotationMetadata, attributes);}}}
}

又进入registerFeignClient(registry, annotationMetadata, attributes)方法

private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {String className = annotationMetadata.getClassName();BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);validate(attributes);definition.addPropertyValue("url", getUrl(attributes));definition.addPropertyValue("path", getPath(attributes));String name = getName(attributes);definition.addPropertyValue("name", name);definition.addPropertyValue("type", className);definition.addPropertyValue("decode404", attributes.get("decode404"));definition.addPropertyValue("fallback", attributes.get("fallback"));definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);String alias = name + "FeignClient";AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be nullbeanDefinition.setPrimary(primary);String qualifier = getQualifier(attributes);if (StringUtils.hasText(qualifier)) {alias = qualifier;}BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,new String[] { alias });BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

这个方法是整个OpenFeign初始化客户端的核心,它注册了一个FeignClientFactoryBean类,这个类实现了FactoryBean接口,它是Spring中一个特殊的类,它可以返回“一类”对象,主要方法是getObject方法

@Override
public Object getObject() throws Exception {FeignContext context = applicationContext.getBean(FeignContext.class);Feign.Builder builder = feign(context);if (!StringUtils.hasText(this.url)) {String url;if (!this.name.startsWith("http")) {url = "http://" + this.name;}else {url = this.name;}url += cleanPath();return loadBalance(builder, context, new HardCodedTarget<>(this.type,this.name, url));}if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {this.url = "http://" + this.url;}String url = this.url + cleanPath();Client client = getOptional(context, Client.class);if (client != null) {if (client instanceof LoadBalancerFeignClient) {// not load balancing because we have a url,// but ribbon is on the classpath, so unwrapclient = ((LoadBalancerFeignClient)client).getDelegate();}builder.client(client);}Targeter targeter = get(context, Targeter.class);return targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url));
}

三.OpenFeign调用原理

        每次当我们注入Feign的接口时,Spring都会为我们创建一个接口的代理,而这个代理的生成都会走getObject方法。当我们没有为接口配置具体url时,使用服务名查找,而服务名查找就会走负载均衡方法,也就是loadBalance方法

这个方法中的client实现为LoadBalanceFeignClient类

 进入target方法

 最终生成代理

 

 最终执行invoke方法

 

 

 使用LoadBalancerFeignClient的实现

 最终调用AbstractLoadBalancerAwareClient中的executeWithLoadBalancer方法,此时已经ribbon的方法了。

进入executeWithLoadBalancer方法

 进入submit方法,注意看selectServer()方法

private Observable<Server> selectServer() {return Observable.create(new OnSubscribe<Server>() {@Overridepublic void call(Subscriber<? super Server> next) {try {Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   next.onNext(server);next.onCompleted();} catch (Exception e) {next.onError(e);}}});
}

 这里就到了客户端负载均衡选择服务的地方了这里面有个细节是代码无法跟到的,在选择服务器,Nacos重写了ribbon的ServerList,这就是为什么ribbon可以读到Nacos注册中心的服务。我们看下Nacos自动装配文件

 再看下ribbon原始的自动装配文件。

 除了ServerList其他类几乎没动,这就完成了ribbon和Nacos的集成,整个调用过程还有很多细节,并没有跟进,后续有机会继续跟进

源码中的一些具体实现类:

Feign.Builder builder = SentinelFeign$Builder

Client client = LoadBalancerFeignClient

Targeter targeter = HystrixTargeter

DynamicServerListLoadBalancer

ZoneAwareLoadBalancer


http://chatgpt.dhexx.cn/article/n0hbVmyf.shtml

相关文章

负载均衡技术原理

参看文章&#xff1a; 快速理解高性能HTTP服务端的负载均衡技术原理 简介几种负载均衡原理 浅谈几种常用负载均衡架构 一篇读懂分布式架构下的负载均衡技术&#xff1a;分类、原理、算法、常见方案等 一、 引言 负载均衡&#xff08;Load Balance&#xff09;是指将负载(工作任…

Docker Swarm 内部服务发现和负载均衡原理

1. 集群环境准备 搭建三台服务器&#xff0c;并安装docker环境&#xff0c;并保证能正常连接互联网&#xff0c;后面会使用其他镜像做负载均衡测试。 192.168.104.79192.168.104.80192.168.104.81 首先修改hostname&#xff0c;便于后面区分当前操作所在机器&#xff1a;使用…

Spring Cloud - Ribbon 负载均衡原理、负载策略、懒加载

目录 ​编辑 一、Ribbon 负载均衡原理 1.1、前言 1.2、负载均衡的工作流程 二、负载均衡策略 2.1、策略原理 2.2、负载均衡自定义方式 三、Ribbon 加载方式 一、Ribbon 负载均衡原理 1.1、前言 ps&#xff1a;案例是上一章所讲的 “根据订单id查询订单的同时&#xff0…

Ribbon-负载均衡原理

负载均衡原理 SpringCloud底层其实是利用了一个名为Ribbon的组件&#xff0c;来实现负载均衡功能的。 那么我们发出的请求明明是http://userservice/user/1&#xff0c;怎么变成了http://localhost:8081的呢&#xff1f; 源码跟踪 为什么我们只输入了service名称就可以访问了…

Nginx负载均衡原理与实战

Nginx 负载均衡原理与实践 本篇摘自《亿级流量网站架构核心技术》第二章 Nginx 负载均衡与反向代理 部分内容。 当我们的应用单实例不能支撑用户请求时&#xff0c;此时就需要扩容&#xff0c;从一台服务器扩容到两台、几十台、几百台。然而&#xff0c;用户访问时是通过如的…

Nginx负载均衡原理

Nginx简介 Nginx是通过反向代理实现的负载均衡。 什么是正向代理与反向代理&#xff1f; 正向代理就是&#xff0c;客户端通过一台代理服务器访问服务端。 反向代理就是&#xff0c;服务端通过代理服务器为客户端提供服务。 看起来似乎没有什么区别&#xff0c;举个例子。 …

负载均衡原理及算法

目录 背景概述原理分类按照软硬件分类硬件负载均衡软件负载均衡 按照地理结构分类本地负载均衡全局负载均衡 按照实现技术DNS负载均衡IP负载均衡链路层负载均衡混合型负载均衡 按照OSI层次二层负载均衡&#xff08;数据链路层&#xff09;三层负载均衡&#xff08;网络层&#…

Ribbon负载均衡原理

Ribbon restTemplate相结合实现负载均衡&#xff0c;具体原理图详见以下截图&#xff1a; LoadBalancerClient 类执行具体的负载均衡&#xff0c;其继承于 LoadBalancerBase。LoadBalancerInterceptor 中注入了 LoadBalancerClient 对象&#xff0c;LoadBalancerClient执行具…

什么是负载均衡,负载均衡的原理解析是怎么样的

负载均衡对于很多大型企业来说&#xff0c;不管网游&#xff0c;商城&#xff0c;金融等业务&#xff0c;他的重要性无需多说&#xff0c;今天带来负载均衡的原理解析。 开头先理解一下所谓的“均衡”。 不能狭义地理解为分配给所有实际服务器一样多的工作量&#xff0c;因为…

负载均衡工作原理详解

负载均衡是由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助。通过某种负载分担技术,将外部发送来的请求均匀分配到对称结构中的某一台服务器上,而接收到请求的服务器独立地回应客户的请求。 均衡负…

负载均衡原理及实现

什么是负载均衡&#xff1f; 负载均衡( LoadBalance )&#xff0c;顾名思义就是把任务压力进行平衡的分摊到集群中各个操作单元(或机器)上&#xff0c;使得避免集群中部分机器压力过大而部分机器过于空闲。经过负载均衡&#xff0c;使得每个机器获取适合自己的处理能力负载。 …

Android 获取手机已安装的应用列表(适配)

权限 <uses-permissionandroid:name"android.permission.QUERY_ALL_PACKAGES"tools:ignore"QueryAllPackagesPermission" /> 加上权限才能获取到所有的应用 获取代码 public static boolean hasApplication(Context context, String packageName) …

如何在没有微软商店的情况下在Windows 10上安装应用程序

如何在没有微软商店的情况下在Windows 10上安装应用程序 通过微软商店&#xff0c;你可以轻松地在Windows 10设备上安装应用程序&#xff0c;就像使用Google Play或AppleStore一样。IT部门经常从设备上删除微软商店&#xff0c;或设置组策略禁用Windows更新&#xff0c;以防止…

“打开文件所在位置“失败,提示“该文件没有与之关联的应用来执行操作。请安装应用,若已经安装应用...““

问题点:装了某个软件,卸载后出现"打开文件所在位置"失败,出现以下提示: 该文件没有与之关联的应用来执行操作。请安装应用&#xff0c;若已经安装应用&#xff0c;请在“默认应用设置"页面中创建关联 解决办法: 打开注册表,找到HKEY_CLASSES_ROOT\Folder\shell,…

Win 11 打开未知文件/打开方式 该文件没有与之关联的应用来执行该操作。请安装应用,若已经安装应用,请在“默认应用设置”页面中创建关联。

问题 鼠标右键选中文件 - 打开方式&#xff0c;或者选择其他应用 弹窗提示&#xff1a; 该文件没有与之关联的应用来执行该操作。请安装应用&#xff0c;若已经安装应用&#xff0c;请在“默认应用设置”页面中创建关联。 打开注册表&#xff0c;查看是否存在以下路径 HKEY_C…

该文件没有与之关联的应用来执行该操作。请安装应用,若已经安装应用,请在“默认应用设置“页面中创建关联

在任意文件上鼠标右键&#xff0c;选择打开方式或者选择其他应用时报错“该文件没有与之关联的应用来执行该操作。请安装应用&#xff0c;若已经安装应用&#xff0c;请在"默认应用设置"页面中创建关联”&#xff0c;错误现象如下&#xff1a; 打开注册表&#xf…

安装软件程序

包管理基础 在深入了解Linux软件包管理之前&#xff0c;本章将先介绍一些基础知识。各种主流Linux发行版都采用了某种形式的包管理系统来控制软件和库的安装。 PMS利用一个数据库来记录各种相关内容&#xff1a;  Linux系统上已安装了什么软件包&#xff1b;  每个包安装了…

android 获取已安装应用(App)列表

效果图 获取手机上已安装应用列表&#xff0c;将获取到的信息用集合返回&#xff0c;可以自己定义是否过滤系统应用&#xff0c;Appinfo是一个实体类&#xff0c;包含应用的名称 包名 图标icon等等 /*** 获取手机已安装应用列表* param ctx* param isFilterSystem 是否过滤系统…

应用程序安装流程

https://maoao530.github.io/2017/01/18/package-install/ 本文介绍APK的安装流程。 一、安装流程图 APK安装流程&#xff0c;总体可以下图流程&#xff0c;用ProcessOn画的&#xff0c;凑合看&#xff1a; 从上图我们可以看到apk安装到最后都会调用到这个flow&#xff1a; PM…

app 安装方式

APK的安装方式主要有以下几种&#xff1a; 通过adb命令安装&#xff1a;adb 命令包括adb push/install用户下载的Apk&#xff0c;通过系统安装器PackageInstaller安装该Apk。PackageInstaller是系统内置的应用程序&#xff0c;用于安装和卸载应用程序。系统开机时安装系统应用…