本项目代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford

我们使用 Spring Cloud 官方推荐的 Spring Cloud LoadBalancer 作为我们的客户端负载均衡器。

Spring Cloud LoadBalancer是一个客户端负载均衡器,类似于Ribbon,但是由于Ribbon已经进入维护模式,并且Ribbon 2并不与Ribbon 1相互兼容,所以Spring Cloud全家桶在Spring Cloud Commons项目中,添加了Spring cloud Loadbalancer作为新的负载均衡器,并且做了向前兼容,就算你的项目中继续用 Spring Cloud Netflix 套装(包括Ribbon,Eureka,Zuul,Hystrix等等)让你的项目中有这些依赖,你也可以通过简单的配置,把ribbon替换成Spring Cloud LoadBalancer。

Spring Cloud 中内部微服务调用默认是 http 请求,主要通过下面三种 API:

  • RestTemplate:同步 http API
  • WebClient:异步响应式 http API
  • 三方客户端封装,例如 openfeign

如果项目中加入了 spring-cloud-loadbalancer 的依赖并且配置启用了,那么会自动在相关的 Bean 中加入负载均衡器的特性。

  • 对于 RestTemplate,会自动对所有 @LoadBalanced 注解修饰的 RestTemplate Bean 增加 Interceptor 从而加上了负载均衡器的特性。
  • 对于 WebClient,会自动创建 ReactorLoadBalancerExchangeFilterFunction,我们可以通过加入ReactorLoadBalancerExchangeFilterFunction会加入负载均衡器的特性。
  • 对于三方客户端,一般不需要我们额外配置什么。

这些使用的示例,会在我们系列升级完最后的测试部分看到。

上一节我们提到了 NamedContextFactory,Spring Cloud LoadBalancer 这里也是使用了这个机制实现了不同微服务使用不同的 Spring Cloud LoadBalancer 配置。相关核心实现是 @LoadBalancerClient@LoadBalancerClients 这两个注解,以及 NamedContextFactory.Specification 的实现 LoadBalancerClientSpecificationNamedContextFactory 的实现 LoadBalancerClientFactory

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

LoadBalancerClientFactory

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

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

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

ReactorLoadBalancer

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

在默认配置中的实现是:

LoadBalancerClientConfiguration

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

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

RoundRobinLoadBalancer

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

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

ServiceInstanceListSupplier

  1. public interface ServiceInstanceListSupplier extends Supplier<Flux<List<ServiceInstance>>> {
  2. String getServiceId();
  3. default Flux<List<ServiceInstance>> get(Request request) {
  4. return get();
  5. }
  6. static ServiceInstanceListSupplierBuilder builder() {
  7. return new ServiceInstanceListSupplierBuilder();
  8. }
  9. }

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

LoadBalancerClientConfiguration

  1. @Bean
  2. @ConditionalOnBean(ReactiveDiscoveryClient.class)
  3. @ConditionalOnMissingBean
  4. //spring.cloud.loadbalancer.configurations 未指定或者为 default
  5. @ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default",
  6. matchIfMissing = true)
  7. public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
  8. ConfigurableApplicationContext context) {
  9. return ServiceInstanceListSupplier.builder()
  10. //通过 DiscoveryClient 提供实例
  11. .withDiscoveryClient()
  12. //开启缓存
  13. .withCaching()
  14. .build(context);
  15. }
  16. @Bean
  17. @ConditionalOnBean(ReactiveDiscoveryClient.class)
  18. @ConditionalOnMissingBean
  19. //如果 spring.cloud.loadbalancer.configurations 指定为 zone-preference
  20. @ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference")
  21. public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
  22. ConfigurableApplicationContext context) {
  23. return ServiceInstanceListSupplier.builder()
  24. //通过 DiscoveryClient 提供实例
  25. .withDiscoveryClient()
  26. //启用更倾向于同一个 zone 下实例的特性
  27. .withZonePreference()
  28. //开启缓存
  29. .withCaching()
  30. .build(context);
  31. }

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

  1. return //开启服务实例缓存
  2. new CachingServiceInstanceListSupplier(
  3. //启用通过 discoveryClient 的服务发现
  4. new DiscoveryClientServiceInstanceListSupplier(
  5. discoveryClient, env
  6. )
  7. , cacheManagerProvider.getIfAvailable()
  8. );

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

[LoadBalancerAutoConfiguration]

  1. private final ObjectProvider<List<LoadBalancerClientSpecification>> configurations;
  2. public LoadBalancerAutoConfiguration(ObjectProvider<List<LoadBalancerClientSpecification>> configurations) {
  3. //注入 LoadBalancerClientSpecification List 的 provider
  4. //在 Bean 创建的时候,进行载入,而不是注册的时候
  5. this.configurations = configurations;
  6. }
  7. @ConditionalOnMissingBean
  8. @Bean
  9. public LoadBalancerClientFactory loadBalancerClientFactory() {
  10. //创建 LoadBalancerClientFactory
  11. LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();
  12. //读取所有的 LoadBalancerClientSpecification,设置为 LoadBalancerClientFactory 的配置
  13. clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));
  14. return clientFactory;
  15. }

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

[LoadBalancerClients]

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

[LoadBalancerClient]

  1. @Configuration(proxyBeanMethods = false)
  2. @Import(LoadBalancerClientConfigurationRegistrar.class)
  3. @Target(ElementType.TYPE)
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Documented
  6. public @interface LoadBalancerClient {
  7. //name 和 value 都是微服务名称
  8. @AliasFor("name")
  9. String value() default "";
  10. @AliasFor("value")
  11. String name() default "";
  12. //这个微服务的配置
  13. Class<?>[] configuration() default {};
  14. }

[LoadBalancerClientConfigurationRegistrar]

  1. @Override
  2. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  3. //获取 LoadBalancerClients 注解的元数据
  4. Map<String, Object> attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName(), true);
  5. if (attrs != null && attrs.containsKey("value")) {
  6. AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
  7. //对于 value 属性,其实就是一个 LoadBalancerClient 列表,对于每个生成一个特定微服务名字的 LoadBalancerClientSpecification
  8. for (AnnotationAttributes client : clients) {
  9. registerClientConfiguration(registry, getClientName(client), client.get("configuration"));
  10. }
  11. }
  12. //如果指定了 defaultConfiguration,则注册为 default 的配置
  13. if (attrs != null && attrs.containsKey("defaultConfiguration")) {
  14. String name;
  15. if (metadata.hasEnclosingClass()) {
  16. name = "default." + metadata.getEnclosingClassName();
  17. }
  18. else {
  19. name = "default." + metadata.getClassName();
  20. }
  21. registerClientConfiguration(registry, name, attrs.get("defaultConfiguration"));
  22. }
  23. //获取 LoadBalancerClient 注解的元数据
  24. Map<String, Object> client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName(), true);
  25. String name = getClientName(client);
  26. if (name != null) {
  27. registerClientConfiguration(registry, name, client.get("configuration"));
  28. }
  29. }
  30. private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
  31. //初始化 LoadBalancerClientSpecification 的 BeanDefinition,用于注册一个 LoadBalancerClientSpecification Bean
  32. BeanDefinitionBuilder builder = BeanDefinitionBuilder
  33. .genericBeanDefinition(LoadBalancerClientSpecification.class);
  34. //构造器参数
  35. builder.addConstructorArgValue(name);
  36. builder.addConstructorArgValue(configuration);
  37. //注册 Bean
  38. registry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition());
  39. }

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

微信搜索“我的编程喵”关注公众号,加作者微信,每日一刷,轻松提升技术,斩获各种offer

image

版权声明:本文为zhxdick原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zhxdick/p/14823256.html