wangguangwu
wangguangwu
发布于 2024-08-20 / 37 阅读
0
0

Spring Cloud LoadBalancer 源码分析

参考文献

1. 负载均衡器概述

负载均衡器通常分为两类:服务端负载均衡(如网关层负载均衡)和客户端负载均衡。

  • 服务端负载均衡: 例如软件负载均衡器 LVS,或者 nginx 等网关层负载均衡。它们在服务端处理请求的分发和调度。
  • 客户端负载均衡: 例如 Spring Cloud LoadBalancer,作为客户端负载均衡器,它负责在客户端进行服务发现和维护服务实例列表,并支持自定义的负载均衡策略(如随机、轮询等)。

2. Spring Cloud LoadBalancer 介绍

2.1 简介

Spring Cloud LoadBalancer 是 Spring Cloud 官方提供的客户端负载均衡器,它为应用程序提供了负载均衡的抽象层和具体实现。为了支持客户端的负载均衡机制,Spring Cloud 引入了 ReactiveLoadBalancer 接口,并提供了基于轮询(Round Robin)和随机(Random)策略的默认实现。

Spring Cloud LoadBalancer 的核心是通过 ServiceInstanceListSupplier 来管理和获取服务实例列表。这个接口的实现通常依赖于类路径中的服务发现客户端,从服务注册中心中检索可用的服务实例。

2.2 核心思想

Spring Cloud LoadBalancer 的核心思想是通过在客户端添加拦截器来实现负载均衡。在每次请求发出之前,拦截器会根据设定的负载均衡策略对请求进行拦截和处理,然后选择一个合适的服务实例来处理该请求。

这种实现方式具有以下特点:

  • 客户端自主负载均衡:负载均衡逻辑完全由客户端执行,服务端无需感知负载均衡的存在。这种方式可以减轻服务端的压力,同时允许客户端根据自身的需求和策略灵活地选择目标服务实例。
  • 可插拔的策略支持:Spring Cloud LoadBalancer 提供了多种内置的负载均衡策略(如轮询、随机等),并允许开发者根据具体的业务需求自定义负载均衡策略。这使得系统可以灵活适应不同的场景和负载模式。
  • 集成与扩展性:通过拦截器的方式,Spring Cloud LoadBalancer 可以轻松集成到现有的 Spring 应用中,并且可以通过配置或编程方式轻松扩展负载均衡功能。

这种思想使得 Spring Cloud LoadBalancer 能够以轻量级、灵活且高效的方式实现客户端负载均衡,适用于各种微服务架构的应用场景。

3. 关键模块

  • org.springframework.cloud.loadbalancer.core:包含核心负载均衡逻辑,提供了多种内置的负载均衡策略,如 RoundRobinLoadBalancer、RandomLoadBalancer。
  • org.springframework.cloud.loadbalancer.annotation:处理与注解相关的内容,如 @LoadBalancerClient 的解析和应用。
  • org.springframework.cloud.loadbalancer.support:提供辅助类和工具类,以支持核心功能的实现。
  • org.springframework.cloud.loadbalancer.config:包含 LoadBalancer 的自动配置类,帮助自动化配置负载均衡器。

4. 关键类

  • ReactorServiceInstanceLoadBalancer:负载均衡器的核心接口,定义了选择服务实例的基本方法。
  • RoundRobinLoadBalancerRandomLoadBalancer:实现了 ReactorServiceInstanceLoadBalancer 的内置负载均衡策略,其中轮询策略是默认策略。
  • ServiceInstanceListSupplier:用于提供当前可用的服务实例列表。
  • LoadBalancerClient:提供了负载均衡的客户端接口,通常在 RestTemplate 和 WebClient 中使用。

5. 服务调用链路

以下是启动服务并调用 NacosConsumerDemo01Application 中 ApiController 的 hello() 方法来访问服务提供方的完整调用链路:

  1. com.wangguangwu.nacosconsumerdemo.controller.ApiController#hello()

    • 控制器方法 hello(),通过调用 RestTemplategetForObject 方法发起 HTTP 请求。
  2. org.springframework.web.client.RestTemplate#getForObject()

    • RestTemplategetForObject 方法用于发送 GET 请求,并将响应体映射为对象。
  3. org.springframework.web.client.RestTemplate#execute()

    • execute()RestTemplate 执行 HTTP 请求的核心方法,它会调用一系列的钩子方法来处理请求和响应。
  4. org.springframework.web.client.RestTemplate#doExecute()

    • doExecute()execute() 方法内部的实现细节,用于实际执行 HTTP 请求。
  5. org.springframework.http.client.AbstractClientHttpRequest#execute()

    • 这是一个抽象类的方法,负责实际的请求执行。子类会实现该方法以处理具体的 HTTP 请求。
  6. org.springframework.http.client.AbstractClientHttpRequest#executeInternal()

    • executeInternal() 方法用于处理请求的内部逻辑,通常包括对请求头的设置、连接管理等。
  7. org.springframework.http.client.InterceptingClientHttpRequest#executeInternal()

    • RestTemplate 配置了拦截器(如 LoadBalancerInterceptor)时,会使用 InterceptingClientHttpRequest 来执行请求,该类负责在请求执行前后调用拦截器。
  8. org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution#execute()

    • execute() 方法会调用配置的拦截器链,并最终执行 HTTP 请求。
  9. org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept()

    • LoadBalancerInterceptor 是一个拦截器,用于在请求执行前根据负载均衡策略选择合适的服务实例。
  10. org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient#execute()

    • 该方法负责执行带有负载均衡的请求,会选择一个服务实例,并将请求路由到该实例。
  11. org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient#choose()

    • choose() 方法根据负载均衡策略从服务实例列表中选择一个实例。
  12. org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory#getInstance()

    • 这个方法用于获取与指定服务名称相关的负载均衡器实例。
  13. org.springframework.cloud.context.named.NamedContextFactory#getInstance()

    • 这是 LoadBalancerClientFactory 的底层实现,负责创建和管理负载均衡器的实例上下文。
  14. org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#choose()

    • RoundRobinLoadBalancer 实现了默认的轮询策略,choose() 方法是其核心方法,用于从一组服务实例中按顺序选择一个实例。当达到实例列表的末尾时,轮询策略会重新从列表的开头开始。

总结

这个调用链展示了一个 HTTP 请求在 Spring Cloud 应用中经过 RestTemplate 处理,并通过 Spring Cloud LoadBalancer 实现客户端负载均衡的全过程。从控制器方法 hello() 开始,经过一系列的拦截器和工厂方法,最终由 BlockingLoadBalancerClient 执行负载均衡请求。Spring Cloud 通过这种机制,确保了每个请求被合理地分配到适当的服务实例上,实现了服务的高可用性和负载分散。

6. 核心组件详解

6.1 LoadBalancerInterceptor

LoadBalancerInterceptor 实现了 ClientHttpRequestInterceptor 接口,用于在 RestTemplate 请求发出前应用负载均衡策略。

类定义:

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

}

属性:

// 负责执行负载均衡逻辑的客户端接口,用于选择服务实例并执行请求。
private LoadBalancerClient loadBalancer;

// 用于创建 LoadBalancerRequest 的工厂类,封装了请求执行的逻辑。
private LoadBalancerRequestFactory requestFactory;

核心方法:

// 拦截 HTTP 请求,应用负载均衡策略。
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
                                    final ClientHttpRequestExecution execution) throws IOException {
    // 获取原始请求的 URI(用于从请求中提取服务名称)。
    final URI originalUri = request.getURI();

    // 从 URI 中提取服务名称(主机名),用于负载均衡查找。
    String serviceName = originalUri.getHost();

    // 确保服务名称不为空,否则抛出异常。
    Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);

    // 使用 LoadBalancerClient 执行负载均衡请求。
    // execute 方法将根据负载均衡策略选择一个服务实例,并通过 LoadBalancerRequestFactory 创建并执行请求。
    return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}

作用:

LoadBalancerInterceptor 是 Spring Cloud 中的一个关键拦截器,它在请求发出之前拦截 HTTP 请求,并为这些请求应用负载均衡逻辑。

重要性:

  • 请求拦截: LoadBalancerInterceptor 是整个负载均衡流程的起点,所有通过 RestTemplate 发出的请求都会先经过此拦截器。
  • 触发负载均衡: 通过 LoadBalancerInterceptor,请求被引导至 BlockingLoadBalancerClient,从而应用相应的负载均衡策略来选择服务实例。

6.2 LoadBalancerClient

LoadBalancerClient 是一个客户端负载均衡器接口,继承了 ServiceInstanceChooser 接口,用于在客户端执行负载均衡操作。

接口定义:

public interface LoadBalancerClient extends ServiceInstanceChooser {

}

核心方法:

// 根据 serviceId 选择合适的服务实例并执行请求。
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

// 使用指定的服务实例直接执行请求。
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

说明

  • execute(String serviceId, LoadBalancerRequest<T> request): 根据给定的 serviceId 执行负载均衡操作,选择一个合适的服务实例并执行请求。
  • execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request): 直接使用指定的服务实例执行请求,不经过负载均衡的实例选择过程。

6.3 BlockingLoadBalancerClient

BlockingLoadBalancerClient 是 LoadBalancerClient 的默认实现,用于在客户端执行带有负载均衡的请求。

类定义:

public class BlockingLoadBalancerClient implements LoadBalancerClient {

}

属性:

// 用于创建和管理服务实例负载均衡器的工厂。
private final ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory;

核心方法:

// 根据服务ID和负载均衡请求执行带有负载均衡的请求。
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
    // 获取服务ID的提示信息
    String hint = getHint(serviceId);

    // 创建LoadBalancerRequest的适配器,封装请求上下文
    LoadBalancerRequestAdapter<T, DefaultRequestContext> lbRequest = new LoadBalancerRequestAdapter<>(request,
          new DefaultRequestContext(request, hint));

    // 获取支持的负载均衡生命周期处理器
    Set<LoadBalancerLifecycle> supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId);
    supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));

    // 根据负载均衡策略选择服务实例
    ServiceInstance serviceInstance = choose(serviceId, lbRequest);
    if (serviceInstance == null) {
       supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
             new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, new EmptyResponse())));
       throw new IllegalStateException("No instances available for " + serviceId);
    }

    // 执行请求,并将请求路由到选定的服务实例
    return execute(serviceId, serviceInstance, lbRequest);
}

// 使用指定的服务实例执行请求,绕过负载均衡选择
@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request)
       throws IOException {
    DefaultResponse defaultResponse = new DefaultResponse(serviceInstance);
    Set<LoadBalancerLifecycle> supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId);
    Request lbRequest = request instanceof Request ? (Request) request : new DefaultRequest<>();

    // 处理请求的开始
    supportedLifecycleProcessors
          .forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, new DefaultResponse(serviceInstance)));
    try {
       T response = request.apply(serviceInstance);
       Object clientResponse = getClientResponse(response);

       // 处理请求的完成
       supportedLifecycleProcessors
             .forEach(lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
                   lbRequest, defaultResponse, clientResponse)));
       return response;
    }
    catch (IOException iOException) {
       supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
             new CompletionContext<>(CompletionContext.Status.FAILED, iOException, lbRequest, defaultResponse)));
       throw iOException;
    }
    catch (Exception exception) {
       supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
             new CompletionContext<>(CompletionContext.Status.FAILED, exception, lbRequest, defaultResponse)));
       ReflectionUtils.rethrowRuntimeException(exception);
    }
    return null;
}

作用:

BlockingLoadBalancerClient 是 Spring Cloud 中执行带有负载均衡的请求的核心组件。它在拦截器的指导下,负责根据负载均衡策略选择合适的服务实例,并将请求路由到该实例。

重要性:

  • 执行负载均衡逻辑: 它通过调用 choose() 方法来决定哪个服务实例将处理当前请求。
  • 请求路由: 选择实例后,BlockingLoadBalancerClient 会将请求转发到选定的服务实例,实现请求的负载分配。

6.4 LoadBalancerClientFactory

LoadBalancerClientFactory 是一个工厂类,专门用于创建和管理负载均衡器实例,以支持不同服务的负载均衡策略。

类定义:

public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification>
       implements ReactiveLoadBalancer.Factory<ServiceInstance> {

}

属性:

// 存储负载均衡器客户端的配置信息。
private final LoadBalancerClientsProperties properties;

核心方法:

// 根据服务名称获取指定类型的负载均衡器实例。
public <T> T getInstance(String name, Class<T> type) {
    AnnotationConfigApplicationContext context = getContext(name);
    try {
       return context.getBean(type);
    }
    catch (NoSuchBeanDefinitionException e) {
       // 如果未找到对应的Bean,忽略异常并返回null
    }
    return null;
}

作用:

LoadBalancerClientFactory 负责为指定服务名称提供合适的 ReactorServiceInstanceLoadBalancer 实例,这些实例实现了具体的负载均衡策略,如轮询、随机等。

重要性:

  • 管理负载均衡策略: 它负责管理服务名与负载均衡策略之间的映射,确保每个服务都可以有独立的负载均衡策略。
  • 实例提供: 它是 BlockingLoadBalancerClient 依赖的关键组件,为其提供了所需的负载均衡器实例,使得负载均衡策略能够被正确应用。

6.5 总结

在 Spring Cloud 的请求处理链路中,客户端负载均衡机制的实现依赖于多个关键组件的协同工作:

  • LoadBalancerInterceptor:负责拦截 HTTP 请求并启动负载均衡流程,是整个负载均衡机制的起点。
  • BlockingLoadBalancerClient:核心组件,执行负载均衡逻辑并将请求路由到选定的服务实例。
  • LoadBalancerClientFactory:工厂类,负责为特定服务提供合适的负载均衡器实例,确保负载均衡策略能够正确应用。

这些组件共同构建了 Spring Cloud 的客户端负载均衡体系,确保请求能够根据预设的策略灵活而可靠地分配到最适合的服务实例上,实现高效的请求处理和服务分发。

7. 获取 serviceId 对应的实例信息

在 Spring Cloud LoadBalancer 中,通过 serviceId 获取对应的服务实例列表并选择一个合适的实例,是实现负载均衡的核心操作。

以下代码片段参考了 org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer,展示了默认轮询策略的实现过程。

类定义:

public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {

}

核心属性:

// 用于提供服务实例列表的供应者
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

核心方法:

public Mono<Response<ServiceInstance>> choose(Request request) {
    // 获取一个 ServiceInstanceListSupplier,提供当前可用的服务实例列表。
    ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
          .getIfAvailable(NoopServiceInstanceListSupplier::new);

    // 获取服务实例列表,并处理选择逻辑,返回一个包含选择结果的 Response 对象。
    return supplier.get(request).next()
          .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}

说明

ServiceInstanceListSupplier supplier:

  • 这是一个用于提供服务实例列表的供应者 (Supplier)。它从 serviceInstanceListSupplierProvider 中获取一个可用的 ServiceInstanceListSupplier 实例。如果没有可用的实例,默认会使用 NoopServiceInstanceListSupplier 作为回退。

supplier.get(request).next():

  • 通过 supplier.get(request) 获取一个 Flux 流,其中包含了当前请求 (Request) 的所有可用服务实例列表。next() 方法将返回这个流中的下一个元素,也就是服务实例列表。

.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)):

  • 使用 map 操作符将获取到的服务实例列表传递给 processInstanceResponse 方法进行处理,返回一个 Response<ServiceInstance> 对象。这个对象包含了经过负载均衡策略选择的服务实例。

作用

该方法的主要作用是根据 serviceId 获取对应的服务实例列表,并基于负载均衡策略选择一个合适的服务实例。最终,choose 方法返回一个包含选择结果的 Mono>,用于后续的请求处理。

8. 总结

在本文中,我们深入探讨了 Spring Cloud LoadBalancer 的工作原理和核心组件。Spring Cloud LoadBalancer 作为客户端负载均衡解决方案,通过在客户端添加拦截器来实现服务实例的动态选择和负载分配。其主要优点包括灵活性、扩展性和对不同负载均衡策略的支持。

关键要点

  • 客户端负载均衡:Spring Cloud LoadBalancer 通过客户端执行负载均衡,减轻了服务端压力,并允许更灵活的负载均衡策略应用。
  • 核心组件:LoadBalancerInterceptor、BlockingLoadBalancerClient 和 LoadBalancerClientFactory 是实现客户端负载均衡的关键组件,它们共同协作确保请求被合理分配到最适合的服务实例。
  • 策略支持:Spring Cloud LoadBalancer 提供了多种内置策略,并允许用户根据需求进行自定义,满足不同的应用场景。

通过这些机制,Spring Cloud LoadBalancer 实现了高效的请求分发和服务稳定性,适用于微服务架构中需要高可用性和负载分散的系统。


评论