【SpringCloud】Ribbon源码解析

news2024/11/23 18:29:36

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中添加拦截器,实现负载均衡功能增强

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LangChain 入门上篇:模型 I/O 封装

LangChain 是面向大模型的开发框架&#xff0c;是 AGI 时代软件工程的探索和原型。学习 LangChain 需要关注接口的变更。 LangChain 的核心组件 1.模型 I/O 封装 LLMS 大语言模型Chat Models 一套基于 LLMS&#xff0c;但按对话结构重新封装PromptTemplate 提示词模板Output…

Unity中TimeLine的一些用法

Unity中TimeLine的一些用法 概念其他 概念 无Track模式&#xff08;PlayableAsset、PlayableBehaviour&#xff09; 1. 两者关系 运行在PlayableTrack中作用 PlayableBehaviour 实际执行的脚本字段并不会显示在timeline面板上 PlayableAsset PlayableBehaviour的包装器&#x…

uboot run命令基本使用

run 命令可以用于运行环境变量的中定义的命令,run bootcmd 可以运行bootcmd中启动命令 作用:可以运行我们自定义的环境变量 include/command.h common/cli.c /*** board_run_command() - Fallback function to execute a command** When no command line features are enabled …

性能测试-JMeter学习

1、给不同的访问口分配访问占比&#xff1b;例&#xff1a;登录30%&#xff0c;首页&#xff1a;20%&#xff0c;新增&#xff1a;50% 不同业务放到不同线程组里&#xff0c;实现不同业务的分配 使用吞吐量控制器&#xff0c;设置不同的占比 使用if控制器&#xff0c;设置不同…

mac鼠标键盘共享:ShareMouse for Mac 激活版

hareMouse 是一款 Windows 和 macOS 操作系统上的共享和切换鼠标和键盘的实用工具。这款软件允许用户在多台计算机之间无缝地共享鼠标和键盘&#xff0c;使得在不同设备之间进行工作和操作变得更加便捷。占用资源少&#xff1a; ShareMouse 设计轻量&#xff0c;占用系统资源较…

Logback日志配置两种方式

SpringBoot 默认使用的是Logback 1. 在resource新建文件logback-spring.xml&#xff0c;配置日志相关信息 <configuration><property name"app.name" value"order-service"/><property name"log.path" value"./logs/"…

克隆gitee仓库,在vs2022创建文件夹开发项目操作步骤

git网站 git知识大全 git教程&#xff1a;廖雪峰的官方网站 git菜鸟教程 gitee之创建项目步骤 同步源仓库 2. 克隆命令 3. 右击git Bash Here>粘贴命令行 4. 选中项目文件夹》创建本人文件夹&#xff08;ZYY&#xff09; 5. 打开vs2022》新建项目》选择Framework》下…

C++之boost智能指针

1、boost智能指针 资源获取即初始化&#xff1a;在构造函数中对资源进行初始化&#xff0c;在析构函数中释放。 智能指针的本质思想是&#xff1a;将堆对象的生存期&#xff0c;用栈对象来管理。这个栈对象就是智能指针。 当new 一个堆对象的时候&#xff0c;立刻用智能指针…

VS2022(Visual Studio 2022)最新安装教程

1、下载 1、下载地址 - 官网地址&#xff1a;下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux - 根据自己的电脑的 【操作系统】 灵活选择。 2、安装包 【此处为Windows系统安装包】 2、安装 1、打开软件 - 右击【以管理员身份打开】&#xff0c; 2、准备配置 …

【Unity 角色控制器组件】

【Unity 角色控制器组件】 Character Controller&#xff1a; Unity 内置的一个组件&#xff0c;用于提供高级的物理控制&#xff0c;允许开发者控制角色的移动、跳跃和碰撞。 csharp csharp // 假设你已经有了一个带有Character Controller组件的游戏对象// 获取Character Co…

网安小贴士(7)网络加密

一、前言 网络加密的历史是一个长期发展的过程&#xff0c;其起源可以追溯到古代文明&#xff0c;主要用于战争时期的通信保密&#xff0c;其目的始终是为了保护信息的安全和保密。 二、定义 网络加密是一种安全措施&#xff0c;它通过使用编码算法对通过网络&#xff08;例…

TreeSize Free - 硬盘空间管理工具

TreeSize FreeTreeSize Free 是一款免费的强大灵活的硬盘空间管理工具。可以帮你找出硬盘上最大的目录以及它占用的空间。支持空间大小显示、分配空间和占用空间、文件数、3D工具条和分配图、最近使用数据、文件作者、NTFS压缩率等信息&#xff0c;并支持搜索文件。该软件类似浏…

使用Anaconda虚拟环境安装Opencv、pytorch、torchvision踩坑记录

电脑 python 环境版本过高与下载Opencv&#xff08;3.4以下&#xff09;不匹配&#xff0c;因为版本过高部分算法收米&#xff0c; 从而在虚拟环境重新下载python老版本 本文默认您的电脑上已经安装了Anaconda 我是按照这位博文安装的 安装Opencv (详解)安装3.4.1.15版本…

linux中awk,sed, grep使用

《linux私房菜》这本书中将sed和awk一同归为行的修改这一点&#xff0c;虽然对&#xff0c;但不利于实际处理问题时的思考。因为这样的话&#xff0c;当我们实际处理问题时&#xff0c;遇到比如说统计文本打印内容时&#xff0c;我们选择sed还是awk进行处理呢&#xff1f; 也因…

论文解读——掌纹生成网络 RPG-Palm

论文&#xff1a;RPG-Palm: Realistic Pseudo-data Generation for Palmprint Recognition&#xff08;2023.7&#xff09; 作者&#xff1a;Lei Shen, Jianlong Jin, Ruixin Zhang, Huaen Li, Kai Zhao, Yingyi Zhang, Jingyun Zhang, Shouhong Ding, Yang Zhao, Wei Jia 链接…

React、JSX简介、渲染列表、基础和复杂的条件渲染

目录 一、简介 1、搭建环境 2、回到项目&#xff08;VScode&#xff09; 3、项目核心渲染路径 4、网站资料&#xff08;启动项目的方法&#xff09; 二、JSX 三、实现渲染列表 四、实现条件渲染 五、实现复杂条件渲染 一、简介 1、搭建环境 npx creat-react-app reac…

视图库对接系列(GA-T 1400)七、视图库对接系列(本级)校时

校时 1400文档中的定义 在我做的项目中, 一般情况下用的比较少, 我目前的这个设备型号也不支持这个接口, 对接下级视图库平台的时候,其他平台对这个接口也不怎么使用。我理解的话 大概率是用于平台之间的校时或者验证二个服务器之间的时间使用的。 目前该接口我没有实现…

2024亚太赛(中文赛)数学建模竞赛选题建议+初步分析

提示&#xff1a;DS C君认为的难度&#xff1a;B<C<A&#xff0c;开放度&#xff1a;C<A<B。 综合评价来看 A题适合有较强计算几何和优化能力的团队&#xff0c;难度较高&#xff0c;但适用面较窄。 B题数据处理和分析为主&#xff0c;适合数据科学背景的团队…

IT服务管理的最佳实践:光大银行案例分析

IT服务管理的最佳实践&#xff1a;光大银行案例分析 在信息技术日益发展的今天&#xff0c;IT运维已成为企业不可或缺的一部分。为了确保系统的稳定运行和业务的连续性&#xff0c;智能化运维实践中的IT健康管理显得尤为重要。本文将结合最新的互联网技术&#xff0c;探讨智能化…

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

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