SpringCloud升级之路2020.0.x版-22.Spring Cloud LoadBalancer核心源码
2021-08-27 08:50
447 查看
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford
经过上一节的详细分析,我们知道可以通过
LoadBalancerClientFactory知道默认配置类为
LoadBalancerClientConfiguration. 并且获取微服务名称可以通过
environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
LoadBalancerClientFactory
public static final String NAMESPACE = "loadbalancer"; public static final String PROPERTY_NAME = NAMESPACE + ".client.name"; public LoadBalancerClientFactory() { super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME); }
查看配置类
LoadBalancerClientConfiguration,我们可以发现这个类主要定义两种 Bean,分别是
ReactorLoadBalancer<ServiceInstance>和
ServiceInstanceListSupplier。
ReactorLoadBalancer是负载均衡器,主要提供根据服务名称获取服务实例列表并从从中选择的功能。
ReactorLoadBalancer
Mono<Response<T>> choose(Request request);
在默认配置中的实现是:
LoadBalancerClientConfiguration
@Bean @ConditionalOnMissingBean public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer( Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { //获取微服务名称 String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); //创建 RoundRobinLoadBalancer //注意这里注入的是 LazyProvider,这主要因为在注册这个 Bean 的时候相关的 Bean 可能还没有被加载注册,利用 LazyProvider 而不是直接注入所需的 Bean 防止报找不到 Bean 注入的错误。 return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); }
可以看出,默认配置的
ReactorLoadBalancer实现是
RoundRobinLoadBalancer。这个负载均衡器实现很简单,有一个原子类型的
AtomicInteger position,从
ServiceInstanceListSupplier中读取所有的服务实例列表,然后对于
position原子加1,对列表大小取模,返回列表中这个位置的服务实例
ServiceInstance。
RoundRobinLoadBalancer
public Mono<Response<ServiceInstance>> choose(Request request) { //注入的时候注入的是 Lazy Provider,这里取出真正的 Bean,也就是 ServiceInstanceListSupplier 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) { Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances); // 如果 ServiceInstanceListSupplier 也实现了 SelectedInstanceCallback,则执行下面的逻辑进行回调。SelectedInstanceCallback 就是每次负载均衡器选择实例之后进行的回调 if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer()); } return serviceInstanceResponse; } private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) { if (instances.isEmpty()) { return new EmptyResponse(); } //postion 原子 +1 并取绝对值 int pos = Math.abs(this.position.incrementAndGet()); //返回对应下标的实例 ServiceInstance instance = instances.get(pos % instances.size()); return new DefaultResponse(instance); }
ServiceInstanceListSupplier是服务列表提供者接口:
ServiceInstanceListSupplier
public interface ServiceInstanceListSupplier extends Supplier<Flux<List<ServiceInstance>>> { String getServiceId(); default Flux<List<ServiceInstance>> get(Request request) { return get(); } static ServiceInstanceListSupplierBuilder builder() { return new ServiceInstanceListSupplierBuilder(); } }
spring-cloud-loadbalancer 中有很多
ServiceInstanceListSupplier的实现,在默认配置中是通过属性配置指定实现的,这个配置项是
spring.cloud.loadbalancer.configurations。例如:
LoadBalancerClientConfiguration
@Bean @ConditionalOnBean(ReactiveDiscoveryClient.class) @ConditionalOnMissingBean //spring.cloud.loadbalancer.configurations 未指定或者为 default @ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default", matchIfMissing = true) public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context) { return ServiceInstanceListSupplier.builder() //通过 DiscoveryClient 提供实例 .withDiscoveryClient() //开启缓存 .withCaching() .build(context); } @Bean @ConditionalOnBean(ReactiveDiscoveryClient.class) @ConditionalOnMissingBean //如果 spring.cloud.loadbalancer.configurations 指定为 zone-preference @ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference") public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context) { return ServiceInstanceListSupplier.builder() //通过 DiscoveryClient 提供实例 .withDiscoveryClient() //启用更倾向于同一个 zone 下实例的特性 .withZonePreference() //开启缓存 .withCaching() .build(context); }
可以看到,可以通过
ServiceInstanceListSupplier.builder()生成官方封装好各种特性的
ServiceInstanceListSupplier。其实从底层实现可以看出,所有的
ServiceInstanceListSupplier实现都是代理模式,例如对于默认配置,底层代码近似于:
return //开启服务实例缓存 new CachingServiceInstanceListSupplier( //启用通过 discoveryClient 的服务发现 new DiscoveryClientServiceInstanceListSupplier( discoveryClient, env ) , cacheManagerProvider.getIfAvailable() );
除了默认配置
LoadBalancerClientConfiguration,用户配置自定义配置则是通过
@LoadBalancerClients和
@LoadBalancerClient.这个原理是通过
LoadBalancerClientConfigurationRegistrar实现的。首先,我们来看一下
LoadBalancerClientFactory这个
NamedContextFactory是如何创建的:
[
LoadBalancerAutoConfiguration]
private final ObjectProvider<List<LoadBalancerClientSpecification>> configurations; public LoadBalancerAutoConfiguration(ObjectProvider<List<LoadBalancerClientSpecification>> configurations) { //注入 LoadBalancerClientSpecification List 的 provider //在 Bean 创建的时候,进行载入,而不是注册的时候 this.configurations = configurations; } @ConditionalOnMissingBean @Bean public LoadBalancerClientFactory loadBalancerClientFactory() { //创建 LoadBalancerClientFactory LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory(); //读取所有的 LoadBalancerClientSpecification,设置为 LoadBalancerClientFactory 的配置 clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList)); return clientFactory; }
那么,
LoadBalancerClientSpecification这些 Bean 是怎么创建的呢?在
@LoadBalancerClients和
@LoadBalancerClient注解中,都包含
@Import(LoadBalancerClientConfigurationRegistrar.class)。这个
@Import加载一个
ImportBeanDefinitionRegistrar,这里是
LoadBalancerClientConfigurationRegistrar.
ImportBeanDefinitionRegistrar里面的方法参数包含注解元数据,以及注册 Bean 的
BeanDefinitionRegistry。一般通过注解元数据,动态通过
BeanDefinitionRegistry注册 Bean,在这里的实现是:
[
LoadBalancerClients]
@Configuration(proxyBeanMethods = false) @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE }) @Documented @Import(LoadBalancerClientConfigurationRegistrar.class) public @interface LoadBalancerClients { //可以指定多个 LoadBalancerClient LoadBalancerClient[] value() default {}; //指定所有的负载均衡配置的默认配置 Class<?>[] defaultConfiguration() default {}; }
[
LoadBalancerClient]
@Configuration(proxyBeanMethods = false) @Import(LoadBalancerClientConfigurationRegistrar.class) @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LoadBalancerClient { //name 和 value 都是微服务名称 @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; //这个微服务的配置 Class<?>[] configuration() default {}; }
[
LoadBalancerClientConfigurationRegistrar]
@Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //获取 LoadBalancerClients 注解的元数据 Map<String, Object> attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName(), true); if (attrs != null && attrs.containsKey("value")) { AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value"); //对于 value 属性,其实就是一个 LoadBalancerClient 列表,对于每个生成一个特定微服务名字的 LoadBalancerClientSpecification for (AnnotationAttributes client : clients) { registerClientConfiguration(registry, getClientName(client), client.get("configuration")); } } //如果指定了 defaultConfiguration,则注册为 default 的配置 if (attrs != null && attrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } registerClientConfiguration(registry, name, attrs.get("defaultConfiguration")); } //获取 LoadBalancerClient 注解的元数据 Map<String, Object> client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName(), true); String name = getClientName(client); if (name != null) { registerClientConfiguration(registry, name, client.get("configuration")); } } private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { //初始化 LoadBalancerClientSpecification 的 BeanDefinition,用于注册一个 LoadBalancerClientSpecification Bean BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(LoadBalancerClientSpecification.class); //构造器参数 builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); //注册 Bean registry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition()); }
从代码中我们可以看出,通过使用
@LoadBalancerClients和
@LoadBalancerClient注解可以自动生成对应的
LoadBalancerClientSpecification进而实现公共负载均衡配置或者特定某个微服务的负载均衡配置。
我们这一节详细分析 Spring Cloud LoadBalancer 的源代码来理解其中的原理,下一节我们将介绍在我们项目中使用 Spring Cloud LoadBalancer 要实现的功能。
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:
相关文章推荐
- SpringCloud升级之路2020.0.x版-24.测试Spring Cloud LoadBalancer
- Spring Cloud 升级之路 - 2020.0.x - 7. 使用 Spring Cloud LoadBalancer (2)
- SpringCloud升级之路2020.0.x版-23.订制Spring Cloud LoadBalancer
- 【spring cloud hoxton】Ribbon 真的能被 spring-cloud-loadbalancer 替代吗
- 【Spring Cloud 源码解读】之 【这也太神奇了,RestTemplate加上一个@LoadBalanced注解就能实现负载均衡!】
- Spring Cloud 升级之路 - 2020.0.x - 2. 使用 Undertow 作为我们
- springCloud出现:Load balancer does not have available server for client: SERVICE-XXX的错误解决方法
- SpringCloud使用Feign出现java.lang.ClassNotFoundException: org.springframework.cloud.client.loadbalancer....
- Spring Cloud 升级之路 - 2020.0.x - 4. 使用 Eureka 作为注册中心
- Spring Cloud 升级之路 - 2020.0.x - 1. 背景知识、需求描述与公共依赖
- Spring Cloud 升级之路 - 2020.0.x - 4. 使用 Eureka 作为注册中心
- Spring Cloud 升级之路 - 2020.0.x - 1. 背景知识、需求描述与公共依赖
- Spring Cloud 升级之路 - 2020.0.x - 3. Undertow 的 acces
- Spring IOC核心源码学习
- 网关 Spring-Cloud-Gateway 源码解析 —— 调试环境搭建
- spring cloud bus介绍与源码分析
- Spring源码解读之核心容器下节
- Spring Cloud 微服务开发:入门、进阶与源码剖析 —— 9.1 Spring Cloud Gateway 概述
- Spring Cloud 微服务开发:入门、进阶与源码剖析 —— 6.5 Spring Cloud Zuul 灰度发布
- SpringCloud(五)之Ribbon(负载均衡和源码追踪)