【SpringCloud负载均衡】【源码+图解】【三】LoadBalancer的工作原理
目录
- 4. 负载均衡
-
- 4.1 提供者DiscoveryClient
- 4.2 过滤器Supplier
4. 负载均衡
loadBalancer采用了webFlux响应式编程,只有在真正choose的时候才会去拉取数据,所以它的顺序是一层一层往下拉取真正的数据,然后再一层一层向上筛选过滤数据实现负载均衡,接下来我们分析下这些Supplier
4.1 提供者DiscoveryClient
对于DiscoveryClientServiceInstanceListSupplier它不会单独使用,而是作为其他Supplier的delegate,也就是兜底用的,所以它的作用至关重要。先看下它的构造函数
public class DiscoveryClientServiceInstanceListSupplier implements ServiceInstanceListSupplier {
private Duration timeout = Duration.ofSeconds(30);
private final String serviceId;
private final Flux<List<ServiceInstance>> serviceInstances;
// delegate默认是DiscoveryClient的实现类CompositeDiscoveryClient,见后文的分析
public DiscoveryClientServiceInstanceListSupplier(DiscoveryClient delegate, Environment environment) {
this.serviceId = environment.getProperty(PROPERTY_NAME);
// timeout为配置中的spring.cloud.loadbalancer.service-discovery.timeout
resolveTimeout(environment);
// 这里采用了响应式编程,这里仅仅是定义,当调用get()函数获取的时候才会真正的拿数据
this.serviceInstances = Flux.defer(
// 调用delegate,即CompositeDiscoveryClient获取实例信息
() -> 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<>());
});
}
}
DiscoveryClientServiceInstanceListSupplier最重要的是DiscoveryClient,它才是真正获取ServiceInstance的类,先看下DiscoveryClient的类图
DiscoveryClient提供了两个方法,最重要的是getInstances,默认的实现是CompositeDiscoveryClient,它的成员参数discoveryClients默认下是EurekaDiscoveryClient和SimpleDiscoveryClient,除了这两个也可以自定义。接下来我们着重分下三个client的配置和getInstances方法
4.1.1 CompositeDiscoveryClient
public class CompositeDiscoveryClientAutoConfiguration {
@Bean
// 声明默认情况使用CompositeDiscoveryClient
@Primary
public CompositeDiscoveryClient compositeDiscoveryClient(List<DiscoveryClient> discoveryClients) {
return new CompositeDiscoveryClient(discoveryClients);
}
}
public class CompositeDiscoveryClient implements DiscoveryClient {
@Override
public List<ServiceInstance> getInstances(String serviceId) {
if (this.discoveryClients != null) {
// 默认情况下EurekaDiscoveryClient,SimpleDiscoveryClient
// 如果要使用SimpleDiscoveryClient,可以使用以下两种方式:
// 第一eureka.client.enabled为false,这样就不会注入EurekaDiscoveryClient类
// 第二eureka.client.fetch-registry为false,这样就不会获取registry信息,那么EurekaDiscoveryClient拿到的值为空
for (DiscoveryClient discoveryClient : this.discoveryClients) {
List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
if (instances != null && !instances.isEmpty()) {
// 一旦获得ServiceInstance就立即返回,后面的client就废弃了
return instances;
}
}
}
return Collections.emptyList();
}
}
4.1.2 EurekaDiscoveryClient
// eureka.client.enabled为true才开启,默认下为true
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
public class EurekaDiscoveryClientConfiguration {
@Bean
@ConditionalOnMissingBean
public EurekaDiscoveryClient discoveryClient(EurekaClient client, EurekaClientConfig clientConfig) {
return new EurekaDiscoveryClient(client, clientConfig);
}
}
public class EurekaDiscoveryClient implements DiscoveryClient {
// eurekaClient即Eureka中的DiscoveryClient类
private final EurekaClient eurekaClient;
// eureka.client.*的配置
private final EurekaClientConfig clientConfig;
@Override
public List<ServiceInstance> getInstances(String serviceId) {
// 调用DiscoveryClient.getInstancesByVipAddress,这里拿到的instance即DiscoveryClient中refreshRegistry方法定时更新的实例列表
List<InstanceInfo> infos = this.eurekaClient.getInstancesByVipAddress(serviceId, false);
List<ServiceInstance> instances = new ArrayList<>();
for (InstanceInfo info : infos) {
// 封装成EurekaServiceInstance
instances.add(new EurekaServiceInstance(info));
}
return instances;
}
}
4.1.3 SimpleDiscoveryClient
public class SimpleDiscoveryClientAutoConfiguration implements ApplicationListener<WebServerInitializedEvent> {
// spring.cloud.discovery.client.simple.*
private SimpleDiscoveryProperties simple = new SimpleDiscoveryProperties();
@Bean
@Order
public DiscoveryClient simpleDiscoveryClient(SimpleDiscoveryProperties properties) {
return new SimpleDiscoveryClient(properties);
}
}
public class SimpleDiscoveryClient implements DiscoveryClient {
@Override
public List<ServiceInstance> getInstances(String serviceId) {
List<ServiceInstance> serviceInstances = new ArrayList<>();
// 这里返回的instances为yml文件中的spring.cloud.discovery.client.simple.instances的配置
List<DefaultServiceInstance> serviceInstanceForService = this.simpleDiscoveryProperties.getInstances()
.get(serviceId);
if (serviceInstanceForService != null) {
serviceInstances.addAll(serviceInstanceForService);
}
return serviceInstances;
}
}
看下spring.cloud.discovery.client.simple
的配置例子
spring:
application:
name: user
cloud:
discovery:
client:
simple:
instances:
product:
- instance-id: localhost:product:5001
service-id: product
host: localhost
port: 5001
secure: false
uri:
host: localhost
port: 5001
metadata:
my: my
- instance-id: localhost:product:5002
service-id: product
host: localhost
port: 5002
secure: false
uri:
host: localhost
port: 5002
metadata:
my: my
4.1.4 自定义DiscoveryClient
// 1、注入Spring容器
@Component
// 2、实现DiscoveryClient
public class MyDiscoveryClient implements DiscoveryClient{
@Override
public String description() {
return null;
}
@Override
public List<ServiceInstance> getInstances(String serviceId) {
return null;
}
@Override
public List<String> getServices() {
return null;
}
}
4.2 过滤器Supplier
先看下ServiceInstanceListSupplier类图
顶层接口为Supplier,唯一的方法为get(),它的作用就是提供服务,loadBalance中提供List<ServiceInstance>
,接下来我们逐一分析每一个Supplier的使用
4.2.1 CachingServiceInstanceListSupplier
yml配置
spring:
cloud:
loadbalancer:
configurations: default # 默认值,即默认使用CachingServiceInstanceListSupplier
cache:
ttl: 35 # 缓存过期时间
capacity: 256 # 缓存最大容量
caffeine:
spec: initialCapacity=500,expireAfterWrite=5s # CaffeineBasedLoadBalancerCacheManager的相关配置
Configuration配置
public static class BlockingSupportConfiguration {
@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean
// DefaultConfigurationCondition即spring.cloud.loadbalancer.configurations = default,默认值
@Conditional(DefaultConfigurationCondition.class)
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder() // 返回ServiceInstanceListSupplierBuilder
.withBlockingDiscoveryClient() // A DiscoveryClientServiceInstanceListSupplier
.withCaching() // B CachingServiceInstanceListSupplier
.build(context); // C
}
}
创建
public final class ServiceInstanceListSupplierBuilder {
private Creator baseCreator;
private DelegateCreator cachingCreator;
public ServiceInstanceListSupplierBuilder withBlockingDiscoveryClient() {
this.baseCreator = context -> {
// A
// 注意DiscoveryClient是接口,不是eureka那个DiscoveryClient类
DiscoveryClient discoveryClient = context.getBean(DiscoveryClient.class);
// 创建DiscoveryClientServiceInstanceListSupplier,真正提供实例的Supplier
return new DiscoveryClientServiceInstanceListSupplier(discoveryClient, context.getEnvironment());
};
return this;
}
public ServiceInstanceListSupplierBuilder withCaching() {
this.cachingCreator = (context, delegate) -> {
// B, 注意这里用到的缓存管理器为LoadBalancerCacheManager,后面会详细分析
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
.getBeanProvider(LoadBalancerCacheManager.class);
if (cacheManagerProvider.getIfAvailable() != null) {
// 创建CachingServiceInstanceListSupplier
return new CachingServiceInstanceListSupplier(delegate, cacheManagerProvider.getIfAvailable());
}
return delegate;
};
return this;
}
// C
public ServiceInstanceListSupplier build(ConfigurableApplicationContext context) {
// apply方法即调用A处的代码获的DiscoveryClientServiceInstanceListSupplier
ServiceInstanceListSupplier supplier = baseCreator.apply(context);
......
if (this.cachingCreator != null) {
// apply方法即调用B处的代码,将DiscoveryClientServiceInstanceListSupplier作为delegate
// 创建CachingServiceInstanceListSupplier
supplier = this.cachingCreator.apply(context, supplier);
}
return supplier;
}
}
使用
public class CachingServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier {
private final Flux<List<ServiceInstance>> serviceInstances;
@SuppressWarnings("unchecked")
public CachingServiceInstanceListSupplier(ServiceInstanceListSupplier delegate, CacheManager cacheManager) {
super(delegate);
this.serviceInstances = CacheFlux.lookup(key -> {
// 获取缓存容器,缓存的管理使用DefaultLoadBalancerCacheManager,见后文分析
Cache cache = cacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
// 根据serviceId获取List<ServiceInstance>
List<ServiceInstance> list = cache.get(key, List.class);
return Flux.just(list).materialize().collectList();