为何一个@LoadBalanced注解就让RestTemplate拥有负载均衡的能力?【享学Spring Cloud】(中)

简介: 为何一个@LoadBalanced注解就让RestTemplate拥有负载均衡的能力?【享学Spring Cloud】(中)

LoadBalancerInterceptor


该类唯一被使用的地方就是LoadBalancerAutoConfiguration里配置上去~


public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
  // 这个命名都不叫Client了,而叫loadBalancer~~~
  private LoadBalancerClient loadBalancer;
  // 用于构建出一个Request
  private LoadBalancerRequestFactory requestFactory;
  ... // 省略构造函数(给这两个属性赋值)
  @Override
  public 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));
  }
}


此拦截器拦截请求后把它的serviceName委托给了LoadBalancerClient去执行,根据ServiceName可能对应N多个实际的Server,因此就可以从众多的Server中运用均衡算法,挑选出一个最为合适的Server做最终的请求(它持有真正的请求执行器ClientHttpRequestExecution)。


LoadBalancerClient


请求被拦截后,最终都是委托给了LoadBalancerClient处理。


// 由使用负载平衡器选择要向其发送请求的服务器的类实现
public interface ServiceInstanceChooser {
  // 从负载平衡器中为指定的服务选择Service服务实例。
  // 也就是根据调用者传入的serviceId,负载均衡的选择出一个具体的实例出来
  ServiceInstance choose(String serviceId);
}
// 它自己定义了三个方法
public interface LoadBalancerClient extends ServiceInstanceChooser {
  // 执行请求
  <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
  <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
  // 重新构造url:把url中原来写的服务名 换掉 换成实际的
  URI reconstructURI(ServiceInstance instance, URI original);
}


它只有一个实现类RibbonLoadBalancerClient(ServiceInstanceChooser是有多个实现类的~)。


RibbonLoadBalancerClient


首先我们应当关注它的choose()方法:


public class RibbonLoadBalancerClient implements LoadBalancerClient {
  @Override
  public ServiceInstance choose(String serviceId) {
    return choose(serviceId, null);
  }
  // hint:你可以理解成分组。若指定了,只会在这个偏好的分组里面去均衡选择
  // 得到一个Server后,使用RibbonServer把server适配起来~~~
  // 这样一个实例就选好了~~~真正请求会落在这个实例上~
  public ServiceInstance choose(String serviceId, Object hint) {
    Server server = getServer(getLoadBalancer(serviceId), hint);
    if (server == null) {
      return null;
    }
    return new RibbonServer(serviceId, server, isSecure(server, serviceId),
        serverIntrospector(serviceId).getMetadata(server));
  }
  // 根据ServiceId去找到一个属于它的负载均衡器
  protected ILoadBalancer getLoadBalancer(String serviceId) {
    return this.clientFactory.getLoadBalancer(serviceId);
  }
}


choose方法:传入serviceId,然后通过SpringClientFactory获取负载均衡器com.netflix.loadbalancer.ILoadBalancer,最终委托给它的chooseServer()方法选取到一个com.netflix.loadbalancer.Server实例,也就是说真正完成Server选取的是ILoadBalancer。


ILoadBalancer以及它相关的类是一个较为庞大的体系,本文不做更多的展开,而是只聚焦在我们的流程上


LoadBalancerInterceptor执行的时候是直接委托执行的loadBalancer.execute()这个方法:


RibbonLoadBalancerClient:
  // hint此处传值为null:一视同仁
  // 说明:LoadBalancerRequest是通过LoadBalancerRequestFactory.createRequest(request, body, execution)创建出来的
  // 它实现LoadBalancerRequest接口是用的一个匿名内部类,泛型类型是ClientHttpResponse
  // 因为最终执行的显然还是执行器:ClientHttpRequestExecution.execute()
  @Override
  public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
    return execute(serviceId, request, null);
  }
  // public方法(非接口方法)
  public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
    // 同上:拿到负载均衡器,然后拿到一个serverInstance实例
    ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
    Server server = getServer(loadBalancer, hint);
    if (server == null) { // 若没找到就直接抛出异常。这里使用的是IllegalStateException这个异常
      throw new IllegalStateException("No instances available for " + serviceId);
    }
    // 把Server适配为RibbonServer  isSecure:客户端是否安全
    // serverIntrospector内省  参考配置文件:ServerIntrospectorProperties
    RibbonServer ribbonServer = new RibbonServer(serviceId, server,
        isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));
    //调用本类的重载接口方法~~~~~
    return execute(serviceId, ribbonServer, request);
  }
  // 接口方法:它的参数是ServiceInstance --> 已经确定了唯一的Server实例~~~
  @Override
  public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
    // 拿到Server)(说白了,RibbonServer是execute时的唯一实现)
    Server server = null;
    if (serviceInstance instanceof RibbonServer) {
      server = ((RibbonServer) serviceInstance).getServer();
    }
    if (server == null) {
      throw new IllegalStateException("No instances available for " + serviceId);
    }
    // 说明:执行的上下文是和serviceId绑定的
    RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
    ... 
    // 真正的向server发送请求,得到返回值
    // 因为有拦截器,所以这里肯定说执行的是InterceptingRequestExecution.execute()方法
    // so会调用ServiceRequestWrapper.getURI(),从而就会调用reconstructURI()方法
      T returnVal = request.apply(serviceInstance);
      return returnVal;
    ... // 异常处理
  }


returnVal是一个ClientHttpResponse,最后交给handleResponse()方法来处理异常情况(若存在的话),若无异常就交给提取器提值:responseExtractor.extractData(response),这样整个请求就算全部完成了。



相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
4天前
|
Java 开发者 Spring
深入理解Spring Boot的@ComponentScan注解
【4月更文挑战第22天】在构建 Spring Boot 应用时,@ComponentScan 是一个不可或缺的工具,它使得组件发现变得自动化和高效。这篇博客将详细介绍 @ComponentScan 的基本概念、关键属性及其在实际开发中的应用。
21 4
|
6天前
|
Java 开发者 Spring
Spring Framework 中的 @Autowired 注解:概念与使用方法
【4月更文挑战第20天】在Spring Framework中,@Autowired 注解是实现依赖注入(Dependency Injection, DI)的一种非常强大的工具。通过使用 @Autowired,开发者可以减少代码中的引用绑定,提高模块间的解耦能力
28 6
|
2天前
|
监控 Java 微服务
第八章 Spring Cloud 之 Hystrix
第八章 Spring Cloud 之 Hystrix
|
2天前
|
监控 Java API
第七章 Spring Cloud 之 GateWay
第七章 Spring Cloud 之 GateWay
|
2天前
|
消息中间件 Java Nacos
第三章 Spring Cloud简介
第三章 Spring Cloud简介
|
2天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
2天前
|
Dubbo Java 应用服务中间件
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
|
9天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
9天前
|
Java API 对象存储
对象存储OSS产品常见问题之使用Spring Cloud Alibaba情况下文档添加水印如何解决
对象存储OSS是基于互联网的数据存储服务模式,让用户可以安全、可靠地存储大量非结构化数据,如图片、音频、视频、文档等任意类型文件,并通过简单的基于HTTP/HTTPS协议的RESTful API接口进行访问和管理。本帖梳理了用户在实际使用中可能遇到的各种常见问题,涵盖了基础操作、性能优化、安全设置、费用管理、数据备份与恢复、跨区域同步、API接口调用等多个方面。
25 2
|
13天前
|
XML Java 数据格式
进阶注解探秘:深入Spring高级注解的精髓与实际运用
进阶注解探秘:深入Spring高级注解的精髓与实际运用
26 2