至此我想读者应该掌握了NameContextFactory实现客户端隔离的原理了,现在我们再看下目前已经介绍的图:
上述表红圈的就是目前已经介绍的内容,接下来我们看下它的默认配置。
在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方法开始(目前先不管它的调用时序,后续会介绍完整的远程调用时序图)
-
先
ServiceInstanceListSupplier的get方法获取所有的实例,并对每一个实例调用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,从它的构造器中可以看出,在创建它的时候,会传入一系列的负载均衡客户端,CompositeDiscoveryClient在CompositeDiscoveryClientAutoConfiguration中被创建
@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查找对应serviceId的List<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中提供缓存实现。
默认的是使用
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,传入DiscoveryClientServiceInstanceListSupplier和ConfigurableApplicationContext,此处的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。主要看下第三个参数,它先利用serviceInstance和healthCheckPath构建了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;
}
该方法创建了DefaultServiceInstance给aliveFunction使用
我们回到HealthCheckServiceInstanceListSupplier.healthCheckFlux方法中来,它在最后将存活的实例放到result中。
至此我们分析完了健康检查。
😇是不是看的又点累了,其实还有重试retryAwareDiscoveryClientServiceInstanceListSupplier,它创建了RetryAwareServiceInstanceListSupplier,RetryAwareServiceInstanceListSupplier的get方法中调用了filteredByPreviousInstance来删除先前重试的实例。具体重试我们在后边的RetryLoadBalancerInterceptor拦截器中给大家介绍。😇
🤓目前为止我们看下都介绍了哪些东西,梳理一下。
一下子干掉了好多啊!后边还有呢!😀