本我的项目代码地址:https://github.com/HashZhang/...

咱们应用 Spring Cloud 官网举荐的 Spring Cloud LoadBalancer 作为咱们的客户端负载均衡器。

Spring Cloud LoadBalancer背景

Spring Cloud LoadBalancer是一个客户端负载均衡器,相似于Ribbon,然而因为Ribbon曾经进入保护模式,并且Ribbon 2并不与Ribbon 1互相兼容,所以Spring Cloud全家桶在Spring Cloud Commons我的项目中,增加了Spring cloud Loadbalancer作为新的负载均衡器,并且做了向前兼容,就算你的我的项目中持续用 Spring Cloud Netflix 套装(包含Ribbon,Eureka,Zuul,Hystrix等等)让你的我的项目中有这些依赖,你也能够通过简略的配置,把ribbon替换成Spring Cloud LoadBalancer。

负载均衡器在哪里应用?

Spring Cloud 中外部微服务调用默认是 http 申请,次要通过上面三种 API:

  • RestTemplate:同步 http API
  • WebClient:异步响应式 http API
  • 三方客户端封装,例如 openfeign

如果我的项目中退出了 spring-cloud-loadbalancer 的依赖并且配置启用了,那么会主动在相干的 Bean 中退出负载均衡器的个性。

  • 对于 RestTemplate,会主动对所有 @LoadBalanced 注解润饰的 RestTemplate Bean 减少 Interceptor 从而加上了负载均衡器的个性。
  • 对于 WebClient,会主动创立 ReactorLoadBalancerExchangeFilterFunction,咱们能够通过退出ReactorLoadBalancerExchangeFilterFunction会退出负载均衡器的个性。
  • 对于三方客户端,个别不须要咱们额定配置什么。

这些应用的示例,会在咱们系列降级完最初的测试局部看到。

Spring Cloud LoadBalancer 构造简介

上一节咱们提到了 NamedContextFactory,Spring Cloud LoadBalancer 这里也是应用了这个机制实现了不同微服务应用不同的 Spring Cloud LoadBalancer 配置。相干外围实现是 @LoadBalancerClient@LoadBalancerClients 这两个注解,以及 NamedContextFactory.Specification 的实现 LoadBalancerClientSpecificationNamedContextFactory 的实现 LoadBalancerClientFactory

通过上一节的详细分析,咱们晓得能够通过 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@ConditionalOnMissingBeanpublic 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@Beanpublic 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)@Documentedpublic @interface LoadBalancerClient {    //name 和 value 都是微服务名称    @AliasFor("name")    String value() default "";    @AliasFor("value")    String name() default "";    //这个微服务的配置    Class<?>[] configuration() default {};}

[LoadBalancerClientConfigurationRegistrar]

@Overridepublic 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 进而实现公共负载平衡配置或者特定某个微服务的负载平衡配置。

微信搜寻“我的编程喵”关注公众号,加作者微信,每日一刷,轻松晋升技术,斩获各种offer