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.hqwc.cn/news/318999.html

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

相关文章

设计模式_结构型模式_装饰器模式

装饰器模式和代理模式很像。 代理模式是已经知道代理谁了&#xff0c;所以只是对委托类的访问权限进行限制&#xff0c;因此用户只需要访问相应的代理类就可以。装饰器模式并不知道要装饰谁&#xff0c;所以需要传入具体的被装饰对象进行功能的添加 目的&#xff1a; 增加现有…

Scikit-Learn线性回归(四)

Scikit-Learn线性回归四:梯度下降 1、梯度下降1.1、梯度下降概述1.2、梯度下降及原理1.3、梯度下降的实现2、梯度下降法求解线性回归的最优解2.1、梯度下降法求解的原理2.2、梯度下降法求解线性回归的最优解2.3、梯度下降法求解线性回归案例(波士顿房价预测)3、Scikit-Learn…

Go Lang Fiber介绍

利用GoLang Fiber进行高性能Web开发 在不断发展的Web开发世界中&#xff0c;选择合适的框架至关重要。速度、简洁性和强大的功能集是每个开发者都追求的品质。在使用Go构建Web应用时&#xff0c;“Fiber”作为一个强大且轻量级的框架在众多选择中脱颖而出。在这份全面的指南中…

【Electron】快速建立Vue3+Vite+Electron Ts项目

git https://github.com/electron-vite/electron-vite-vue 创建项目 npm create electron-vite or pnpm create electron-vite 初始化 pnpm install or pnpm i 启动项目 pnpm dev 打包项目 pnpm build 项目创建成功后默认情况下 窗口是H800 W600 在createWindow 函数…

网络安全—模拟ARP欺骗

文章目录 网络拓扑安装使用编辑数据包客户机攻击机验证 仅做实验用途&#xff0c;禁止做违法犯罪的事情&#xff0c;后果自负。当然现在的计算机多无法被欺骗了&#xff0c;开了防火墙ARP欺骗根本无效。 网络拓扑 均使用Windows Server 2003系统 相关配置可以点击观看这篇文章…

Spring Cloud Bus 相关面试题及答案(2024)

1、什么是 Spring Cloud Bus&#xff1f; Spring Cloud Bus 是建立在 Spring Cloud 的基础之上&#xff0c;用于处理微服务架构中各服务实例间消息通信的框架。它与 Spring Cloud Config 结合使用时&#xff0c;可以提供一种动态刷新配置的能力&#xff0c;不需要重启服务实例…

CGAL的无限制的Delaunay图

本章描述了构建L∞距离下线段Delaunay图的算法和几何特征。这些特征还包括绘制L∞距离下线段Delaunay图对偶&#xff08;即L∞距离下线段Voronoi图&#xff09;边缘的方法。L∞算法和特征依赖于欧几里得&#xff08;或L2&#xff09;距离下的线段Delaunay图算法和特征。L∞度量…

STM32CubeMX RS485接口使用

一、基本知识 TTL&#xff08;Transistor-Transistor Logic&#xff09;&#xff1a; 电平范围&#xff1a; 逻辑1对应于2.4V–5V&#xff0c;逻辑0对应于0V–0.5V。通信特点&#xff1a; 全双工。特点&#xff1a; 常见于单片机和微控制器的IO电平&#xff0c;USB转TTL模块通常…

Python 编写代码的工具-交互式环境

交互式环境意思就是我们输入一行代码&#xff0c;按回车&#xff0c;代码就会立马执行并产生结果和显示在窗口中。 要打开Python交互式环境&#xff0c;具体操作如下&#xff08;win系统&#xff09;&#xff1a; 键盘WINR&#xff0c;再输入指令“cmd”,就可以打开命令提示符…

AIGC时代-GPT-4和DALL·E 3的结合

在当今这个快速发展的数字时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为了我们生活中不可或缺的一部分。从简单的自动化任务到复杂的决策制定&#xff0c;AI的应用范围日益扩大。而在这个广阔的领域中&#xff0c;有两个特别引人注目的名字&#xff1a;GPT-4和D…

NVMe SSD IO压力导致宕机案例解读-3

最后找到问题的根因&#xff1a; NVME硬盘&#xff08;mdts参数为10&#xff09;的max_hw_sectors_kb设置为4096KB。当进行流式DMA映射时。如果单次请求的数据量过大&#xff0c;超过了128KB&#xff0c;导致无法有效利用IOVA优化机制&#xff0c;进而引发了对iova_rbtree_loc…

勒索检测能力升级,亚信安全发布《勒索家族和勒索事件监控报告》

评论员简评 近期(12.08-12.14)共发生勒索事件119起&#xff0c;相较之前呈现持平趋势。 与上周相比&#xff0c;本周仍然流行的勒索家族为lockbit3和8base。在涉及的勒索家族中&#xff0c;活跃程度Top5的勒索家族分别是&#xff1a;lockbit3、siegedsec、dragonforce、8base和…