Springcloud 微服务实战笔记 Ribbon

使用

 @Configurationpublic class CustomConfiguration {@Bean@LoadBalanced // 开启负载均衡能力public RestTemplate restTemplate() {return new RestTemplate();}}

可看到使用Ribbon,非常简单,只需将@LoadBalanced注解加在RestTemplate的Bean上,就可以实现负载均衡。

@LoadBalanced

从该注解的源码上的注释,可看到LoadBalancerClient类来配置的

/*** Annotation to mark a RestTemplate or WebClient bean to be configured to use a* LoadBalancerClient.* @author Spencer Gibb*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {}

查看LoadBalancerClient源码,如下:

/*** Represents a client-side load balancer.** @author Spencer Gibb*/
public interface LoadBalancerClient extends ServiceInstanceChooser {/*** Executes request using a ServiceInstance from the LoadBalancer for the specified* service.* @param serviceId The service ID to look up the LoadBalancer.* @param request Allows implementations to execute pre and post actions, such as* incrementing metrics.* @param <T> type of the response* @throws IOException in case of IO issues.* @return The result of the LoadBalancerRequest callback on the selected* ServiceInstance.*/<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;/*** Executes request using a ServiceInstance from the LoadBalancer for the specified* service.* @param serviceId The service ID to look up the LoadBalancer.* @param serviceInstance The service to execute the request to.* @param request Allows implementations to execute pre and post actions, such as* incrementing metrics.* @param <T> type of the response* @throws IOException in case of IO issues.* @return The result of the LoadBalancerRequest callback on the selected* ServiceInstance.*/<T> T execute(String serviceId, ServiceInstance serviceInstance,LoadBalancerRequest<T> request) throws IOException;/*** Creates a proper URI with a real host and port for systems to utilize. Some systems* use a URI with the logical service name as the host, such as* http://myservice/path/to/service. This will replace the service name with the* host:port from the ServiceInstance.* @param instance service instance to reconstruct the URI* @param original A URI with the host as a logical service name.* @return A reconstructed URI.*/URI reconstructURI(ServiceInstance instance, URI original);}
* Implemented by classes which use a load balancer to choose a server to send a request* to.** @author Ryan Baxter*/
public interface ServiceInstanceChooser {/*** Chooses a ServiceInstance from the LoadBalancer for the specified service.* @param serviceId The service ID to look up the LoadBalancer.* @return A ServiceInstance that matches the serviceId.*/ServiceInstance choose(String serviceId);}

ServiceInstanceChooser看类名便可知用来帮我们从同一个服务的多个服务实例中根据负载均衡策略选择出所需的服务实例。

choose:根据传入的服务名ServiceId,从负载均衡器中选择一个对应服务的实例。

execute:使用从负载均衡器中挑选出的服务实例来执行请求内容。

reconstructURI:为系统构建一个合适的host:port形式的URI。在分布式系统中,我们使用逻辑上的服务名称作为host来构建URI(替代服务实例的host:port形式)进行请求,比如http://myservice/path/to/service。在该操作的定义中,前者ServiceInstance对象是带有host和port的具体服务实例,而后者URI对象则是使用逻辑服务名定义为host 的 URI,而返回的 URI 内容则是通过ServiceInstance的服务实例详情拼接出的具体host:post形式的请求地址。

通过搜索@LoadBalanced使用地方发现,只有org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration使用到了@LoadBalanced

/*** Auto-configuration for Ribbon (client-side load balancing).** @author Spencer Gibb* @author Dave Syer* @author Will Tran* @author Gang Li*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {@LoadBalanced@Autowired(required = false)private List<RestTemplate> restTemplates = Collections.emptyList();@Autowired(required = false)private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();@Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> restTemplateCustomizers.ifAvailable(customizers -> {for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {for (RestTemplateCustomizer customizer : customizers) {customizer.customize(restTemplate);}}});}@Bean@ConditionalOnMissingBeanpublic LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);}@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")static class LoadBalancerInterceptorConfig {@Beanpublic LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRequestFactory requestFactory) {return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}}/*** Auto configuration for retry mechanism.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass(RetryTemplate.class)public static class RetryAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic LoadBalancedRetryFactory loadBalancedRetryFactory() {return new LoadBalancedRetryFactory() {};}}/*** Auto configuration for retry intercepting mechanism.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass(RetryTemplate.class)public static class RetryInterceptorAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic RetryLoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRetryProperties properties,LoadBalancerRequestFactory requestFactory,LoadBalancedRetryFactory loadBalancedRetryFactory) {return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,requestFactory, loadBalancedRetryFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}}}

这段自动装配的代码的含义不难理解,就是利用了RestTempllate的拦截器,使用RestTemplateCustomizer对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例ServiceInstance。

@LoadBalanced @Autowired(required = false)

这两个注解在一起的意思:只注入@LoadBalanced注解的Bean ,带有@LoadBalanced注解的RestTemplate会在原有的拦截器基础上加上LoadBalancerInterceptor拦截器

@Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> restTemplateCustomizers.ifAvailable(customizers -> {for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {for (RestTemplateCustomizer customizer : customizers) {customizer.customize(restTemplate);}}});}
@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}

可看到RestTemplate是可以设置拦截器。

org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor实现如下:

/*** @author Spencer Gibb* @author Dave Syer* @author Ryan Baxter* @author William Tran*/
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {private LoadBalancerClient loadBalancer;private LoadBalancerRequestFactory requestFactory;public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,LoadBalancerRequestFactory requestFactory) {this.loadBalancer = loadBalancer;this.requestFactory = requestFactory;}public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {// for backwards compatibilitythis(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));}@Overridepublic ClientHttpResponse intercept(final HttpRequest request, final byte[] body,final ClientHttpRequestExecution execution) throws IOException {final URI originalUri = request.getURI();String serviceName = originalUri.getHost();Assert.state(serviceName != null,"Request URI does not contain a valid hostname: " + originalUri);return this.loadBalancer.execute(serviceName,this.requestFactory.createRequest(request, body, execution));}}

可看出:将LoadBalancerClient以及LoadBalancerRequestFactory创建的请求封装到LoadBalancerInterceptor中,并在intercept()方法中,使用LoadBalancerClient执行请求。

RestTemplate调用过程

RestTemplate.getForObject/getForEntity... --> RestTemplate.excute --> RestTemplate.doExecute --> org.springframework.http.client.ClientHttpRequestFactory#createRequest

--> org.springframework.http.client.AbstractClientHttpRequest#execute

--> org.springframework.http.client.AbstractClientHttpRequest#executeInternal

--> org.springframework.http.client.InterceptingClientHttpRequest#executeInternal

--> org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute

--> org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept

--> org.springframework.cloud.client.loadbalancer.LoadBalancerClient#execute(java.lang.String, org.springframework.cloud.client.loadbalancer.LoadBalancerRequest)

--> org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#execute(java.lang.String, org.springframework.cloud.client.loadbalancer.LoadBalancerRequest)

整个流程最终可看到执行拦截器的execute方法,最终调用负载均衡的execute方法

最终调用org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept

@Overridepublic ClientHttpResponse intercept(final HttpRequest request, final byte[] body,final ClientHttpRequestExecution execution) throws IOException {final URI originalUri = request.getURI();String serviceName = originalUri.getHost();Assert.state(serviceName != null,"Request URI does not contain a valid hostname: " + originalUri);return this.loadBalancer.execute(serviceName,this.requestFactory.createRequest(request, body, execution));}

org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#execute(java.lang.String, org.springframework.cloud.client.loadbalancer.LoadBalancerRequest, java.lang.Object)

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)throws IOException {ILoadBalancer loadBalancer = getLoadBalancer(serviceId);Server server = getServer(loadBalancer, hint);if (server == null) {throw new IllegalStateException("No instances available for " + serviceId);}RibbonServer ribbonServer = new RibbonServer(serviceId, server,isSecure(server, serviceId),serverIntrospector(serviceId).getMetadata(server));return execute(serviceId, ribbonServer, request);}

此处是RibbonLoadBalancerClient的execute方法,首先获取负载均衡器(getLoadBalancer),然后通过负载均衡器获取服务,看下getServer()方法实现:

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {if (loadBalancer == null) {return null;}// Use 'default' on a null hint, or just pass it on?return loadBalancer.chooseServer(hint != null ? hint : "default");}
  public Server chooseServer(Object key) {if (counter == null) {counter = createCounter();}counter.increment();if (rule == null) {return null;} else {try {return rule.choose(key);} catch (Exception e) {logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);return null;}}}

可看到最终是调用策略器的choose方法,选择服务器。

参考资料:

Spring Cloud微服务实战

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

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

相关文章

cesium键盘控制模型

效果&#xff1a; 由于对添加模型和更新位置api进行二次了封装&#xff0c;下面提供思路 1.添加模型 const person reactive({modelTimer: null,position: {lon: 104.07274,lat: 30.57899,alt: 1200,heading: 0,pitch: 0,roll: 0,}, }); window.swpcesium.addEntity.addMo…

VirtualBox + Redhat7.6 +Oracle19C 数据库安装

软件工具&#xff1a; 虚拟化工具&#xff1a;VirtualBox-6.1.26-145957-Win.exe操作系统镜像&#xff1a;rhel-server-7.6-x86_64-dvd.iso远程连接工具&#xff1a;XmanagerPowerSuite-7.0.0004r.exe、SecureCRT 8.5.3数据库版本镜像&#xff1a;LINUX.X64_193000_grid_home.…

海外服务器2核2G/4G/8G和4核8G配置16M公网带宽优惠价格表

腾讯云海外服务器租用优惠价格表&#xff0c;2核2G10M带宽、2核4G12M、2核8G14M、4核8G16M配置可选&#xff0c;可以选择Linux操作系统或Linux系统&#xff0c;相比较Linux服务器价格要更优惠一些&#xff0c;腾讯云服务器网txyfwq.com分享腾讯云国外服务器租用配置报价&#x…

63.网游逆向分析与插件开发-游戏增加自动化助手接口-自动化助手UI与游戏菜单的对接

内容来源于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;游戏公告类的C还原-CSDN博客 码云地址&#xff08;master分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;19a2828def451a280ee211c62dcd1074e…

Spark---RDD介绍

文章目录 1.Spark核心编程2.RDD介绍2.1.RDD基本原理2.2 RDD特点1.弹性2.分布式 &#xff1a;数据存储在大数据集群的不同节点上3.数据集 &#xff1a;RDD封装了计算逻辑&#xff0c;并不保存数据4.数据抽象 &#xff1a;RDD是一个抽象类&#xff0c;具体实现由子类来实现5. 不可…

HackTheBox - Medium - Linux - BroScience

BroScience BroScience 是一款中等难度的 Linux 机器&#xff0c;其特点是 Web 应用程序容易受到“LFI”的攻击。通过读取目标上的任意文件的能力&#xff0c;攻击者可以深入了解帐户激活码的生成方式&#xff0c;从而能够创建一组可能有效的令牌来激活新创建的帐户。登录后&a…

回首2023,期待2024!

2023&#xff0c;在改变中到来 2023年1月1日&#xff0c;我从成都冷清的学校回到了哈尔滨的老家&#xff0c;开始了保研之前的最后一个寒假 当时的目标是将之前的科研理论转化为实际&#xff0c;生产出一篇sci&#xff0c;助力保研加分 星移斗转&#xff0c;事与愿违&#x…

如何设计企业级业务流程?学习华为的流程六级分类经验

业务流程管理&#xff08;BPM&#xff09;是一种系统化的方法&#xff0c;用于分析、设计、执行、监控和优化组织的业务流程&#xff0c;以实现预期的目标和价值。业务流程管理中&#xff0c;流程的分级方法有多种&#xff0c;常见的有以下几种&#xff1a; APQC的流程分级方法…

C++与数据库MySQL锁——模拟订票(事务)

假设订票的时候&#xff0c;好几个人同时进入&#xff0c;查看这张票是否售出&#xff0c;假如同时购买了这张票&#xff0c;那对于售票行业来说&#xff0c;可能就会发生低级错误。那么如何避免这类事情发生呢&#xff1f; 解决办法&#xff1a; 在一个人访问的时候&#xf…

【Docker基础一】Docker安装Elasticsearch,Kibana,IK分词器

安装elasticsearch 下载镜像 查看版本&#xff1a;Elasticsearch Guide [8.11] | Elastic # 下载镜像 docker pull elasticsearch:7.17.16 # 查看镜像是否下载成功 docker images创建网络 因为需要部署kibana容器&#xff0c;要让es和kibana容器互联 # 创建一个网络&…

解析大语言模型LLM的幻觉问题:消除错觉、提高认知

文章目录 前言一、幻觉介绍二、幻觉产生的原因三、幻觉的现象四、幻觉的分类五、幻觉解决方案六、幻觉待解决问题后记 前言 在人类的感知和认知过程中&#xff0c;幻觉一直是一个被广泛讨论和研究的问题。幻觉指的是一种虚假的感知或认知经验&#xff0c;使我们看到、听到或感…

LLM之RAG实战(十三)| 利用MongoDB矢量搜索实现RAG高级检索

想象一下&#xff0c;你是一名侦探&#xff0c;身处庞大的信息世界&#xff0c;试图在堆积如山的数据中找到隐藏的一条重要线索&#xff0c;这就是检索增强生成&#xff08;RAG&#xff09;发挥作用的地方&#xff0c;它就像你在人工智能和语言模型世界中的可靠助手。但即使是最…

LCD—液晶显示

本节主要介绍以下内容 显示器简介 液晶控制原理 秉火3.2寸液晶屏简介 使用FSMC模拟8080时序 NOR FLASH时序结构体 FSMC初始化结构体 一、显示器简介 显示器属于计算机的I/O设备&#xff0c;即输入输出设备。它是一种将特定电子信息输出到屏幕上再反射到人眼的显示工具。…

助力成长的开源项目 —— 筑梦之路

闯关式 SQL 自学&#xff1a;sql-mother 免费的闯关式 SQL 自学教程网站&#xff0c;从 0 到 1 带大家掌握常用 SQL 语法&#xff0c;目前一共有 30 多个关卡&#xff0c;希望你在通关的时候&#xff0c;变身为一个 SQL 高手。除了闯关模式之外&#xff0c;这个项目支持自由选…

UI动效设计师通往高薪之路,AE设计从基础到进阶教学

一、教程描述 UI动效设计&#xff0c;顾名思义即动态效果的设计&#xff0c;用户界面上所有运动的效果&#xff0c;也可以视其为界面设计与动态设计的交集&#xff0c;或者可以简单理解为UI设计中的动画效果&#xff0c;是UI设计中不可或缺的组成部分。现在UI设计的要求越来越…

光速爱购--靠谱的SpringBoot项目

简介 这是一个靠谱的SpringBoot项目实战&#xff0c;名字叫光速爱购。从零开发项目&#xff0c;视频加文档&#xff0c;十天就能学会开发JavaWeb项目。 教程路线是&#xff1a;搭建环境> 安装软件> 创建项目> 添加依赖和配置> 通过表生成代码> 编写Java代码&g…

Docker-Compose部署Redis(v7.2)分片集群(含主从)

文章目录 一、前提准备1. 文件夹结构 二、配置文件1. redis.conf2. docker-compose文件 三、构建集群1. 自动分配主从关系2.1 构建3 master集群2.2 手动配置从节点 四、测试1. 集群结构2. 分片测试 环境 docker desktop for windows 4.23.0redis 7.2 目标 搭建如下图分片主从…

多模态——旷视大模型Vary更细粒度的视觉感知实现文档级OCR或图表理解

概述 现代大型视觉语言模型&#xff08;LVLMs&#xff09;&#xff0c;例如CLIP&#xff0c;使用一个共同的视觉词汇&#xff0c;以适应多样的视觉任务。然而&#xff0c;在处理一些需要更精细和密集视觉感知的特殊任务时&#xff0c;例如文档级OCR或图表理解&#xff0c;尤其…

Mac环境下Parallels Desktop 19的安装和使用

为了后续构建漏洞靶场和渗透测试环境&#xff0c;我们需要提前准备好几套与宿主机隔离的工作环境&#xff08;Windows、Linux等&#xff09;&#xff0c;在Mac上最常用的就是Paralles Desktop&#xff08;PD&#xff09;工具了&#xff0c;当前最新版本为19。接下来介绍如何安装…

Linux引导过程和服务

一、Linux操作系统引导过程 1.引导过程 bios 加电自检——mbr——grub——加载内核——启动进程 加电后BIOS程序回自检硬件&#xff0c;硬件无故障后&#xff0c;会根据第一次启动项去找内核&#xff0c;一般来说第一启动项是硬盘&#xff0c;找到硬盘后&#xff0c;会根据mb…