【SpringCloud】Ribbon源码解析

e246a1dda09849a5a89535a62441565d.png

ribbon是一个负载均衡组件,它可以将请求分散到多个服务提供者实例中,提高系统的性能和可用性。本章分析ribbon是如何实现负载均衡的

1、@LoadBalanced

消费者在引入ribbon组件后,给http客户端添加@LoadBalanced注解就可以启用负载均衡功能。@LoadBalanced注解比较简单,本身没有包含什么业务逻辑,值得一提的是@Qualifier注解。@Qualifier通常配合@Autowired一起使用,当有多个同类型的bean时可以根据@Qualifier指定的bean名称完成注入;此外在bean的入口和出口使用@Qualifier修饰,也能建立稳定的供应关系,ribbon收集所有被@LoadBalanced修饰的restTemplate,用的就是这种方法

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
@Component
@Qualifier
public class A {}


public class B {
  
   // 精准注入A
   @Autowired
   @Qualifier
   private A a;
}

2、LoadBalancerAutoConfiguration

和负载均衡相关的配置在LoadBalancerAutoConfiguration内,查看spring-cloud-common包的spring.factories文件,我们知道这是一个自动配置类

# AutoConfiguration
...
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
...

LoadBalancerAutoConfiguration注入了所有使用@LoadBalanced修饰的restTemplate,为什么加了@LoadBalanced就能引入其他其他被@LoadBalanced修饰的bean,原因就是1中提到的@Qualifier。

LoadBalancerAutoConfiguration内部还有一个loadBalancedRestTemplateInitializerDeprecated方法,这个方法会对restTemplate做定制化处理

@LoadBalanced
@Autowired(
	required = false
)
private List<RestTemplate> restTemplates = Collections.emptyList();

@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
	return () -> {
		restTemplateCustomizers.ifAvailable((customizers) -> {
			Iterator var2 = this.restTemplates.iterator();

			while(var2.hasNext()) {
				RestTemplate restTemplate = (RestTemplate)var2.next();
				Iterator var4 = customizers.iterator();

				while(var4.hasNext()) {
					RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next();
					// 定制化处理
					customizer.customize(restTemplate);
				}
			}

		});
	};
}

RestTemplateCustomizer接口的实现类在LoadBalancerAutoConfiguration内,它们的定制化处理其实就是给restTemplate添加RetryLoadBalancerInterceptor拦截器

@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
	return (restTemplate) -> {
		List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
		list.add(loadBalancerInterceptor);

		// 客户端添加拦截器
		restTemplate.setInterceptors(list);
	};
}

3、RetryLoadBalancerInterceptor

查看RetryLoadBalancerInterceptor的拦截方法,核心语句是loadBalancer.choose,也就是说服务实例的选择功能其实是由LoadBalancerClient接口实现类完成的

private final LoadBalancerClient loadBalancer;

public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
	...

		if (serviceInstance == null) {
			if (LOG.isDebugEnabled()) {
				LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. Reattempting service instance selection");
			}

			// 选择服务实例
			serviceInstance = this.loadBalancer.choose(serviceName);
			if (LOG.isDebugEnabled()) {
				LOG.debug(String.format("Selected service instance: %s", serviceInstance));
			}
		}
		...
		ClientHttpResponse response = (ClientHttpResponse)
		this.loadBalancer.execute(serviceName, serviceInstance, this.requestFactory.createRequest(request, body, execution));       
	...
}

RibbonLoadBalancerClient是LoadBalancerClient接口实现类之一,查看它的choose方法

// 选择合适的服务实例
public ServiceInstance choose(String serviceId, Object hint) {
	Server server = this.getServer(this.getLoadBalancer(serviceId), hint);
	return server == null ? null : new RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
}

// 选择合适的服务器
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
	return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}

选择服务器的任务移交给了ILoadBalancer的实现类,如ZoneAwareLoadBalancer。ZoneAwareLoadBalancer会调用父类BaseLoadBalancer的choose方法,按照指定的策略选取服务,如轮询、加权等

public Server chooseServer(Object key) {
	if (this.counter == null) {
		this.counter = this.createCounter();
	}

	this.counter.increment();
	if (this.rule == null) {
		return null;
	} else {
		try {
			// 按规则选择服务
			return this.rule.choose(key);
		} catch (Exception var3) {
			logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", new Object[]{this.name, key, var3});
			return null;
		}
	}
}

再聊聊服务的来源,LoadBalancer是从哪里挑选的服务?

ZoneAwareLoadBalancer在创建时会调用父类DynamicServerListLoadBalancer的构造方法,然后调用updateListOfServers方法获取服务列表

public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList<T> serverList, ServerListFilter<T> filter, ServerListUpdater serverListUpdater) {
	...
	this.restOfInit(clientConfig);
}

void restOfInit(IClientConfig clientConfig) {
	...
	this.updateListOfServers();
	...
}

@VisibleForTesting
public void updateListOfServers() {
	List<T> servers = new ArrayList();
	if (this.serverListImpl != null) {

		// 获取服务列表
		servers = this.serverListImpl.getUpdatedListOfServers();
		LOGGER.debug("List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);
		if (this.filter != null) {
			servers = this.filter.getFilteredListOfServers((List)servers);
			LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);
		}
	}

	this.updateAllServerList((List)servers);
}

看到服务实例自然联想到eureka,跟踪getUpdatedListOfServers方法

调用链:
-> DynamicServerListLoadBalancer.updateListOfServers
-> this.serverListImpl.getUpdatedListOfServers;
-> DiscoveryEnabledNIWSServerList.obtainServersViaDiscovery
-> eurekaClient.getInstancesByVipAddress
private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
	...
	// eureka客户端拉取服务
	List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, this.isSecure, this.targetRegion);
	Iterator var8 = listOfInstanceInfo.iterator();
	...
}

eureka客户端从远程拉取服务实例,然后ribbon从服务实例中挑选服务

3、总结

ribbon组件向restTemplate中添加拦截器,实现负载均衡功能增强

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/771599.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB贝叶斯线性回归模型案例

采用辛烷值数据集“spectra_data.mat”(任意数据集均可),介绍贝叶斯线性回归模型的构建和使用流程。 运行结果如下: 训练集预测精度指标如下: 训练集数据的R2为: 1 训练集数据的MAE为: 0.00067884 训练集数据的RMSE为: 0.00088939 测试集预测精度指标如下: 测试集数据的R2…

Python学习之小游戏--坦克大作战

今天跟视频学习了Python实现坦克大作战小游戏&#xff0c;挺有意思的&#xff0c;一起来玩吧~ 按空格发射子弹&#xff0c;上下左右键实现移动&#xff0c;ESC键无限复活。 import pygame,time,random from pygame.sprite import Sprite SCREEN_WIDTH800 SCREEN_HEIGHT500 BG…

如何改善提示词,让 GPT-4 更高效准确地把视频内容整体转换成文章?

&#xff08;注&#xff1a;本文为小报童精选文章。已订阅小报童或加入知识星球「玉树芝兰」用户请勿重复付费&#xff09; 让我们来讨论一下大语言模型应用中的一个重要原则 ——「欲速则不达」。 作为一个自认为懒惰的人&#xff0c;我一直有一个愿望&#xff1a;完成视频制作…

typescript2-类的类型

/* 输出 吃饭 游泳 */ []( )继承与多态------------------------------------------------------------------------1. 子类继承父类特征子类 extends 父类2. 当需要父类参数传递时&#xff0c;用子类也可以&#xff0c;这就是多态/* 继承&#xff1a;子类继承父类 多态…

集团型企业组织架构复杂,业务线多,如何进行高效费用管控?

企业管理中流行这样一句话&#xff1a;“企业转型&#xff0c;财务先行”。对集团型企业而言&#xff0c;当今的发展形势下&#xff0c;通过财务战略全面转型、最终撬动企业价值提升&#xff0c;是一件难而正确的事情。 集团企业具有经营规模大、产业链多、分支机构多、地域跨度…

容器部署rabbitmq集群迁移

1、场景&#xff1a; 因业务需要&#xff0c;要求把rabbitmq-A集群上的数据迁移到rabbitmq-B集群上&#xff0c;rabbitmq的数据包括元数据&#xff08;RabbitMQ用户、vhost、队列、交换和绑定&#xff09;和消息数据&#xff0c;而消息数据存储在单独的消息存储库中。 2、迁移要…

中国算力网络市场发展分析

中国算力网络市场发展现状 算力涵盖计算、内存、存储等全方位能力&#xff0c;广泛分布于网络边缘、云计算中心、联网设备及转发节点。随着数字化技术革新&#xff0c;算力与网络正深度融合&#xff0c;推动“算网一体化”的演进。这一新型基础设施日渐凸显其重要性&#xff0c…

番外篇 | YOLOv8改进之即插即用全维度动态卷积ODConv + 更换Neck网络为GFPN

前言:Hello大家好,我是小哥谈。本文所做出的改进是在YOLOv8中引入即插即用全维度动态卷积ODConv和更换Neck网络为GFPN,希望大家学习之后能够有所收获~!🌈 目录 🚀1.基础概念 🚀2.网络结构 🚀3.添加步骤 🚀4.改进方法 🍀🍀步骤1:block.py文件修改…

Kamailio-Web管理页面Siremis的安装与部署

siremis 是针对于 Kamailio 的web管理接口&#xff0c;使用PHP书写&#xff0c;更新至2020年&#xff0c;相对不是太新但是是官方友链的 以下就采用 Ubuntu 22.04Siremis 5.8.0apache http server 2.4php7.0 如有疑问请参看官方指南 以下开始介绍操作步骤 安装apache2.4 we…

一文读懂什么是“GPU算力”

在数字化转型的浪潮中&#xff0c;各行业对算力的需求日益激增&#xff0c;GPU&#xff08;图形处理单元&#xff09;算力作为推动科技进步的重要力量&#xff0c;正逐步从传统的图形渲染领域扩展到人工智能、大数据分析、高性能计算等多个前沿领域。通过本文&#xff0c;深入剖…

亮相2024世界人工智能大会,扫描全能王AIGC“黑科技”助力敦煌遗书数字化修复

7月4日&#xff0c;2024年世界人工智能大会&#xff08;简称“大会”&#xff09;在上海举行。这次这场科技与创新的盛会上&#xff0c;一张古朴、典雅的卷轴吸引了众人的目光。这张被修复的卷轴脱胎于敦煌遗书系列古籍&#xff0c;在被机器拍摄扫描后&#xff0c;卷轴上脏污、…

Airflow: 大数据调度工具详解

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 欢迎关注微信公众号&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&a…

SalesForce集成案例-获取联系人信息

SalesForce本身比较复杂&#xff0c;涉及的东西比较多&#xff0c;下面以使用REST API接口为例&#xff0c;介绍与SalesForce集成的过程&#xff0c;集成案例&#xff1a;获取联系人信息。 首先需要注册一个免费的开发者帐号&#xff0c;具有完全操作SalesForce的权限。 1、注…

Echarts中的热力图和漏斗图(在Vue中使用热力图和漏斗图)

热力图 (Heatmap) Echarts的热力图用于展示两个维度数据矩阵中的值分布情况。它通过在平面上划分成多个矩形区域&#xff0c;并用不同的颜色填充这些区域来表示数据的大小或强度。颜色渐变从浅到深通常映射着数值从小到大&#xff0c;从而直观展示数据的集中程度和分布模式。热…

STM32工业自动化控制系统教程

目录 引言环境准备工业自动化控制系统基础代码实现&#xff1a;实现工业自动化控制系统 4.1 数据采集模块 4.2 数据处理与分析 4.3 控制系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;工业自动化与优化问题解决方案与优化收尾与总结 1. 引言 工业自动化控制系统利用…

INFINI Console 使用介绍

上次在《INFINI Easysearch尝鲜Hands on》中我们部署了两个节点的Easysearch&#xff0c;并且也设置了Console对集群进行监控。那么今天我们再来介绍下INFINI Console的使用。 INFINI Console 仪表盘功能介绍 INFINI Console 是一个功能强大的数据管理和分析平台&#xff0c;…

JBoss JMXInvokerServlet 反序列化漏洞

漏洞原理&#xff1a; 这是经典的JBoss反序列化漏洞&#xff0c;JBoss在/invoker/JMXInvokerServlet请求中读取了用户传入的对象&#xff0c;然后我们利用Apache Commons Collections中的Gadget执行任意代码。 影响版本&#xff1a; JBoss Enterprise Application Platform 6…

实时数仓Hologres OLAP场景核心能力介绍

作者&#xff1a;赵红梅 Hologres PD OLAP典型应用场景与痛点 首先介绍典型的OLAP场景以及在这些场景上的核心痛点&#xff0c;OLAP典型应用场景很多&#xff0c;总结有四类&#xff1a;第一类是BI报表分析类&#xff0c;例如BI报表&#xff0c;实时大屏&#xff0c;数据中台等…

Java项目:基于SSM框架实现的班主任助理管理系统【ssm+B/S架构+源码+数据库+开题报告+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的班主任助理管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功…

iOS App 测试环境升级,遇到的问题以及解决方法

iOS App 测试环境升级&#xff0c;遇到的问题以及解决方法 Mac 实体机升级到 Sonima 14.5 Xcode 升级到 15.3 问题1&#xff1a; Xcode 编译 WebDriverAgent 失败 尝试下载 最新版本的WDA 源码编译&#xff0c;可以编译成功。 问题2&#xff1a;具体坐标直接点击的代码都会报错…