springcloud-loadbalancer-02

921 阅读11分钟

至此我想读者应该掌握了NameContextFactory实现客户端隔离的原理了,现在我们再看下目前已经介绍的图:

png2.png

上述表红圈的就是目前已经介绍的内容,接下来我们看下它的默认配置。

LoadBalancerClientFactory的构造器中,传入了LoadBalancerClientConfiguration类型的默认配置。

public LoadBalancerClientFactory(LoadBalancerClientsProperties properties) {
   super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);
   this.properties = properties;
}

我们看下该类配置了个啥

@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {

   private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;

   @Bean
   @ConditionalOnMissingBean
   //默认的负载均衡策略,默认是轮训,我们可以自定义来覆盖默认的负载策略
   public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
         LoadBalancerClientFactory loadBalancerClientFactory) {
      //该值会在NamedContextFactory.createContext放入到environment中
      String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
      return new RoundRobinLoadBalancer(
            loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
   }
  
   //普通 web 环境下的配置
   @Configuration(proxyBeanMethods = false)
   @ConditionalOnBlockingDiscoveryEnabled
   @Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER + 1)
   public static class BlockingSupportConfiguration {

      @Bean
      @ConditionalOnBean(DiscoveryClient.class)
      @ConditionalOnMissingBean
      @Conditional(DefaultConfigurationCondition.class)
      //服务发现、缓存缓存
      public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
         return ServiceInstanceListSupplier.builder()
               //通过 DiscoveryClient 提供实例
               //其中会从AnnotationConfigApplicationContext中获取    DiscoveryClient
               //CompositeDiscoveryClient。
               .withBlockingDiscoveryClient()
               .withCaching()
               .build(context);
      }

      @Bean
      @ConditionalOnBean(DiscoveryClient.class)
      @ConditionalOnMissingBean
      @Conditional(ZonePreferenceConfigurationCondition.class)
      //区域优先配置
      public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
         return ServiceInstanceListSupplier.builder()
               .withBlockingDiscoveryClient()
               .withZonePreference()
               .withCaching().build(context);
      }

      @Bean
      @ConditionalOnBean({ DiscoveryClient.class, RestTemplate.class })
      @ConditionalOnMissingBean
      @Conditional(HealthCheckConfigurationCondition.class)
      //健康检查配置
      public ServiceInstanceListSupplier healthCheckDiscoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
         return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withBlockingHealthChecks()
               .build(context);
      }
   }
  
  
  //普通 web 环境下的配置的重试策略
   public static class BlockingRetryConfiguration {
		 ....

   }
  //普通  Reactive 环境下的配置的重试策略
   public static class ReactiveRetryConfiguration {
			....
   }

 

}

我们删除了大部分的配置,主要介绍下以上2个部分。

  • 创建了默认的负载策略RoundRobinLoadBalancer
  • 普通 web 环境下的配置

先来看下RoundRobinLoadBalancer的创建过程。

1、从environment获取name

2、RoundRobinLoadBalancer

/**
 * @param serviceInstanceListSupplierProvider a provider of
 * {@link ServiceInstanceListSupplier} that will be used to get available instances
 * @param serviceId id of the service for which to choose an instance
 */
public RoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
      String serviceId) {
   this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));
}

以上是RoundRobinLoadBalancer的构造器,其中注释中阐述了,ServiceInstanceListSupplier将会被用来获取可靠的实例。目前我们暂时任务它可以从注册中心获取存活的实例就行了。接下来完整的看下它的源码:

public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {

   private static final Log log = LogFactory.getLog(RoundRobinLoadBalancer.class);

   final AtomicInteger position;

   final String serviceId;

   ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

   /**
    * @param serviceInstanceListSupplierProvider a provider of
    * {@link ServiceInstanceListSupplier} that will be used to get available instances
    * @param serviceId id of the service for which to choose an instance
    */
   public RoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
         String serviceId) {
      this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));
   }

   /**
    * @param serviceInstanceListSupplierProvider a provider of
    * {@link ServiceInstanceListSupplier} that will be used to get available instances
    * @param serviceId id of the service for which to choose an instance
    * @param seedPosition Round Robin element position marker
    */
   public RoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
         String serviceId, int seedPosition) {
      this.serviceId = serviceId;
      this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
      this.position = new AtomicInteger(seedPosition);
   }

   @SuppressWarnings("rawtypes")
   @Override
   // see original
   // https://github.com/Netflix/ocelli/blob/master/ocelli-core/
   // src/main/java/netflix/ocelli/loadbalancer/RoundRobinLoadBalancer.java
   public Mono<Response<ServiceInstance>> choose(Request request) {
      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) {
      //经过轮训后得到包含最终选择的ServiceInstance响应
      Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);
      if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
         ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
      }
      return serviceInstanceResponse;
   }

   //轮训负载均衡
   private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
      if (instances.isEmpty()) {
         if (log.isWarnEnabled()) {
            log.warn("No servers available for service: " + serviceId);
         }
         return new EmptyResponse();
      }
      // TODO: enforce order?
      int pos = Math.abs(this.position.incrementAndGet());

      ServiceInstance instance = instances.get(pos % instances.size());

      return new DefaultResponse(instance);
   }

}

👉从choose方法开始(目前先不管它的调用时序,后续会介绍完整的远程调用时序图)

  • ServiceInstanceListSupplierget方法获取所有的实例,并对每一个实例调用processInstanceResponse方法

  • getInstanceResponse方法执行轮训策略,我们可以看出,每次调用它增加了position的值,并通过ServiceInstance instance = instances.get(pos % instances.size());轮训实例。当然这么做是非常简单的,它没有为每个实例添加权重,这样的缺点就导致性能差的服务压力将会加大。

当然官方还提供了随机负载均衡RandomLoadBalancer,实现原理也是类似。

那么读者此时可能会问,如何切换负载均衡策略呢!其实在官方文档中已经给出了,这里就照猫画虎的介绍一下。

1、自定义配置类

public class CustomLoadBalancerConfiguration {
   @Bean
   public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
         LoadBalancerClientFactory loadBalancerClientFactory) {
      String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
      return new RandomLoadBalancer(
            loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
   }
}

2、将配置按照客户端进行隔离

@LoadBalancerClient(name = "HELLO-SERVICE", configuration = CustomLoadBalancerConfiguration.class)
public class MyConfiguration {
}

以上我们实现了以客户端名称为HELLO-SERVICE命名的配置类。

此时读者可能有疑惑,为什么这么配置就会生效呢,后面在介绍@LoadBalancerClient注解,当前只是展示如何用。

默认的负载均衡我们介绍完了,接下来看下LoadBalancerClientConfiguration提供的一系列ServiceInstanceListSuppier

它主要包括响应式和阻塞式,我们主要看下阻塞式。主要包括以下几个ServiceInstanceListSuppier

  • 缓存
  • 区域优先配置
  • 健康检查配置
  • 重试策略配置

👉先看下缓存

@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean
@Conditional(DefaultConfigurationCondition.class)
//服务发现、缓存缓存
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
      ConfigurableApplicationContext context) {
   return ServiceInstanceListSupplier.builder()
         //通过 DiscoveryClient 提供实例
         //其中会从AnnotationConfigApplicationContext中获取    DiscoveryClient
         //CompositeDiscoveryClient。
         .withBlockingDiscoveryClient()
         .withCaching()
         .build(context);
}

ServiceInstanceListSupplier.builder()会创建ServiceInstanceListSupplierBuilder,我们看下它的withBlockingDiscoveryClient方法

public ServiceInstanceListSupplierBuilder withBlockingDiscoveryClient() {
   if (baseCreator != null && LOG.isWarnEnabled()) {
      LOG.warn("Overriding a previously set baseCreator with a blocking DiscoveryClient baseCreator.");
   }
   this.baseCreator = context -> {
      // 获取DiscoveryClient
      DiscoveryClient discoveryClient = context.getBean(DiscoveryClient.class);

      return new DiscoveryClientServiceInstanceListSupplier(discoveryClient, context.getEnvironment());
   };
   return this;
}

其实就干了2件事:1、从容器中获取DiscoveryClient;2、创建DiscoveryClientServiceInstanceListSupplier

先看下DiscoveryClient接口

/**
 * Represents read operations commonly available to discovery services such as Netflix
 * Eureka or consul.io.
 *
 * @author Spencer Gibb
 * @author Olga Maciaszek-Sharma
 * @author Chris Bono
 */
public interface DiscoveryClient extends Ordered {
  /**
	 * Gets all ServiceInstances associated with a particular serviceId.
	 * @param serviceId The serviceId to query.
	 * @return A List of ServiceInstance.
	 */
  List<ServiceInstance> getInstances(String serviceId);
  /**
	 * @return All known service IDs.
	 */
	List<String> getServices();
  ....
}

注释描述的很明显了,表示Netflix等发现服务通常可用的读取操作。比如Eureka的EurekaDiscoveryClient

其中springcloud提供了CompositeDiscoveryClient

/**
 * A {@link DiscoveryClient} that is composed of other discovery clients and delegates
 * calls to each of them in order.
 *
 * @author Biju Kunjummen
 * @author Olga Maciaszek-Sharma
 */
//获取服务实例
public class CompositeDiscoveryClient implements DiscoveryClient {

	private final List<DiscoveryClient> discoveryClients;

	public CompositeDiscoveryClient(List<DiscoveryClient> discoveryClients) {
		AnnotationAwareOrderComparator.sort(discoveryClients);
		this.discoveryClients = discoveryClients;
	}

	@Override
	public String description() {
		return "Composite Discovery Client";
	}

	@Override
	public List<ServiceInstance> getInstances(String serviceId) {
		if (this.discoveryClients != null) {
			//在discoveryClients中获取实例
			for (DiscoveryClient discoveryClient : this.discoveryClients) {
				List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
				if (instances != null && !instances.isEmpty()) {
					return instances;
				}
			}
		}
		return Collections.emptyList();
	}

	@Override
	public List<String> getServices() {
		LinkedHashSet<String> services = new LinkedHashSet<>();
		if (this.discoveryClients != null) {
			for (DiscoveryClient discoveryClient : this.discoveryClients) {
				List<String> serviceForClient = discoveryClient.getServices();
				if (serviceForClient != null) {
					services.addAll(serviceForClient);
				}
			}
		}
		return new ArrayList<>(services);
	}

	public List<DiscoveryClient> getDiscoveryClients() {
		return this.discoveryClients;
	}

}

同样的由注释我们看出,它是由其他客户端委托办事的一个DiscoveryClient,从它的构造器中可以看出,在创建它的时候,会传入一系列的负载均衡客户端,CompositeDiscoveryClientCompositeDiscoveryClientAutoConfiguration中被创建

@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(SimpleDiscoveryClientAutoConfiguration.class)
public class CompositeDiscoveryClientAutoConfiguration {

   @Bean
   @Primary
   public CompositeDiscoveryClient compositeDiscoveryClient(List<DiscoveryClient> discoveryClients) {
      return new CompositeDiscoveryClient(discoveryClients);
   }

}

此次他会收集我们环境中所有的DiscoveryClient,并传入到CompositeDiscoveryClient中。

ServiceInstanceListSupplierBuilder.withBlockingDiscoveryClient方法在调用DiscoveryClient discoveryClient = context.getBean(DiscoveryClient.class);时,会拿到CompositeDiscoveryClient

接着我们看下ServiceInstanceListSupplierBuilder.withBlockingDiscoveryClient方法创建的DiscoveryClientServiceInstanceListSupplier中做了哪些事。

public DiscoveryClientServiceInstanceListSupplier(DiscoveryClient delegate, Environment environment) {
   this.serviceId = environment.getProperty(PROPERTY_NAME);
   resolveTimeout(environment);
   this.serviceInstances = Flux.defer(() -> Mono.fromCallable(() -> delegate.getInstances(serviceId)))
         .timeout(timeout, Flux.defer(() -> {
            logTimeout();
            return Flux.just(new ArrayList<>());
         }), Schedulers.boundedElastic()).onErrorResume(error -> {
            logException(error);
            return Flux.just(new ArrayList<>());
         });
}

这是Reactor的API,有兴趣的可以看下API说明。不懂也没关系,该构造器中先执行了delegate.getInstances(serviceId)进而获取Flux<List<ServiceInstance>> serviceInstances。其实执行的是CompositeDiscoveryClient.getInstances(String serviceId),代码如下:

@Override
public List<ServiceInstance> getInstances(String serviceId) {
   if (this.discoveryClients != null) {
      //在discoveryClients中获取实例
      for (DiscoveryClient discoveryClient : this.discoveryClients) {
         List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
         if (instances != null && !instances.isEmpty()) {
            return instances;
         }
      }
   }
   return Collections.emptyList();
}

它通过遍历所有的DiscoveryClient查找对应serviceIdList<ServiceInstance> instances,找到直接返回,否则返回空的。

我们回到LoadBalancerClientConfiguration.BlockingSupportConfiguration.discoveryClientServiceInstanceListSupplier(ConfigurableApplicationContext context)中来。看下ServiceInstanceListSupplierBuilder.withCaching()方法。

/**
 * If {@link LoadBalancerCacheManager} is available in the context, wraps created
 * {@link ServiceInstanceListSupplier} hierarchy with a
 * {@link CachingServiceInstanceListSupplier} instance to provide a caching mechanism
 * for service instances. Uses {@link ObjectProvider} to lazily resolve
 * {@link LoadBalancerCacheManager}.
 * @return the {@link ServiceInstanceListSupplierBuilder} object
 */
public ServiceInstanceListSupplierBuilder withCaching() {
   if (cachingCreator != null && LOG.isWarnEnabled()) {
      LOG.warn(
            "Overriding a previously set cachingCreator with a CachingServiceInstanceListSupplier-based cachingCreator.");
   }
   this.cachingCreator = (context, delegate) -> {
      ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
            .getBeanProvider(LoadBalancerCacheManager.class);
      if (cacheManagerProvider.getIfAvailable() != null) {
         return new CachingServiceInstanceListSupplier(delegate, cacheManagerProvider.getIfAvailable());
      }
      if (LOG.isWarnEnabled()) {
         LOG.warn("LoadBalancerCacheManager not available, returning delegate without caching.");
      }
      return delegate;
   };
   return this;
}

从官方描述中可以看出,如果Spring容器中存在LoadBalancerCacheManager,则将创建的ServiceInstanceListSupplier包装为CachingServiceInstanceListSupplier实例,以提供服务实例的缓存机制。简单来说就是使用容器中的LoadBalancerCacheManager包装到CachingServiceInstanceListSupplier中提供缓存实现。

cacheManager.png 默认的是使用DefaultLoadBalancerCacheManager,如果存在Caffeine则使用CaffeineBasedLoadBalancerCacheManager

接下来看下CachingServiceInstanceListSupplier的构造器

//从缓存cacheManager中取出serviceInstances,key为服务id。cacheManager一般为CaffeineCacheManager
public CachingServiceInstanceListSupplier(ServiceInstanceListSupplier delegate, CacheManager cacheManager) {
   super(delegate);
   //CacheFlux用法参考https://projectreactor.io/docs/extra/release/api/
   this.serviceInstances = CacheFlux.lookup(key -> {
      // TODO: configurable cache name
      //从cacheManager中取出缓存实例名称对应的Cache
      Cache cache = cacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
      if (cache == null) {
         if (log.isErrorEnabled()) {
            log.error("Unable to find cache: " + SERVICE_INSTANCE_CACHE_NAME);
         }
         return Mono.empty();
      }
      //获取缓存中key的所有实例
      List<ServiceInstance> list = cache.get(key, List.class);
      if (list == null || list.isEmpty()) {
         return Mono.empty();
      }
      //返回缓存中对应key的所有实例
      return Flux.just(list).materialize().collectList();
   }, delegate.getServiceId())
         //如果没有命中缓存。则将Flux中的List<ServiceInstance>取出
         .onCacheMissResume(delegate.get().take(1))
         .andWriteWith((key, signals) -> Flux.fromIterable(signals).dematerialize().doOnNext(instances -> {
            Cache cache = cacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
            if (cache == null) {
               if (log.isErrorEnabled()) {
                  log.error("Unable to find cache for writing: " + SERVICE_INSTANCE_CACHE_NAME);
               }
            }
            else {
               //将key对应的instances写入缓存
               cache.put(key, instances);
            }
         }).then());
}

同样的CacheFlux的用法参考API,不看也没关系,它先取出缓存Cache cache = cacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);其中SERVICE_INSTANCE_CACHE_NAME描述如下:

/**
 * Name of the service cache instance.
 */
public static final String SERVICE_INSTANCE_CACHE_NAME = CachingServiceInstanceListSupplier.class.getSimpleName()
      + "Cache";

并从缓存中获取缓存中key的所有实例List<ServiceInstance> list = cache.get(key, List.class);

如果缓存没有命中,则将Flux中的List<ServiceInstance>取出,并将key对应的instances写入缓存。

最后看下ServiceInstanceListSupplierBuilder.build()方法

/**
 * Builds the {@link ServiceInstanceListSupplier} hierarchy.
 * @param context application context
 * @return a {@link ServiceInstanceListSupplier} instance on top of the delegate
 * hierarchy
 */
public ServiceInstanceListSupplier build(ConfigurableApplicationContext context) {
   Assert.notNull(baseCreator, "A baseCreator must not be null");

   ServiceInstanceListSupplier supplier = baseCreator.apply(context);

   for (DelegateCreator creator : creators) {
      supplier = creator.apply(context, supplier);
   }

   if (this.cachingCreator != null) {
      supplier = this.cachingCreator.apply(context, supplier);
   }
   return supplier;
}

建造一个ServiceInstanceListSupplier。此时建造supplier就是DiscoveryClientServiceInstanceListSupplier,并遍历有List<DelegateCreator> creators,传入DiscoveryClientServiceInstanceListSupplierConfigurableApplicationContext,此处的ConfigurableApplicationContext其实就是之前提到的AnnotationConfigApplicationContext

至此LoadBalancerClientConfiguration.BlockingSupportConfiguration.discoveryClientServiceInstanceListSupplier就说完了,它先读取实例,并缓存。

👉在说下区域优先配置LoadBalancerClientConfiguration.BlockingSupportConfiguration.zonePreferenceDiscoveryClientServiceInstanceListSupplier

@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean
@Conditional(ZonePreferenceConfigurationCondition.class)
//区域优先配置
public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
      ConfigurableApplicationContext context) {
   return ServiceInstanceListSupplier.builder()
         .withBlockingDiscoveryClient()
         .withZonePreference()
         .withCaching().build(context);
}

现在直接从看withZonePreference()开始看吧

public ServiceInstanceListSupplierBuilder withZonePreference() {
   DelegateCreator creator = (context, delegate) -> {
      LoadBalancerZoneConfig zoneConfig = context.getBean(LoadBalancerZoneConfig.class);
      return new ZonePreferenceServiceInstanceListSupplier(delegate, zoneConfig);
   };
   this.creators.add(creator);
   return this;
}

其中LoadBalancerZoneConfig如下:

/**
 * @author Olga Maciaszek-Sharma
 */
public class LoadBalancerZoneConfig {

   /**
    * A {@link String} representation of the <code>zone</code> used for filtering
    * instances by zoned load-balancing implementations.
    */
   //标识当前负载均衡器处于哪一个 zone
   private String zone;
	 ....
}

在来看ZonePreferenceServiceInstanceListSupplier

public ZonePreferenceServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
      LoadBalancerZoneConfig zoneConfig) {
   super(delegate);
   this.zoneConfig = zoneConfig;
}

其中ZonePreferenceServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier

DelegatingServiceInstanceListSupplier的作用一般是过滤服务实例的。ZonePreferenceServiceInstanceListSupplier通过继承DelegatingServiceInstanceListSupplier并实现了Flux<List<ServiceInstance>> get(Request request) 方法来过滤实例。

public class ZonePreferenceServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier {

   private final String ZONE = "zone";

   private final LoadBalancerZoneConfig zoneConfig;

   private String zone;
   ...

   @Override
   // delegate.get 后进行 filteredByZone 过滤
   public Flux<List<ServiceInstance>> get() {
      return getDelegate().get().map(this::filteredByZone);
   }

   //这里对于没指定zone或者该zone下没有存活实例的情况下,会返回所有查到的实例,不区分zone
   private List<ServiceInstance> filteredByZone(List<ServiceInstance> serviceInstances) {
      if (zone == null) {
         zone = zoneConfig.getZone();
      }
      //如果zone不为null,并且该zone下有存活实例,则返回这个实例列表
      //否则,返回所有的实例
      if (zone != null) {
         List<ServiceInstance> filteredInstances = new ArrayList<>();
         for (ServiceInstance serviceInstance : serviceInstances) {
            //从Service元数据中获取instanceZone
            String instanceZone = getZone(serviceInstance);
            if (zone.equalsIgnoreCase(instanceZone)) {
               filteredInstances.add(serviceInstance);
            }
         }
         if (filteredInstances.size() > 0) {
            return filteredInstances;
         }
      }
      // If the zone is not set or there are no zone-specific instances available,
      // we return all instances retrieved for given service id.
      return serviceInstances;
   }

   private String getZone(ServiceInstance serviceInstance) {
      Map<String, String> metadata = serviceInstance.getMetadata();
      if (metadata != null) {
         return metadata.get(ZONE);
      }
      return null;
   }

}

先获取zone其实就是上边提到的HELLO-SERVICE的前缀,比如在eureka中是这样配置的eureka.client.serviceUrl.defaultZone,此时defaultZone就是默认的zone,你可以配置不同的zone来实现区域优先效果。将同zone一样的实例添加到filteredInstances并返回。如果没有zone没有设置或者没有指定zone可用的实例,则返回全部的服务实例。

回到ServiceInstanceListSupplierBuilder.withZonePreference方法,它在最后执行了this.creators.add(creator);。这是为了最后执行build方法时用到的,片段代码如下:

for (DelegateCreator creator : creators) {
   supplier = creator.apply(context, supplier);
}

其实就是函数编程,利用apply传入参数而已。

至此区域优先我们也说完了。

👉我们在来看下健康检查的相关源码ServiceInstanceListSupplierBuilder.withBlockingHealthChecks()

/**
 * Adds a {@link HealthCheckServiceInstanceListSupplier} that uses user-provided
 * {@link RestTemplate} instance to the {@link ServiceInstanceListSupplier} hierarchy.
 * @return the {@link ServiceInstanceListSupplierBuilder} object
 */
public ServiceInstanceListSupplierBuilder withBlockingHealthChecks() {
   DelegateCreator creator = (context, delegate) -> {
      RestTemplate restTemplate = context.getBean(RestTemplate.class);
      LoadBalancerClientFactory loadBalancerClientFactory = context.getBean(LoadBalancerClientFactory.class);
      return blockingHealthCheckServiceInstanceListSupplier(restTemplate, delegate, loadBalancerClientFactory);
   };
   this.creators.add(creator);
   return this;
}

它先获取了RestTemplate用来执行远程调用,其次还获取了LoadBalancerClientFactory,从上面的分析中我们了解了LoadBalancerClientFactory中包含了负载均衡配置以及属性配置,最后创建了blockingHealthCheckServiceInstanceListSupplier

private ServiceInstanceListSupplier blockingHealthCheckServiceInstanceListSupplier(RestTemplate restTemplate,
      ServiceInstanceListSupplier delegate,
      ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory) {
   return new HealthCheckServiceInstanceListSupplier(delegate, loadBalancerClientFactory,
         (serviceInstance, healthCheckPath) -> Mono.defer(() -> {
            URI uri = UriComponentsBuilder.fromUriString(getUri(serviceInstance, healthCheckPath)).build()
                  .toUri();
            try {
               return Mono
                     .just(HttpStatus.OK.equals(restTemplate.getForEntity(uri, Void.class).getStatusCode()));
            }
            catch (Exception ignored) {
               return Mono.just(false);
            }
         }));
}

创建了new HealthCheckServiceInstanceListSupplier。主要看下第三个参数,它先利用serviceInstancehealthCheckPath构建了URI,并利用HttpStatus.OK.equals(restTemplate.getForEntity(uri, Void.class).getStatusCode())进行远程调用,查看状体码是否时Ok,当然这该方法会在后面的isAlive中执行,后面再说。接下来看下HealthCheckServiceInstanceListSupplier

public HealthCheckServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
      ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory,
      BiFunction<ServiceInstance, String, Mono<Boolean>> aliveFunction) {
   super(delegate);
   this.healthCheck = loadBalancerClientFactory.getProperties(getServiceId()).getHealthCheck();
   defaultHealthCheckPath = healthCheck.getPath().getOrDefault("default", "/actuator/health");
   this.aliveFunction = aliveFunction;
   Repeat<Object> aliveInstancesReplayRepeat = Repeat
         .onlyIf(repeatContext -> this.healthCheck.getRefetchInstances())
         .fixedBackoff(healthCheck.getRefetchInstancesInterval());
   Flux<List<ServiceInstance>> aliveInstancesFlux = Flux.defer(delegate).repeatWhen(aliveInstancesReplayRepeat)
         //健康检查
         .switchMap(serviceInstances -> healthCheckFlux(serviceInstances)
               .map(alive -> Collections.unmodifiableList(new ArrayList<>(alive))));
   aliveInstancesReplay = aliveInstancesFlux.delaySubscription(healthCheck.getInitialDelay()).replay(1)
         .refCount(1);
}

该构造器中,获取了健康检查的配置(前面提到过在LoadBalancerProperties有配置),主要关注下healthCheckFlux(serviceInstances)方法

//健康检查
protected Flux<List<ServiceInstance>> healthCheckFlux(List<ServiceInstance> instances) {
   Repeat<Object> healthCheckFluxRepeat = Repeat.onlyIf(repeatContext -> healthCheck.getRepeatHealthCheck())
         .fixedBackoff(healthCheck.getInterval());
   return Flux.defer(() -> {
      List<Mono<ServiceInstance>> checks = new ArrayList<>(instances.size());
      for (ServiceInstance instance : instances) {
         //检查每个ServiceInstance是否存活
         Mono<ServiceInstance> alive = isAlive(instance).onErrorResume(error -> {
            if (LOG.isDebugEnabled()) {
               LOG.debug(String.format(
                     "Exception occurred during health check of the instance for service %s: %s",
                     instance.getServiceId(), instance.getUri()), error);
            }
            return Mono.empty();
         }).timeout(healthCheck.getInterval(), Mono.defer(() -> {
            if (LOG.isDebugEnabled()) {
               LOG.debug(String.format(
                     "The instance for service %s: %s did not respond for %s during health check",
                     instance.getServiceId(), instance.getUri(), healthCheck.getInterval()));
            }
            return Mono.empty();
         })).handle((isHealthy, sink) -> {
            if (isHealthy) {
               sink.next(instance);
            }
         });

         checks.add(alive);
      }
      List<ServiceInstance> result = new ArrayList<>();
      return Flux.merge(checks).map(alive -> {
         result.add(alive);
         return result;
      }).defaultIfEmpty(result);
   }).repeatWhen(healthCheckFluxRepeat);
}

方法中遍历了所有实例,并调用isAlive(instance)远程调用,查看是否存活。

//检查serviceInstance是否存活
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
   boolean containsService = healthCheck.getPath().containsKey(serviceInstance.getServiceId());
   String healthCheckPropertyValue = healthCheck.getPath().get(serviceInstance.getServiceId());
   if (containsService && !StringUtils.hasText(healthCheckPropertyValue)) {
      return Mono.just(true);
   }
   String healthCheckPath = healthCheckPropertyValue != null ? healthCheckPropertyValue : defaultHealthCheckPath;
   return aliveFunction.apply(updatedServiceInstance(serviceInstance), healthCheckPath);
}

其中aliveFunction.apply(updatedServiceInstance(serviceInstance), healthCheckPath)其实就是调用了上面提到的

 (serviceInstance, healthCheckPath) -> Mono.defer(() -> {
            URI uri = UriComponentsBuilder.fromUriString(getUri(serviceInstance, healthCheckPath)).build()
                  .toUri();
            try {
               return Mono
                     .just(HttpStatus.OK.equals(restTemplate.getForEntity(uri, Void.class).getStatusCode()));
            }
            catch (Exception ignored) {
               return Mono.just(false);
            }
         }));

我们看下updatedServiceInstance(serviceInstance)

private ServiceInstance updatedServiceInstance(ServiceInstance serviceInstance) {
   Integer healthCheckPort = healthCheck.getPort();
   if (serviceInstance instanceof DefaultServiceInstance && healthCheckPort != null) {
      return new DefaultServiceInstance(serviceInstance.getInstanceId(), serviceInstance.getServiceId(),
            serviceInstance.getHost(), healthCheckPort, serviceInstance.isSecure(),
            serviceInstance.getMetadata());
   }
   return serviceInstance;
}

该方法创建了DefaultServiceInstancealiveFunction使用

我们回到HealthCheckServiceInstanceListSupplier.healthCheckFlux方法中来,它在最后将存活的实例放到result中。

至此我们分析完了健康检查。

😇是不是看的又点累了,其实还有重试retryAwareDiscoveryClientServiceInstanceListSupplier,它创建了RetryAwareServiceInstanceListSupplier,RetryAwareServiceInstanceListSupplierget方法中调用了filteredByPreviousInstance来删除先前重试的实例。具体重试我们在后边的RetryLoadBalancerInterceptor拦截器中给大家介绍。😇

🤓目前为止我们看下都介绍了哪些东西,梳理一下。

png3.png 一下子干掉了好多啊!后边还有呢!😀