【SpringCloud负载均衡】【源码+图解】【四】负载均衡的实现

【SpringCloud负载均衡】【源码+图解】【三】LoadBalancer的工作原理

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默认下是EurekaDiscoveryClientSimpleDiscoveryClient,除了这两个也可以自定义。接下来我们着重分下三个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(); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值