spring boot loadbalancer 加载过程

open feign3.0开始就不支持Ribbon了
所以只能用loadbalancer
spring-cloud-loadbalancer 官网文档

引入


  org.springframework.cloud
  spring-cloud-starter-loadbalancer
  3.0.2

首先说明一下
spring cloud loadbalancer 是可以单独使用的
但通常我们会和consul等注册中心一起使用
这样我们就不用写死配置了(配置集群里面有哪些服务)
还有这个版本与OpenFeign 和 consul配合使用是不需要做任何配置的

并且spring-cloud-loadbalancer 是 spring-cloud-commons的一个子项目

spring-cloud-loadbalancer

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.LoadBalancerCacheAutoConfiguration,\
org.springframework.cloud.loadbalancer.security.OAuth2LoadBalancerClientAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.LoadBalancerStatsAutoConfiguration
LoadBalancerAutoConfiguration

功能:

  • 1 接受一个客户端配置列表,用consul这里为空
  • 2 设置区域,目前只有Eureka 可以自动检测,consul需要手动设置,负载均衡器会优先在同一个zone下找其他服务器实例,如果只有一个,则去其他zone里找
  • 3 生成LoadBalancerClientFactory 对象,看名字就知道 用来生产LoadBalancerClient的
@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@EnableConfigurationProperties(LoadBalancerProperties.class)
@AutoConfigureBefore({ ReactorLoadBalancerClientAutoConfiguration.class,
        LoadBalancerBeanPostProcessorAutoConfiguration.class })
public class LoadBalancerAutoConfiguration {

    private final ObjectProvider> configurations;

    public LoadBalancerAutoConfiguration(ObjectProvider> configurations) {
        this.configurations = configurations;
    }

    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerZoneConfig zoneConfig(Environment environment) {
        return new LoadBalancerZoneConfig(environment.getProperty("spring.cloud.loadbalancer.zone"));
    }

    @ConditionalOnMissingBean
    @Bean
    public LoadBalancerClientFactory loadBalancerClientFactory() {
        LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();
        clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));
        return clientFactory;
    }

}

BlockingLoadBalancerClientAutoConfiguration

首先在spring-cloud-commons包下也有个叫LoadBalancerAutoConfiguration的配置类,这个配置类会在它前面执行
功能:

  • 1 生产BlockingLoadBalancerClient对象
  • 2 生产LoadBalancerServiceInstanceCookieTransformer对象:用来在发送请求时,通过cookie传递实例serviceId
  • 3 生产BlockingLoadBalancedRetryFactory对象
@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
@AutoConfigureBefore({ org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.class,
        AsyncLoadBalancerAutoConfiguration.class })
@ConditionalOnClass(RestTemplate.class)
public class BlockingLoadBalancerClientAutoConfiguration {

    @Bean
    @ConditionalOnBean(LoadBalancerClientFactory.class)
    @ConditionalOnMissingBean
    public LoadBalancerClient blockingLoadBalancerClient(LoadBalancerClientFactory loadBalancerClientFactory,
            LoadBalancerProperties properties) {
        return new BlockingLoadBalancerClient(loadBalancerClientFactory, properties);
    }

    @Bean
    @ConditionalOnProperty(value = "spring.cloud.loadbalancer.sticky-session.add-service-instance-cookie",
            havingValue = "true")
    @ConditionalOnMissingBean(LoadBalancerServiceInstanceCookieTransformer.class)
    public LoadBalancerServiceInstanceCookieTransformer loadBalancerServiceInstanceCookieTransformer(
            LoadBalancerProperties properties) {
        return new LoadBalancerServiceInstanceCookieTransformer(properties.getStickySession());
    }

    @Configuration
    @ConditionalOnClass(RetryTemplate.class)
    @EnableConfigurationProperties(LoadBalancerProperties.class)
    protected static class BlockingLoadBalancerRetryConfig {

        @Bean
        @ConditionalOnMissingBean
        LoadBalancedRetryFactory loadBalancedRetryFactory(LoadBalancerProperties properties) {
            return new BlockingLoadBalancedRetryFactory(properties);
        }

    }

}

BlockingLoadBalancerClient

请先读一下这篇文章:spring boot open feign 客户端调用过程
会知道OpenFeign发起请求前会调用BlockingLoadBalancerClient.choose选择一个服务端
上面也提到了BlockingLoadBalancerClient 它的加载过程。
接下来看看它的 实现

public class BlockingLoadBalancerClient implements LoadBalancerClient {

    private final LoadBalancerClientFactory loadBalancerClientFactory;

    private final LoadBalancerProperties properties;

    public BlockingLoadBalancerClient(LoadBalancerClientFactory loadBalancerClientFactory,
            LoadBalancerProperties properties) {
        this.loadBalancerClientFactory = loadBalancerClientFactory;
        this.properties = properties;

    }

    。。。

    @Override
    public ServiceInstance choose(String serviceId) {
        return choose(serviceId, REQUEST);
    }

    @Override
    public  ServiceInstance choose(String serviceId, Request request) {
        ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
        if (loadBalancer == null) {
            return null;
        }
        Response loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();
        if (loadBalancerResponse == null) {
            return null;
        }
        return loadBalancerResponse.getServer();
    }

    private String getHint(String serviceId) {
        String defaultHint = properties.getHint().getOrDefault("default", "default");
        String hintPropertyValue = properties.getHint().get(serviceId);
        return hintPropertyValue != null ? hintPropertyValue : defaultHint;
    }

}

从 loadBalancerClientFactory 里取得服务列表
但我们提到了,我们并没有主动的创建服务器列表。
而是通过consul取得的,那么是什么时候取得的呢?
读一下:spring boot consul 客户端加载过程
我们能知道 consul是在web服务器启动完成后,才向注册中心发起注册的
也就是在这之前LoadBalancerClientFactory 一直是空的

所以是调用choose方法的时候才去拉取consul 上的服务器列表
所以我们看下:ReactiveLoadBalancer.choose
它是一个接口,看一下它有哪些实现


spring boot loadbalancer 加载过程_第1张图片
loadbalancer.png

共两个实现:
1,RoundRobinLoadBalancer
2,RandomLoadBalancer
这里的ReactiveLoadBalancer实际上是RoundRobinLoadBalancer(默认的)

但我们在
spring-cloud-loadbalancer的spring.factories
并没有发现RoundRobinLoadBalancer的初始化
不过我们发现它是从
loadBalancerClientFactory里取出来的

ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance(serviceId);

LoadBalancerClientFactory

继承自:NamedContextFactory:真正创建ReactiveLoadBalancer对象
实现了:ReactiveLoadBalancer.Factory。看名字就知道,它就是用来创建ReactiveLoadBalancer 的,但它是委托给了NamedContextFactory 来做的

public class LoadBalancerClientFactory extends NamedContextFactory
        implements ReactiveLoadBalancer.Factory {

    /**
     * Property source name for load balancer.
     */
    public static final String NAMESPACE = "loadbalancer";

    /**
     * Property for client name within the load balancer namespace.
     */
    public static final String PROPERTY_NAME = NAMESPACE + ".client.name";

    public LoadBalancerClientFactory() {
        super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);
    }

    public String getName(Environment environment) {
        return environment.getProperty(PROPERTY_NAME);
    }

    @Override
    public ReactiveLoadBalancer getInstance(String serviceId) {
        return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
    }

}

NamedContextFactory

看一下NamedContextFactory是怎么做的

public abstract class NamedContextFactory
        implements DisposableBean, ApplicationContextAware {

      。。。

    public NamedContextFactory(Class defaultConfigType, String propertySourceName, String propertyName) {
        this.defaultConfigType = defaultConfigType;
        this.propertySourceName = propertySourceName;
        this.propertyName = propertyName;
    }

    protected AnnotationConfigApplicationContext getContext(String name) {
        if (!this.contexts.containsKey(name)) {
            synchronized (this.contexts) {
                if (!this.contexts.containsKey(name)) {
                    this.contexts.put(name, createContext(name));
                }
            }
        }
        return this.contexts.get(name);
    }

    protected AnnotationConfigApplicationContext createContext(String name) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        if (this.configurations.containsKey(name)) {
            for (Class configuration : this.configurations.get(name).getConfiguration()) {
                context.register(configuration);
            }
        }
        for (Map.Entry entry : this.configurations.entrySet()) {
            if (entry.getKey().startsWith("default.")) {
                for (Class configuration : entry.getValue().getConfiguration()) {
                    context.register(configuration);
                }
            }
        }
        context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
        context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,
                Collections.singletonMap(this.propertyName, name)));
        if (this.parent != null) {
            // Uses Environment from parent as well as beans
            context.setParent(this.parent);
            // jdk11 issue
            // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
            context.setClassLoader(this.parent.getClassLoader());
        }
        context.setDisplayName(generateDisplayName(name));
        context.refresh();
        return context;
    }

    public  T getInstance(String name, Class type) {
        AnnotationConfigApplicationContext context = getContext(name);
        try {
            return context.getBean(type);
        }
        catch (NoSuchBeanDefinitionException e) {
            // ignore
        }
        return null;
    }
        。。。

}

可以看到
先getContext(name)
没有的化就createContext

然后就是这句

context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);

defaultConfigType是LoadBalancerClientConfiguration

context将LoadBalancerClientConfiguration注册到上下文环境中
然后context.refresh();
LoadBalancerClientConfiguration的对象就创建出来了(spring.fatories里是没有它的)

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

    private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;

    @Bean
    @ConditionalOnMissingBean
    public ReactorLoadBalancer reactorServiceInstanceLoadBalancer(Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RoundRobinLoadBalancer(
                loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnBlockingDiscoveryEnabled
    @Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER + 1)
    public static class BlockingSupportConfiguration {

        @Bean
        @ConditionalOnBean(DiscoveryClient.class)
        @ConditionalOnMissingBean
        @ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default",
                matchIfMissing = true)
        public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
                ConfigurableApplicationContext context) {
            return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching().build(context);
        }
    }
        。。。
}

可以看到 这里创建了 RoundRobinLoadBalancer

为什么要用NamedContextFactory这样创建
因为,道理很简单,spring-cloud-balancer要为每一个rpc客户端创建一个自己的上下文环境包含自己的配置

用服务名去取,这个serviceId在OpenFeign, Loadbalancer,Consul里是一致的

然后这里还创建了ServiceInstanceListSupplier
看这句,创建这个类的时候用到了DiscoveryClient,还把它们缓存了起来
这里context:ConfigurableApplicationContext包含ConsuDiscoveryClient

ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching().build(context)

ServiceInstanceListSupplierBuilder

public final class ServiceInstanceListSupplierBuilder {

    private static final Log LOG = LogFactory.getLog(ServiceInstanceListSupplierBuilder.class);

    private Creator baseCreator;

    private DelegateCreator cachingCreator;

    private final List creators = new ArrayList<>();

    ServiceInstanceListSupplierBuilder() {
    }

    /**
     * Sets a blocking {@link DiscoveryClient}-based
     * {@link DiscoveryClientServiceInstanceListSupplier} as a base
     * {@link ServiceInstanceListSupplier} in the hierarchy.
     * @return the {@link ServiceInstanceListSupplierBuilder} object
     */
    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 = context.getBean(DiscoveryClient.class);

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

    /**
     * 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 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;
    }
    /**
     * 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;
    }
        。。。
}

这里用到了BiFunciton,大家可以自己去查一下怎么用

DiscoveryClientServiceInstanceListSupplier
这里还用到了Flux
大家可以自己去看

public class DiscoveryClientServiceInstanceListSupplier implements ServiceInstanceListSupplier {

    /**
     * Property that establishes the timeout for calls to service discovery.
     */
    public static final String SERVICE_DISCOVERY_TIMEOUT = "spring.cloud.loadbalancer.service-discovery.timeout";

    private static final Log LOG = LogFactory.getLog(DiscoveryClientServiceInstanceListSupplier.class);

    private Duration timeout = Duration.ofSeconds(30);

    private final String serviceId;

    private final Flux> serviceInstances;

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

    。。。

    @Override
    public Flux> get() {
        return serviceInstances;
    }

    。。。

}

主要是这句

delegate.getInstances(serviceId)

这里的delegate就是ConsulDiscoveryClient

然后就回到RoundRobinLoadBalancer
这个类其实很简单,就是取到服务器列表,然后一个简单的轮询

public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {

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

    final AtomicInteger position;

    final String serviceId;

    ObjectProvider 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 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 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> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
                .getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next()
                .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
    }

    private Response processInstanceResponse(ServiceInstanceListSupplier supplier,
            List serviceInstances) {
        Response serviceInstanceResponse = getInstanceResponse(serviceInstances);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }

    private Response getInstanceResponse(List 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);
    }

}

你可能感兴趣的:(spring boot loadbalancer 加载过程)