Spring-Cloud-Loadblancer详细分析_3

前两篇文章介绍了加载过程,本文从Feign的入口开始分析执行过程,还是从FeignBlockingLoadBalancerClient.execute来入手

public class FeignBlockingLoadBalancerClient implements Client {private static final Log LOG = LogFactory.getLog(FeignBlockingLoadBalancerClient.class);private final Client delegate;private final LoadBalancerClient loadBalancerClient;private final LoadBalancerClientFactory loadBalancerClientFactory;@Overridepublic Response execute(Request request, Request.Options options) throws IOException {//请求路径final URI originalUri = URI.create(request.url());//获取到要调用的服务idString serviceId = originalUri.getHost();DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(new RequestDataContext(buildRequestData(request), hint));//在这步创建了每个服务的子容器		Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors(//在这步创建了每个服务的子容器loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),RequestDataContext.class, ResponseData.class, ServiceInstance.class);supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));		//执行loadBalancer的负载均衡策略,返回将过滤后的服务,非常重要ServiceInstance instance = loadBalancerClient.choose(serviceId, lbRequest);org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(instance);//省略...//将ServiceInstance进行解析后,转换为真正的http方式进行远程调用服务String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();Request newRequest = buildRequest(request, reconstructedUrl);LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse,supportedLifecycleProcessors, loadBalancerProperties.isUseRawStatusCodeInResponseData());}protected Request buildRequest(Request request, String reconstructedUrl) {return Request.create(request.httpMethod(), reconstructedUrl, request.headers(), request.body(),request.charset(), request.requestTemplate());}private String getHint(String serviceId) {LoadBalancerProperties properties = loadBalancerClientFactory.getProperties(serviceId);String defaultHint = properties.getHint().getOrDefault("default", "default");String hintPropertyValue = properties.getHint().get(serviceId);return hintPropertyValue != null ? hintPropertyValue : defaultHint;}}

loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class)

在这步是创建了每个服务的负载均衡子容器,loadBalancerClientFactory的生成过程在上篇文章已经分析了,这里getInstances实际调用的是父类NamedContextFactory

NamedContextFactory.getInstances

public <T> Map<String, T> getInstances(String name, Class<T> type) {AnnotationConfigApplicationContext context = getContext(name);return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
}

NamedContextFactory.getContext

protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {if (!this.contexts.containsKey(name)) {//创建子容器,name为服务名 value为容器this.contexts.put(name, createContext(name));}}}return this.contexts.get(name);
}

NamedContextFactory.createContext

protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context;//创建容器if (this.parent != null) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();if (parent instanceof ConfigurableApplicationContext) {beanFactory.setBeanClassLoader(((ConfigurableApplicationContext) parent).getBeanFactory().getBeanClassLoader());}else {beanFactory.setBeanClassLoader(parent.getClassLoader());}context = new AnnotationConfigApplicationContext(beanFactory);context.setClassLoader(this.parent.getClassLoader());}else {context = new AnnotationConfigApplicationContext();}//configurations就是LoadBalancerClientSpecification类型的LoadBalancerAutoConfiguration和BlockingLoadBalancerClientAutoConfiguration//这里是将@LoadBalancerClient对应的负载均衡配置注册到对应的容器中//由以上可知通过此步我们可以使用@LoadBalancerClient自定义负载均衡策略//如果不自定义的话,这里为falseif (this.configurations.containsKey(name)) {for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {context.register(configuration);}}//这时的configurations中会有LoadBalancerClientSpecification类型的LoadBalancerAutoConfiguration和BlockingLoadBalancerClientAutoConfigurationfor (Map.Entry<String, C> entry : this.configurations.entrySet()) {if (entry.getKey().startsWith("default.")) {//不会进去for循环体,因为LoadBalancerAutoConfiguration和BlockingLoadBalancerClientAutoConfiguration//包装成LoadBalancerClientSpecification后entry.getValue().getConfiguration()没有值for (Class<?> configuration : entry.getValue().getConfiguration()) {context.register(configuration);}}}//defaultConfigType就是LoadBalancerClientConfiguration,在子类LoadBalancerClientFactory的构造方法传入//在刚才分析LoadBalancerClientFactory的时候介绍过context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.<String, Object>singletonMap(this.propertyName, name)));//将父容器添加进去if (this.parent != null) {// Uses Environment from parent as well as beanscontext.setParent(this.parent);}context.setDisplayName(generateDisplayName(name));context.refresh();return context;
}

重点:

可以看到当每个服务创建子容器后,LoadBalancerAutoConfigurationBlockingLoadBalancerClientAutoConfigurationLoadBalancerClientConfiguration这三个非常重要的装配配置类都注册到了每个子容器中

服务的子容器创建完成后,下面就开始执行负载均衡的流程了

BlockingLoadBalancerClient.choose

public <T> ServiceInstance choose(String serviceId, Request<T> request) {//获取负载均衡器ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);if (loadBalancer == null) {return null;}Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();if (loadBalancerResponse == null) {return null;}return loadBalancerResponse.getServer();
}

loadBalancerClientFactory.getInstance(serviceId)

public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
}

这时的getInstance调用的是父类NamedContextFactory.getInstance

NamedContextFactory.getInstance

public <T> T getInstance(String name, Class<T> type) {AnnotationConfigApplicationContext context = getContext(name);try {return context.getBean(type);}catch (NoSuchBeanDefinitionException e) {// ignore}return null;
}
protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {if (!this.contexts.containsKey(name)) {this.contexts.put(name, createContext(name));}}}return this.contexts.get(name);
}

刚才介绍了,在BlockingLoadBalancerClient.choose执行之前,子容器已经创建完毕,这里就执行返回

容器返回后,获取ReactorServiceInstanceLoadBalancer类型的对象,此类型的对象就是在LoadBalancerClientConfiguration创建的,我们再看一眼

@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;@Bean@ConditionalOnMissingBeanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}//省略
}	

RoundRobinLoadBalancer结构图
在这里插入图片描述
所以loadBalancerClientFactory.getInstance(serviceId)返回的就是RoundRobinLoadBalancer,然后就会调用此策略进行执行

RoundRobinLoadBalancer.choose

public Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
  • serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new)就是获取注册中心的服务列表了
  • processInstanceResponse执行具体的负载均衡策略

serviceInstanceListSupplierProvider

public T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {return delegate().getIfAvailable(defaultSupplier);
}default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {T dependency = getIfAvailable();//dependency 的类型是CachingServiceInstanceListSupplierreturn (dependency != null ? dependency : defaultSupplier.get());
}

RoundRobinLoadBalancer.processInstanceResponse

private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,List<ServiceInstance> serviceInstances) {//serviceInstances就是获取的服务列表了	//getInstanceResponse(serviceInstances)就是真正的负债均衡策略了Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {//((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;
}

RoundRobinLoadBalancer.getInstanceResponse

private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + serviceId);}return new EmptyResponse();}// Do not move position when there is only 1 instance, especially some suppliers// have already filtered instancesif (instances.size() == 1) {return new DefaultResponse(instances.get(0));}// Ignore the sign bit, this allows pos to loop sequentially from 0 to// Integer.MAX_VALUEint pos = this.position.incrementAndGet() & Integer.MAX_VALUE;ServiceInstance instance = instances.get(pos % instances.size());return new DefaultResponse(instance);
}

可以看到是轮训的策略

那么服务列表是怎么获取的呢,在下篇文章中我们回详细的分析

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023全新UI好看的社区源码下载/反编译版

2023全新UI好看的社区源码下载/反编译版 这次分享一个RuleAPP二开美化版&#xff08;尊重每个作者版权&#xff09;&#xff0c;无加密可反编译版本放压缩包了&#xff0c;自己弄吧&#xff01;&#xff01;&#xff01; RuleAPP本身就是一款免费开源强大的社区&#xff0c;基…

【MySQL--->数据库操作】

文章目录 [TOC](文章目录) 一、操作语句1.增2.删3.改4.查5.备份 二、字符集与校验规则 一、操作语句 1.增 语句格式:create database [if no exists]数据库名[create_specification [,create_specification] …]; 中括号内是可选项,if no exists是指如果数据库不存在就创建,存…

Win10启动Jmeter报错提示jmeter.log拒绝访问问题

jmeter版本&#xff1a;5.4.1 查看版本 在dos命令窗口中进入jmeter安装目录下的bin目录中&#xff1a;执行jmeter - v命令 我启动的方式是&#xff1a;进入jmeter安装目录下的bin目录中双击jmeter.bat启动的。结果报错&#xff0c;但是不影响使用。 报错日志如下&#xff1a; …

分类过程中的一种遮挡现象

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点&#xff0c;AB训练集各由6张二值化的图片组成&#xff0c;让A&#xff0c;B中各有3个点&#xff0c;且不重合&#xff0c;统计迭代次数并排序。 其中有10组数据 差值结构 迭代次数 构造平均列A 构造平均列AB…

spring-自定义AOP面向切面注解--统一切面处理-登陆信息采集

2023华为OD统一考试&#xff08;AB卷&#xff09;题库清单-带答案&#xff08;持续更新&#xff09;or2023年华为OD真题机考题库大全-带答案&#xff08;持续更新&#xff09; 1. 先写一个登陆记录注解&#xff08;//记录&#xff1a;XXX时间&#xff0c;XXX姓名&#xff0c;XX…

编译redis-5.0.9报错zmalloc.h:50:31: 致命错误:jemalloc/jemalloc.h:没有那个文件或目录问题解决

上图 解决&#xff1a; make && make install MALLOClibc原因: 原因是jemalloc重载了Linux下的ANSI C的malloc和free函数。

机器视觉项目流程和学习方法

机器视觉项目流程&#xff1a; 00001. 需求分析和方案建立 00002. 算法流程规划和业务逻辑设计 00003. 模块化编程和集成化实现 00004. 调试和优化&#xff0c;交付客户及文档 学习机器视觉的方法&#xff1a; 00001. 实战学习&#xff0c;结合项目经验教训 00002. 学习…

视频监控汇聚EasyCVR平台WebRTC流地址无法播放的原因排查

开源EasyDarwin视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多…

Mac下⬇️Git如何下载/上传远程仓库

使用终端检查电脑是否安装Git git --version 通过此文章安装Git ➡️ ​​​​​​​传送门&#x1f310; 方式1⃣️使用终端操作 1.下载——克隆远程仓库到本地 git clone [远程地址] 例&#xff1a;git clone https://gitee.com/lcannal/movie.git​ 2.编…

Redis简单学习

Redis是一个基于内存的key-value结构数据库 linux上面安装&#xff1a; Redis存储的是key-value结构的数据&#xff0c;其中key是字符串&#xff0c;value有常见的5中数据类型&#xff1a; 字符串 string哈希 hash列表 list集合 set有序集合 sorted set 字符串常用操作&am…

MySQL语句总和之表数据操作(增删改查)

目录 1、增加 insert into 表 (字段1&#xff0c; 字段3&#xff0c; 字段5) values(value1, value2, value3&#xff09; insert into 表 [(字段1&#xff0c; 字段2&#xff0c; 字段3....)] values(value1, value2,value3.....)[,(value1, value2, value3....) .....] i…

React实现关键字高亮

先看效果&#xff1a; 实现很简单通过以下这个函数&#xff1a; highLight (text, keyword ) > {return text.split(keyword).flatMap(str > [<span style{{ color: red, fontWeight: bold }}>{keyword}</span>, str]).slice(1);}展示某段文本时调用该函数…

ECS服务器安装docker

​ 为了安装并配置 Docker &#xff0c;你的系统必须满足下列最低要求&#xff1a; 64 位 Linux 或 Windows 系统 如果使用 Linux &#xff0c;内核版本必须不低于 3.10 能够使用 sudo 权限的用户 在你系统 BIOS 上启用了 VT&#xff08;虚拟化技术&#xff09;支持 on your s…

python工具库有哪些,python工具包怎么用

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python工具包有哪些&#xff0c;python工具包怎么用&#xff0c;现在让我们一起来看看吧&#xff01; 最近有多位读者留言&#xff0c;咨询更便捷、高效的python编程开发工具&#xff08;IDE&#xff09;&#xff0c;本…

分布式 - 服务器Nginx:一小时入门系列之动静分离

文章目录 1. 动静分离的好处2. 分离静态文件3. 修改 Nginx 配置文件4. location 命令修饰符优先级 1. 动静分离的好处 Apache Tocmat 严格来说是一款java EE服务器&#xff0c;主要是用来处理 servlet请求。处理css、js、图片这些静态文件的IO性能不够好&#xff0c;因此&…

JZ32 从上往下打印二叉树(Java)

题目地址&#xff1a;从上往下打印二叉树_牛客题霸_牛客网 题目回顾&#xff1a; 不分行从上往下打印出二叉树的每个节点&#xff0c;同层节点从左至右打印。例如输入{8,6,10,#,#,2,1}&#xff0c;如以下图中的示例二叉树&#xff0c;则依次打印8,6,10,2,1(空节点不打印&…

MYSQL幻读问题

幻读是什么&#xff1f; “Phantom Problem是指在同一事务下&#xff0c;连续执行两次同样的SQL语句可能导致不同的结果&#xff0c;第二次的SQL语句可能会返回之前不存在的行。”摘录来自 MySQL技术内幕&#xff1a;InnoDB存储引擎(第2版) (数据库技术丛书) ​ 通俗来说就是&a…

爆肝整理,Python自动化测试-Pytest参数化实战封装,一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 参数化&#xff1…

原型和原型链理解

这个图大概能概括原型和原型链的关系 1.对象都是通过 _proto_ 访问原型 2.原型都是通过constructor 访问构造函数 3.原型是构造函数的 prototype 4.原型也是对象实例 也是通过 _proto_ 访问原型(Object.prototype) 5.Object.prototype的原型通过 _proto_ 访问 为null 那么…

智能合约 -- 常规漏洞分析 + 实例

1.重入攻击 漏洞分析 攻击者利用合约漏洞&#xff0c;通过fallback()或者receive()函数进行函数递归进行持续取钱。 刚才试了一下可以递归10次&#xff0c;貌似就结束了(version: 0.8.20)。 直接看代码: 银行合约&#xff1a;有存钱、取钱、查看账户余额等函数。攻击合约:…