SpringCloud升级之路2020.0.x版-22.Spring Cloud LoadBalancer核心源码

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
简介: SpringCloud升级之路2020.0.x版-22.Spring Cloud LoadBalancer核心源码
本系列代码地址: https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford

经过上一节的详细分析,我们知道可以通过 LoadBalancerClientFactory 知道默认配置类LoadBalancerClientConfiguration. 并且获取微服务名称可以通过 environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);

LoadBalancerClientFactory

public static final String NAMESPACE = "loadbalancer";
public static final String PROPERTY_NAME = NAMESPACE + ".client.name";
public LoadBalancerClientFactory() {
  super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);
}

查看配置类 LoadBalancerClientConfiguration,我们可以发现这个类主要定义两种 Bean,分别是 ReactorLoadBalancer<ServiceInstance>ServiceInstanceListSupplier

ReactorLoadBalancer 是负载均衡器,主要提供根据服务名称获取服务实例列表并从从中选择的功能。

ReactorLoadBalancer

Mono<Response<T>> choose(Request request);

在默认配置中的实现是:

LoadBalancerClientConfiguration

@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(
    Environment environment,
    LoadBalancerClientFactory loadBalancerClientFactory) {
  //获取微服务名称
  String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
  //创建 RoundRobinLoadBalancer 
  //注意这里注入的是 LazyProvider,这主要因为在注册这个 Bean 的时候相关的 Bean 可能还没有被加载注册,利用 LazyProvider 而不是直接注入所需的 Bean 防止报找不到 Bean 注入的错误。
  return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,
      ServiceInstanceListSupplier.class), name);
}

可以看出,默认配置的 ReactorLoadBalancer 实现是 RoundRobinLoadBalancer。这个负载均衡器实现很简单,有一个原子类型的 AtomicInteger position,从 ServiceInstanceListSupplier 中读取所有的服务实例列表,然后对于 position 原子加1,对列表大小取模,返回列表中这个位置的服务实例 ServiceInstance

RoundRobinLoadBalancer

public Mono<Response<ServiceInstance>> choose(Request request) {
    //注入的时候注入的是 Lazy Provider,这里取出真正的 Bean,也就是 ServiceInstanceListSupplier
  ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
      .getIfAvailable(NoopServiceInstanceListSupplier::new);
      //获取实例列表
  return supplier.get(request)
          .next()
          //从列表中选择一个实例
      .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
    List<ServiceInstance> serviceInstances) {
  Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);
  // 如果 ServiceInstanceListSupplier 也实现了 SelectedInstanceCallback,则执行下面的逻辑进行回调。SelectedInstanceCallback 就是每次负载均衡器选择实例之后进行的回调
  if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
    ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
  }
  return serviceInstanceResponse;
}
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
  if (instances.isEmpty()) {
    return new EmptyResponse();
  }
  //postion 原子 +1 并取绝对值
  int pos = Math.abs(this.position.incrementAndGet());
    //返回对应下标的实例
  ServiceInstance instance = instances.get(pos % instances.size());
  return new DefaultResponse(instance);
}

ServiceInstanceListSupplier 是服务列表提供者接口:

ServiceInstanceListSupplier

public interface ServiceInstanceListSupplier extends Supplier<Flux<List<ServiceInstance>>> {
  String getServiceId();
  default Flux<List<ServiceInstance>> get(Request request) {
    return get();
  }
  static ServiceInstanceListSupplierBuilder builder() {
    return new ServiceInstanceListSupplierBuilder();
  }
}

spring-cloud-loadbalancer 中有很多 ServiceInstanceListSupplier 的实现,在默认配置中是通过属性配置指定实现的,这个配置项是spring.cloud.loadbalancer.configurations。例如:

LoadBalancerClientConfiguration

@Bean
@ConditionalOnBean(ReactiveDiscoveryClient.class)
@ConditionalOnMissingBean
//spring.cloud.loadbalancer.configurations 未指定或者为 default
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default",
    matchIfMissing = true)
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
    ConfigurableApplicationContext context) {
  return ServiceInstanceListSupplier.builder()
  //通过 DiscoveryClient 提供实例
  .withDiscoveryClient()
  //开启缓存
  .withCaching()
  .build(context);
}
@Bean
@ConditionalOnBean(ReactiveDiscoveryClient.class)
@ConditionalOnMissingBean
//如果 spring.cloud.loadbalancer.configurations 指定为 zone-preference
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference")
public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
    ConfigurableApplicationContext context) {
  return ServiceInstanceListSupplier.builder()
  //通过 DiscoveryClient 提供实例
  .withDiscoveryClient()
  //启用更倾向于同一个 zone 下实例的特性
  .withZonePreference()
  //开启缓存
  .withCaching()
      .build(context);
}

可以看到,可以通过 ServiceInstanceListSupplier.builder() 生成官方封装好各种特性的 ServiceInstanceListSupplier。其实从底层实现可以看出,所有的 ServiceInstanceListSupplier 实现都是代理模式,例如对于默认配置,底层代码近似于:

return  //开启服务实例缓存
        new CachingServiceInstanceListSupplier(
                        //启用通过 discoveryClient 的服务发现
                        new DiscoveryClientServiceInstanceListSupplier(
                                discoveryClient, env
                        )
                , cacheManagerProvider.getIfAvailable()
        );

除了默认配置 LoadBalancerClientConfiguration,用户配置自定义配置则是通过 @LoadBalancerClients@LoadBalancerClient.这个原理是通过 LoadBalancerClientConfigurationRegistrar 实现的。首先,我们来看一下 LoadBalancerClientFactory 这个 NamedContextFactory 是如何创建的:

[LoadBalancerAutoConfiguration]

private final ObjectProvider<List<LoadBalancerClientSpecification>> configurations;
public LoadBalancerAutoConfiguration(ObjectProvider<List<LoadBalancerClientSpecification>> configurations) {
    //注入 LoadBalancerClientSpecification List 的 provider
    //在 Bean 创建的时候,进行载入,而不是注册的时候
  this.configurations = configurations;
}
@ConditionalOnMissingBean
@Bean
public LoadBalancerClientFactory loadBalancerClientFactory() {
    //创建 LoadBalancerClientFactory
  LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();
    //读取所有的 LoadBalancerClientSpecification,设置为 LoadBalancerClientFactory 的配置
  clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));
  return clientFactory;
}

那么,LoadBalancerClientSpecification 这些 Bean 是怎么创建的呢?在 @LoadBalancerClients@LoadBalancerClient 注解中,都包含 @Import(LoadBalancerClientConfigurationRegistrar.class)。这个 @Import 加载一个 ImportBeanDefinitionRegistrar,这里是 LoadBalancerClientConfigurationRegistrar. ImportBeanDefinitionRegistrar里面的方法参数包含注解元数据,以及注册 Bean 的BeanDefinitionRegistry。一般通过注解元数据,动态通过 BeanDefinitionRegistry 注册 Bean,在这里的实现是:

[LoadBalancerClients]

@Configuration(proxyBeanMethods = false)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
@Import(LoadBalancerClientConfigurationRegistrar.class)
public @interface LoadBalancerClients {
    //可以指定多个 LoadBalancerClient
  LoadBalancerClient[] value() default {};
  //指定所有的负载均衡配置的默认配置
  Class<?>[] defaultConfiguration() default {};
}

[LoadBalancerClient]

@Configuration(proxyBeanMethods = false)
@Import(LoadBalancerClientConfigurationRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoadBalancerClient {
    //name 和 value 都是微服务名称
  @AliasFor("name")
  String value() default "";
  @AliasFor("value")
  String name() default "";
  //这个微服务的配置
  Class<?>[] configuration() default {};
}

[LoadBalancerClientConfigurationRegistrar]

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    //获取 LoadBalancerClients 注解的元数据
  Map<String, Object> attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName(), true);
  if (attrs != null && attrs.containsKey("value")) {
    AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
    //对于 value 属性,其实就是一个 LoadBalancerClient 列表,对于每个生成一个特定微服务名字的  LoadBalancerClientSpecification
    for (AnnotationAttributes client : clients) {
      registerClientConfiguration(registry, getClientName(client), client.get("configuration"));
    }
  }
  //如果指定了 defaultConfiguration,则注册为 default 的配置
  if (attrs != null && attrs.containsKey("defaultConfiguration")) {
    String name;
    if (metadata.hasEnclosingClass()) {
      name = "default." + metadata.getEnclosingClassName();
    }
    else {
      name = "default." + metadata.getClassName();
    }
    registerClientConfiguration(registry, name, attrs.get("defaultConfiguration"));
  }
  //获取 LoadBalancerClient 注解的元数据
  Map<String, Object> client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName(), true);
  String name = getClientName(client);
  if (name != null) {
    registerClientConfiguration(registry, name, client.get("configuration"));
  }
}
private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
    //初始化 LoadBalancerClientSpecification 的 BeanDefinition,用于注册一个 LoadBalancerClientSpecification Bean
  BeanDefinitionBuilder builder = BeanDefinitionBuilder
      .genericBeanDefinition(LoadBalancerClientSpecification.class);
  //构造器参数
  builder.addConstructorArgValue(name);
  builder.addConstructorArgValue(configuration);
  //注册 Bean
  registry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition());
}

从代码中我们可以看出,通过使用 @LoadBalancerClients@LoadBalancerClient 注解可以自动生成对应的 LoadBalancerClientSpecification 进而实现公共负载均衡配置或者特定某个微服务的负载均衡配置。



image.png


我们这一节详细分析 Spring Cloud LoadBalancer 的源代码来理解其中的原理,下一节我们将介绍在我们项目中使用 Spring Cloud LoadBalancer 要实现的功能。

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
96 2
|
2月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
102 5
|
2天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
57 2
|
1月前
|
消息中间件 监控 Java
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
38 6
|
1月前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
149 5
|
1月前
|
Java 关系型数据库 MySQL
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
62 5
|
1月前
|
缓存 监控 Java
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
44 5
|
2月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
73 9
|
3月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
199 5