Spring Cloud Commons文档翻译(非转译)

2,817 阅读19分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情

Spring Cloud Commons官方文档链接

image.png

云原生应用

Cloud Native是一种应用程序开发风格,鼓励在持续交付和价值驱动开发领域轻松采用最佳实践。一个相关的规程是构建12因素应用程序,其中开发实践与交付和操作目标保持一致——例如,通过使用声明性编程、管理和监视。Spring Cloud以许多特定的方式促进了这些类型的开发。起点是一组特性,分布式系统中的所有组件都需要轻松访问这些特性。

Spring Boot涵盖了许多这些特性,Spring Cloud就是在Spring Boot的基础上构建的。Spring Cloud以两个库的形式提供了更多的特性: Spring Cloud ContextSpring Cloud CommonsSpring Cloud ContextSpring Cloud应用程序的ApplicationContext提供实用程序和特殊服务(引导上下文、加密、刷新范围和环境端点)。Spring Cloud Commons是一组用于不同Spring Cloud实现(如Spring Cloud NetflixSpring Cloud Consul)的抽象和公共类。

如果由于“非法密钥大小”而出现异常,并且使用SunJDK,则需要安装Java加密扩展(JCE)无限强度管辖策略文件。更多信息请参见以下链接:

Spring Cloud是在非限制性的Apache 2.0许可证下发布的。如果你想对这部分文档有所贡献,或者你发现了一个错误,你可以在{docslink}[github]找到项目的源代码和问题跟踪器。

1.Spring cloud context:应用上下文服务(Application Context Services)

Spring Boot对于如何使用Spring构建应用程序有着固执的看法。例如,它具有用于公共配置文件的常规位置,并具有用于公共管理和监视任务的端点(endpoints)Spring Cloud在此基础上构建,并添加了一些系统中的许多组件会使用或偶尔需要的特性。

1.1.The Bootstrap Application Context(Bootstrap应用程序的上下文)

Spring Cloud应用程序通过创建一个bootstrap上下文来操作,它是主应用程序的 parent context。此上下文负责从外部源加载配置属性,并解密本地外部配置文件中的属性。这两个上下文共享一个Environment,它是任何Spring应用程序的外部属性的来源。默认情况下,bootstrap属性(不是bootstrap.properties,而是在引导阶段加载的属性)是以高优先级添加的,因此它们不能被本地配置覆盖。
bootstrap上下文使用了与主应用程序上下文不同的外部配置定位约定。你可以使用bootstrap.yml来代替application.yml(或 .properties),这样可以很好地将bootstrap和主上下文的外部配置分开。下面的列表显示了一个例子。

Example 1. bootstrap.yml

spring:
  application:
    name: foo
  cloud:
    config:
      uri: ${SPRING_CONFIG_URI:http://localhost:8888}

如果您的应用程序需要从服务器上进行任何特定于应用程序的配置,最好是设置spring.application.name(在bootstrap.ymlapplication.yml中)。要使属性spring.application.name被用作应用程序的上下文ID,你必须在bootstrap.[properties | yml]中设置它。

如果你想检索特定的配置文件配置,你还应该在bootstrap.[properties|yml]中设置spring.profiles.active

你可以通过设置spring.cloud.bootstrap.enabled=false(例如,在系统属性中)来完全禁用引导过程。

1.2. Application Context Hierarchies(应用情境层次结构)

如果您从SpringApplicationSpringApplicationBuilder构建应用程序上下文,那么Bootstrap上下文将作为parent上下文添加到该上下文。Spring的一个特性是,子上下文从其父上下文继承属性源和配置文件,与构建没有Spring Cloud Config的相同上下文相比,main应用程序上下文包含额外的属性源。其他额外的属性源包括:

  • bootstrap: 如果在bootstrap上下文中发现任何PropertySourceLocators,并且它们有非空的属性,则会出现一个可选的具有高优先级的CompositePropertySource。一个例子是来自Spring Cloud配置服务器的属性, 有关如何自定义这个属性源的内容,请参阅 自定义Bootstrap属性源
  • applicationConfig:[classpath:bootstrap.yml](如果Spring配置文件处于活动状态,还包括相关文件)。如果你有一个bootstrap.yml(或.properties),这些属性被用来配置bootstrap上下文。然后当它的父类被设置时,它们会被添加到子类上下文中。它们的优先级低于application.yml(或.properties)以及作为创建Spring Boot应用程序过程的正常部分而被添加到子环境中的任何其他属性源。参见 改变Bootstrap属性的位置,了解如何定制这些属性源的内容。

由于属性源的排序规则,bootstrap条目具有优先权。然而,请注意这些并不包含来自bootstrap.yml的任何数据,后者的优先级很低,但可以用来设置默认值。

你可以通过设置你创建的任何ApplicationContext的父级上下文来扩展上下文层次结构--例如,通过使用它自己的接口或使用SpringApplicationBuilder的方便方法(parent()、child()和sibling())。Bootstrap上下文是你自己创建的最高级祖先的父级。层次结构中的每个上下文都有它自己的 "Bootstrap"(可能是空的)属性源,以避免不经意地将值从父代向下推广到他们的子代。如果有配置服务器,层次结构中的每个上下文(原则上)也可以有不同的spring.application.name,因此也可以有不同的远程属性源。正常的Spring应用上下文行为规则适用于属性解析:子上下文的属性通过名称和属性源名称覆盖父上下文的属性。(如果子节点的属性源与父节点同名,则父节点的值不包含在子节点中)。

请注意,SpringApplicationBuilder允许你在整个层次结构中共享一个环境,但这并不是默认的。因此,兄弟情境(尤其是)不需要有相同的配置文件或属性源,尽管它们可能与父级共享共同的值。

1.3. 更改Bootstrap Properties的位置(Changing the Location of Bootstrap Properties)

bootstrap.yml(或.properties)的位置可以通过设置spring.cloud.bootstrap.name(默认:bootstrap)、spring.cloud.bootstrap.location(默认:空)或spring.cloud.bootstrap.additional-location(默认:空)来指定 - 例如,在系统属性中。

这些属性的行为与同名的 spring.config.* 变体相似。使用spring.cloud.bootstrap.location,会替换默认位置,且只使用指定的位置。要将位置添加到默认位置列表中,可以使用 spring.cloud.bootstrap.additional-location。实际上,它们通过在环境中设置这些属性来配置bootstrap ApplicationContext。如果有一个活动的配置文件(从spring.profiles.active或通过你正在构建的上下文中的环境API),该配置文件中的属性也会被加载,与普通的Spring Boot应用程序相同--例如,从bootstrap-development.properties获取开发配置文件。

1.4. 覆盖远程属性的值(Overriding the Values of Remote Properties)

痛过bootstrap上下文添加到你的应用程序中的属性源通常是 "远程 "的(也就是远程的属性配置不会被本地覆盖)(例如,来自Spring Cloud Config Server)。默认情况下,它们不能被本地覆盖。如果你想让你的应用程序用自己的系统属性或配置文件覆盖远程属性,远程属性源必须通过设置spring.cloud.config.allowOverride=true来授予它权限 (在本地设置这个是不行的) 。一旦该标志被设置,两个更细化的设置将控制远程属性与系统属性和应用程序本地配置之间的位置。

  • spring.cloud.config.overrideNone=true:从任何本地属性源重写。
  • spring.cloud.config.overrideSystemProperties=false:只有系统属性、命令行参数和环境变量(但不是本地配置文件)应覆盖远程设置。

1.5. 定制Bootstrap配置(Customizing the Bootstrap Configuration)

通过在/META-INF/spring.factories中添加名为org.springframework.cloud.bootstrap.BootstrapConfiguration作key的条目,可以将bootstrap上下文设置为你想做的任何事情。这包含一个逗号分隔的Spring @Configuration类列表,用于创建上下文。您希望主应用程序上下文中用于自动装配的任何beans都可以在这里创建。对于ApplicationContextInitializer类型的@Beans有一个特殊的契约:如果你想控制启动顺序,你可以用@Order注解来标记类(默认顺序是最后一个)。

当添加自定义BootstrapConfiguration时,要注意你添加的类不会被@ComponentScanned误入你的 "主 "应用程序上下文,因为那里可能不需要它们。为启动配置类使用一个单独的包名,并确保该名称没有被你的@ComponentScan@SpringBootApplication注释的配置类所覆盖。

引导过程以向主SpringApplication实例注入初始化程序而结束(这是正常的Spring Boot启动顺序,无论它是作为独立的应用程序运行还是部署在应用程序服务器中)。首先,从spring.factories中找到的类创建一个bootstrap上下文。然后,在主SpringApplication启动前,将所有ApplicationContextInitializer类型的@Beans添加到主SpringApplication中。

1.6.定制Bootstrap属性源(Customizing the Bootstrap Property Sources)

Bootstrap过程添加的外部配置的默认属性源是Spring Cloud Config Server,但你可以通过向Bootstrap上下文添加PropertySourceLocator类型的bean(通过spring.factories)来添加额外的来源。例如,你可以从不同的服务器或数据库中插入额外的属性。也就是可以通过此方式来调价额外的配置源

@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {

    @Override
    public PropertySource<?> locate(Environment environment) {
        return new MapPropertySource("customProperty",
                Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
    }

}

传入的环境是即将创建的ApplicationContext的环境--换句话说,是我们提供额外属性源的环境。它已经有了正常的Spring Boot提供的属性源,所以你可以使用这些属性源来定位该环境的特定属性源(例如,通过键入spring.application.name,正如默认的Spring Cloud Config Server属性源定位器所做的那样)。

如果你创建了一个包含这个类的jar,然后添加一个包含以下设置的META-INF/factories,自定义属性PropertySource就会出现在任何在classpath上包含该jar的应用程序中。

org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator

1.7.日志配置

如果你使用Spring Boot来配置日志设置。如果你希望它适用于所有事件,你应该把这个配置放在bootstrap.[yml | properties]中,。

为了让Spring Cloud正确地初始化日志配置,你不能使用自定义的前缀。例如,在初始化日志系统时,使用custom.loggin.logpath不会被Spring Cloud识别。

1.8.环境变化

应用程序监听EnvironmentChangeEvent,并以几种标准的方式对变化做出反应(额外的ApplicationListeners可以以正常的方式添加为@Beans)。当一个EnvironmentChangeEvent被观察到时,它有一个已经改变的关键值的列表,应用程序使用这些值来。

  • 重新绑定上下文中的 @ConfigurationProperties beans .
  • logging.level.*中的任何属性设置记录器级别。

请注意,Spring Cloud Config Client默认不会对环境中的变化进行轮询。一般来说,我们不推荐用这种方法来检测变化(尽管你可以用@Scheduled注解来设置)。如果你有一个扩展的客户端应用程序,最好将EnvironmentChangeEvent广播给所有的实例,而不是让它们轮询变化(例如,通过使用Spring Cloud Bus

EnvironmentChangeEvent涵盖了一大类刷新用例,只要你能真正对环境做出改变并发布事件。请注意,这些API是公开的,是Spring核心的一部分)。你可以通过访问/configprops端点(一个标准的Spring Boot Actuator功能)来验证这些变化是否与@ConfigurationProperties Bean绑定。例如,DataSource可以在运行时改变其maxPoolSize(Spring Boot创建的默认DataSource是一个@ConfigurationProperties Bean)并动态增长容量。重新绑定@ConfigurationProperties并不包括另一大类用例,在这些用例中,你需要对刷新进行更多的控制,而且你需要对整个ApplicationContext进行原子式的更改。为了解决这些问题,我们有@RefreshScope

spring boot 2.3 Actuator 提供 /actuator/configprops 端点,提供对配置文件属性跟踪功能,方便我们在 spring boot 应用中,实时的获取配置文件实际加载值。

需要注意的是:configprops Endpoint 会对敏感字段默认脱敏 ,默认关键字类会进行脱敏,当然,这个可以自己配置

Spring Boot 2.3 中配置文件属性跟踪_coder吹雪的博客-CSDN博客

1.9. Refresh Scope

当配置发生变化时,被标记为@RefreshScope的Spring @Bean会得到特殊处理。这个功能解决了有状态的Bean的问题,这些Bean只有在初始化时才会被注入配置。例如,如果一个DataSource在通过Environment改变数据库URL时有开放的连接,你可能希望这些连接的持有者能够完成他们正在做的事情。然后,下次有东西从池中借用连接时,它就会得到一个带有新URL的连接。

有时,甚至必须对一些只能初始化一次的Bean应用@RefreshScope注解。如果一个Bean是不可改变的,你必须用@RefreshScope来注解这个Bean,或者在属性键下指定类名:spring.cloud.refresh.extra-refreshable ,也就是一些类只能在spring生命周期内初始化一次,那么这种类需要@RefreshScope注解。

如果你有一个属于HikariDataSource的DataSource Bean,它不能被刷新。这是 spring.cloud.refresh.never-refreshable 的默认值。如果你需要它被刷新,请选择一个不同的DataSource实现。

刷新作用域Bean是懒惰的代理,当它们被使用时(即当方法被调用时)就会初始化,作用域作为初始化值的缓存。要强制Bean在下一次方法调用时重新初始化,你必须使其缓存条目失效。

RefreshScope是上下文中的一个Bean,它有一个公共的refreshAll()方法,通过清除目标缓存来刷新该范围内的所有Bean。/refresh端点暴露了这个功能(通过HTTP或JMX)。要按名称刷新单个Bean,也有一个refresh(String)方法。

暴露/refresh端点,你需要向你的应用程序添加以下配置:

management:
  endpoints:
    web:
      exposure:
        include: refresh

@RefreshScope(技术上)对@Configuration类起作用,但它可能导致令人惊讶的行为。例如,它并不意味着该类中定义的所有@Bean本身就在@RefreshScope中。具体地说,任何依赖于这些Bean的东西都不能依靠它们在刷新时被更新,除非它本身也在~中。在这种情况下,它在刷新时被重建,其依赖关系被重新注入。在这一点上,它们会从刷新的@Configuration中被重新初始化)。

1.10. Encryption and Decryption

Spring Cloud有一个环境预处理程序,用于在本地解密属性值。它遵循与Spring Cloud配置服务器相同的规则,并通过encrypt.具有相同的外部配置。因此,你可以使用{cipher} 形式的加密值,只要有一个有效的密钥,它们就会在主应用程序上下文获得环境设置之前被解密。要在应用程序中使用加密功能,你需要在classpath中包含Spring Security RSA(Maven坐标:org.springframework.security:spring-security-rsa),而且你还需要在JVM中使用全强度的JCE扩展。

如果你因 "密钥大小不合法 "而出现异常,并且你使用Sun的JDK,你需要安装Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files。更多信息请参见以下链接。`

将这些文件解压到你所使用的JRE/JDK x64/x86的哪个版本的JDK/jre/lib/security文件夹中。

1.11. Endpoints

对于Spring Boot Actuator应用程序,有一些额外的管理端点可用。你可以使用:

  • POST到/actuator/env来更新环境并重新绑定@ConfigurationProperties和日志级别。要启用这个端点,你必须设置 management.endpoint.env.post.enabled=true
  • /actuator/refresh重新加载启动带上下文并刷新@RefreshScope Bean。
  • /actuator/restart关闭ApplicationContext并重新启动它(默认情况下禁用)。
  • /actuator/pause/actuator/resume用于调用生命周期方法(ApplicationContext上的stop()和start())。

如果你禁用了/actuator/restart端点,那么/actuator/pause/actuator/resume端点也会被禁用,因为它们只是/actuator/restart的一种特殊情况。

2.Spring Cloud Commons: 通用抽象

诸如服务发现、负载平衡和断路器等模式适合于一个通用的抽象层,可以被所有的Spring Cloud客户端消费,与实现无关(例如,用Eureka或Consul发现)。

2.1.@EnableDiscoveryClient注解

Spring Cloud Commons提供了@EnableDiscoveryClient 注解。Spring cloud会去寻找META-INF/spring.factories中实现了DiscoveryClient和ReactiveDiscoveryClient接口的类。
自定义的discovery客户端需要在spring.factoriesorg.springframework.cloud.client.discovery.EnableDiscoveryClientkey下向添加一个对应自定义的类全名称。
DiscoveryClient实现的例子包括Spring Cloud Netflix EurekaSpring Cloud Consul DiscoverySpring Cloud Zookeeper Discovery

Spring Cloud默认提供阻塞式和反应式服务发现客户端。你可以通过设置 spring.cloud.discovery.blocking.enabled=false 或 spring.cloud.discovery.reactive.enabled=false 来轻松禁用阻塞式和/或反应式客户端。要完全禁用服务发现,你只需要设置 spring.cloud.discovery.enabled=false

默认情况下,DiscoveryClient的实现会自动将本地Spring Boot服务器与远程发现服务器进行注册。这种行为可以通过在@EnableDiscoveryClient中设置autoRegister=false来禁用。

@EnableDiscoveryClient不再需要了。你可以在classpath上放置一个DiscoveryClient实现,以使Spring Boot应用程序在服务发现服务器注册。

2.1.1.健康指标

Commons自动配置了以下Spring Boot健康指标。

DiscoveryClientHealthIndicator

该健康指标是基于当前注册的DiscoveryClient实现。

  • 要完全禁用,请设置 spring.cloud.discovery.client.health-indicator.enabled=false
  • 要禁用描述字段,请设置 spring.cloud.discovery.client.health-indicator.include-description=false。否则,它可以作为产生的HealthIndicator的描述冒出来。
  • 要禁用服务检索,请设置spring.cloud.discovery.client.health-indicator.use-services-query=false。默认情况下,该指标会调用客户端的getServices方法。在有许多注册服务的部署中,每次检查时检索所有服务的成本可能太高。这将跳过服务检索,去使用客户端的探测方法(todo)。

发现复合健康指标(DiscoveryCompositeHealthContributor)

这个复合健康指标是基于所有注册的DiscoveryHealthIndicator Bean。要禁用,请设置 spring.cloud.discovery.client.composite-indicator.enabled=false

2.1.2.对DiscoveryClient实例进行排序

DiscoveryClient接口扩展了Ordered。这在使用多个发现客户端时非常有用,因为它允许你定义返回的发现客户端的顺序,类似于你可以对Spring应用程序加载的Bean进行排序。默认情况下,任何DiscoveryClient的顺序都被设置为0。如果你想为你的自定义DiscoveryClient实现设置一个不同的顺序,你只需要覆盖getOrder()方法,使其返回适合你的设置的值即可。除此之外,你还可以使用属性来设置Spring Cloud提供的DiscoveryClient实现的顺序,其中包括ConsulDiscoveryClientEurekaDiscoveryClientZookeeperDiscoveryClient。为了做到这一点,你只需将spring.cloud.{clientIdentifier}.discovery.order(或eureka.client.order,)属性设置为所需值。

2.1.3.SimpleDiscoveryClient

如果在classpath中没有服务注册表支持的DiscoveryClient,将使用SimpleDiscoveryClient实例,该实例使用属性来获取服务和实例的信息。
关于可用实例的信息应通过以下格式的属性传递:spring.cloud.discovery.client.simple.instances.service1[0].uri=http://s11:8080,其中spring.cloud.discovery.client.simple.instances是通用前缀,然后service1代表有关服务的 ID,而[0]表示实例的索引号(如示例中可见,索引以0开始),然后uri的值是实例可用的实际 URI。

2.2. ServiceRegistry

Commons现在提供了一个ServiceRegistry接口,提供了register(Registration)deregister(Registration)等方法,让你提供自定义的注册服务。注册是一个标记性的接口。
下面的例子显示了使用中的ServiceRegistry:

@Configuration
@EnableDiscoveryClient(autoRegister=false)
public class MyConfiguration {
    private ServiceRegistry registry;

    public MyConfiguration(ServiceRegistry registry) {
        this.registry = registry;
    }

    // called through some external process, such as an event or a custom actuator endpoint
    public void register() {
        Registration registration = constructRegistration();
        this.registry.register(registration);
    }
}

每个ServiceRegistry的实现都有自己的Registry实现。

  • ZookeeperRegistration used with ZookeeperServiceRegistry
  • EurekaRegistration used with EurekaServiceRegistry
  • ConsulRegistration used with ConsulServiceRegistry
  • NacosRegistration used with NacosServiceRegistry

如果你正在使用ServiceRegistry接口,你将需要自己去实现ServiceRegistry

2.2.1.服务注册处自动注册

默认情况下,ServiceRegistry实现会自动注册正在运行的服务。要禁用该行为,你可以设置。* @EnableDiscoveryClient(autoRegister=false)来永久地禁用自动注册。*spring.cloud.service-registry.auto-registration.enabled=false以通过配置禁用该行为。

服务注册处自动注册事件

当一个服务自动注册时,有两个事件将被触发。第一个事件,称为InstancePreRegisteredEvent,在服务被注册之前被触发。第二个事件,称为InstanceRegisteredEvent,在服务被注册后被触发。你可以注册一个ApplicationListener(s)来监听这些事件并作出反应。

如果spring.cloud.service-registry.auto-registration.enabled属性被设置为false,这些事件将不会被触发。

2.2.2. 服务注册处执行器端点

Spring Cloud Commons提供了一个/service-registry执行器端点。这个端点依赖于Spring应用上下文中的一个注册Bean。用GET方式调用/service-registry会返回注册的状态。使用JSON体对同一端点进行POST,将当前注册的状态改为新值。JSON主体必须包括带有首选值的status字段。关于更新状态时允许的值和返回的状态值,请参见您使用的ServiceRegistry实现的文档。例如,Eureka支持的状态有:UPDOWNOUT_OF_SERVICEUNKNOWN

2.3. 作为负载均衡器客户端的Spring RestTemplate

你可以配置一个RestTemplate来使用一个负载平衡客户端。为了创建一个负载平衡的RestTemplate,创建一个RestTemplate @Bean,并使用@LoadBalanced限定词,如下例所示:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
    @Autowired
    private RestTemplate restTemplate;

    public String doOtherStuff() {
        String results = restTemplate.getForObject("http://stores/stores", String.class);
        return results;
    }
}

RestTemplate Bean不再通过自动配置创建。个人应用程序必须创建它。

URI需要使用一个虚拟主机名(即服务名,而不是主机名)。BlockingLoadBalancerClient是用来创建一个完整的物理地址。

要使用负载平衡的RestTemplate,你需要在你的classpath中拥有一个负载均衡器的实现。在你的项目中添加Spring Cloud LoadBalancer启动器,以便使用它。

2.4. 作为负载均衡器客户端的Spring WebClient

你可以将WebClient配置为自动使用一个负载平衡客户端。要创建一个负载平衡的WebClient,请创建一个WebClient.Builder @Bean并使用@LoadBalanced限定词,如下所示。

@Configuration
public class MyConfiguration {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}

public class MyClass {
    @Autowired
    private WebClient.Builder webClientBuilder;

    public Mono<String> doOtherStuff() {
        return webClientBuilder.build().get().uri("http://stores/stores")
                        .retrieve().bodyToMono(String.class);
    }
}

URI需要使用一个虚拟主机名(即服务名,而不是主机名)。Spring Cloud LoadBalancer用于创建一个完整的物理地址。

如果你想使用@LoadBalanced WebClient.Builder,你需要在classpath中拥有一个负载均衡器实现。我们建议你在项目中添加Spring Cloud LoadBalancer start。然后,在下面使用ReactiveLoadBalancer

2.4.1. 重试失败的请求

负载均衡的RestTemplate可以被配置为重试失败的请求。默认情况下,这种逻辑是禁用的。对于非反应式版本(使用RestTemplate),你可以通过在应用程序的classpath中添加Spring Retry来启用它。对于反应式版本(使用WebTestClient),你需要设置spring.cloud.loadbalancer.retry.enabled=true

当然如果你想在classpath上禁用Spring RetryReactive Retry的重试逻辑,你可以设置spring.cloud.loadbalancer.retry.enabled=false

对于非反应式实现,如果你想在重试中实现BackOffPolicy,你需要创建一个LoadBalancedRetryFactory类型的bean,并重载createBackOffPolicy()方法。

这里说一下BackOffPolicy: 重试的回退策略,在业务逻辑执行发生异常时。如果需要重试,我们可能需要等一段时间(可能服务器过于繁忙,如果一直不间隔重试可能拖垮服务器),当然这段时间可以是0,也可以是固定的,可以是随机的(参见tcp的拥塞控制算法中的回退策略)。

对于反应式实现,你只需要将 spring.cloud.loadbalancer.retry.backoff.enabled 设置为false,就可以启用。

你可以设置:

  • spring.cloud.loadbalancer.retry.maxRetriesOnSameServiceInstance - 指示在同一个服务实例上应重试多少次请求(对每个选定的实例单独计算)。
  • spring.cloud.loadbalancer.retry.maxRetriesOnNextServiceInstance- 指示请求应在新选择的服务实例上重试多少次。
  • spring.cloud.loadbalancer.retry.retryableStatusCodes - 始终重试失败请求的状态代码。

对于反应式实现,你可以额外设置。- spring.cloud.loadbalancer.retry.backoff.minBackoff - 设置最小回退时间(默认为5毫秒) -spring.cloud.loadbalancer.retry.backoff.minBackoff - 设置最大回退时间(默认为最大长值毫秒) - spring.cloud.loadbalancer.retry.backoff.jitter - 设置用于计算每次调用实际回退时间的抖动(默认为0.5)。

对于反应式的实现,你也可以实现你自己的LoadBalancerRetryPolicy,以便对负载平衡的调用重试有更详细的控制。

除了前缀为 spring.cloud.loadbalancer.clients.<clientId>.*,其中 clientId是负载均衡器的名称外,单个负载均衡器客户端可以单独配置与上述相同的属性。

对于负载均衡重试,默认情况下,我们用RetryAwareServiceInstanceListSupplierBean包裹ServiceInstanceListSupplier Bean,以选择与之前选择的实例不同的实例(如果有的话)。您可以通过将 spring.cloud.loadbalancer.retry.avoidPreviousInstance 的值设置为 false 来禁用这种行为。

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryFactory retryFactory() {
        return new LoadBalancedRetryFactory() {
            @Override
            public BackOffPolicy createBackOffPolicy(String service) {
                return new ExponentialBackOffPolicy();
            }
        };
    }
}

如果你想为你的重试功能添加一个或多个RetryListener实现,你需要创建一个LoadBalancedRetryListenerFactory类型的bean,并返回你想为给定服务使用的RetryListener数组,如下例所示:

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryListenerFactory retryListenerFactory() {
        return new LoadBalancedRetryListenerFactory() {
            @Override
            public RetryListener[] createRetryListeners(String service) {
                return new RetryListener[]{new RetryListener() {
                    @Override
                    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                        //TODO Do you business...
                        return true;
                    }

                    @Override
                     public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                        //TODO Do you business...
                    }

                    @Override
                    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                        //TODO Do you business...
                    }
                }};
            }
        };
    }
}

2.5. 多个RestTemplate对象

如果你想要一个没有负载均衡的RestTemplate,可以创建一个RestTemplate Bean并注入它。要访问负载均衡的RestTemplate,在你创建@Bean时使用@LoadBalanced注解,如下例所示:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate loadBalanced() {
        return new RestTemplate();
    }

    @Primary
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
@Autowired
private RestTemplate restTemplate;

    @Autowired
    @LoadBalanced
    private RestTemplate loadBalanced;

    public String doOtherStuff() {
        return loadBalanced.getForObject("http://stores/stores", String.class);
    }

    public String doStuff() {
        return restTemplate.getForObject("http://example.com", String.class);
    }
}

注意在前面的例子中,在普通的RestTemplate声明上使用了@Primary注解,以消除不合格的@Autowired注入的歧义。

如果你看到诸如 java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89,尝试注入RestOperations或设置spring.aop.proxyTargetClass=true

2.6. 多个WebClient对象

如果你想要一个没有负载均衡的WebClient,可以创建一个WebClient Bean并注入它。要访问负载均衡的WebClient,请在创建@Bean时使用@LoadBalanced注解,如下例所示。

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    WebClient.Builder loadBalanced() {
        return WebClient.builder();
    }

    @Primary
    @Bean
    WebClient.Builder webClient() {
        return WebClient.builder();
    }
}

public class MyClass {
    @Autowired
    private WebClient.Builder webClientBuilder;

    @Autowired
    @LoadBalanced
    private WebClient.Builder loadBalanced;

    public Mono<String> doOtherStuff() {
        return loadBalanced.build().get().uri("http://stores/stores")
                        .retrieve().bodyToMono(String.class);
    }

    public Mono<String> doStuff() {
        return webClientBuilder.build().get().uri("http://example.com")
                        .retrieve().bodyToMono(String.class);
    }
}

2.7. 作为负载均衡器客户端的Spring WebFlux WebClient

Spring WebFlux可以与反应式和非反应式的WebClient配置一起工作,如主题所述。

使用ReactorLoadBalancerExchangeFilterFunction的Spring WebFlux WebClient

load-balancer-exchange-filter-functionload-balancer-exchange-filter-function

2.8. 忽略网络接口

有时,忽略某些命名的网络接口是很有用的,这样它们就可以被排除在服务发现注册之外(例如,在Docker容器中运行时)。可以设置一个正则表达式列表,使所需的网络接口被忽略。下面的配置忽略了docker0接口和所有以veth开头的接口。

Example 2. application.yml

spring:
  cloud:
    inetutils:
      ignoredInterfaces:
        - docker0
        - veth.*

你也可以通过使用正则表达式列表强制只使用指定的网络地址,如下例所示:

Example 3. bootstrap.yml

spring:
  cloud:
    inetutils:
      preferredNetworks:
        - 192.168
        - 10.0

你也可以强制只使用站点本地地址,如下例所示:

Example 4. application.yml

spring:
  cloud:
    inetutils:
      useOnlySiteLocalInterfaces: true

关于什么是站点-本地地址的更多细节,请参见 Inet4Address.html.isSiteLocalAddress()

2.9. HTTP客户端工厂

Spring Cloud Commons提供了用于创建Apache HTTP客户端(ApacheHttpClientFactory)和OK HTTP客户端(OkHttpClientFactory)的bean。只有当OK HTTP jar在classpath上时才会创建OkHttpClientFactory Bean。此外,Spring Cloud Commons还提供了用于创建两种客户端所使用的连接管理器的bean。

ApacheHttpClientConnectionManagerFactory用于Apache HTTP客户端,OkHttpClientConnectionPoolFactory用于OK HTTP客户端。如果你想自定义HTTP客户端在下游项目中的创建方式,你可以提供你自己对这些Bean的实现。此外,如果你提供一个HttpClientBuilder或OkHttpClient.Builder类型的bean,默认工厂会使用这些构建器作为返回给下游项目的构建器的基础。你也可以通过将spring.cloud.httpclientfactories.apache.enabled 或 spring.cloud.httpclientfactories.ok.enabled设置为 false来禁用这些 bean 的创建。

2.10. 启用的功能

Spring Cloud Commons提供了一个/features执行器端点。这个端点返回classpath上可用的特性以及它们是否被启用。返回的信息包括功能类型、名称、版本和供应商。

2.10.1. Feature types

有两种类型的 "特征":抽象的和命名的。

抽象特征是指定义了一个接口或抽象类的特征,并且创建了一个实现,例如DiscoveryClientLoadBalancerClientLockService。抽象类或接口被用来在上下文中找到该类型的Bean。显示的版本是 bean.getClass().getPackage().getImplementationVersion()

命名的特性是指没有它们实现的特定类的特性。这些特性包括 "Circuit Breaker"、"API Gateway"、"Spring Cloud Bus "和其他。这些特性需要一个名称和一个bean类型。

2.10.2. 声明特征

任何模块都可以声明任意数量的HasFeature beans,正如下面的例子所示:

@Bean
public HasFeatures commonsFeatures() {
  return HasFeatures.abstractFeatures(DiscoveryClient.class, LoadBalancerClient.class);
}

@Bean
public HasFeatures consulFeatures() {
  return HasFeatures.namedFeatures(
    new NamedFeature("Spring Cloud Bus", ConsulBusAutoConfiguration.class),
    new NamedFeature("Circuit Breaker", HystrixCommandAspect.class));
}

@Bean
HasFeatures localFeatures() {
  return HasFeatures.builder()
      .abstractFeature(Something.class)
      .namedFeature(new NamedFeature("Some Other Feature", Someother.class))
      .abstractFeature(Somethingelse.class)
      .build();
}

这些beans中的每一个都应该放在一个有适当保护的@Configuration中。

2.11. Spring Cloud兼容性验证

由于一些用户在设置Spring Cloud应用程序时遇到了问题,我们决定增加一个兼容性验证机制。如果你目前的设置不符合Spring Cloud的要求,它就会中断,并提供一份报告,说明到底是哪里出了问题。

目前,我们会验证哪个版本的Spring Boot被添加到你的classpath中。

报告的例子:

***************************
APPLICATION FAILED TO START
***************************

Description:

Your project setup is incompatible with our requirements due to following reasons:

- Spring Boot [2.1.0.RELEASE] is not compatible with this Spring Cloud release train


Action:

Consider applying the following actions:

- Change Spring Boot version to one of the following versions [1.2.x, 1.3.x] .
You can find the latest Spring Boot versions here [https://spring.io/projects/spring-boot#learn].
If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [https://spring.io/projects/spring-cloud#overview] and check the [Release Trains] section.

为了禁用该功能,请将spring.cloud.compatibility-verifier.enabled设置为false。如果你想覆盖兼容的Spring Boot版本,只需将spring.cloud.compatibility-verifier.compatible-boot-versions属性设为逗号分隔的兼容Spring Boot版本列表。

3. Spring Cloud LoadBalancer

Spring Cloud提供了自己的客户端负载均衡器的抽象和实现。对于负载平衡机制,添加了ReactiveLoadBalancer接口,并为其提供了Round-Robin-basedRound的实现。为了从反应式服务实例中获得选择,我们使用了ServiceInstanceListSupplier。目前我们支持基于服务发现ServiceInstanceListSupplier的实现,它使用classpath中的发现客户端从服务发现中检索可用的实例。

可以通过将spring.cloud.loadbalancer.enabled的值设置为false来禁用Spring Cloud LoadBalancer

3.1. 负载平衡算法之间的切换

默认使用的ReactiveLoadBalancer实现是RoundRobinLoadBalancer。要切换到不同的实现,无论是对选择的服务还是所有的服务,你都可以使用自定义LoadBalancer配置机制。

例如,可以通过@LoadBalancerClient注解传递以下配置,以切换到使用RandomLoadBalancer:

public class CustomLoadBalancerConfiguration {

    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
    }
}

作为@LoadBalancerClient@LoadBalancerClients配置参数传递的类不应该用@Configuration来注解,或者是在组件扫描范围之外。

3.2. Spring Cloud LoadBalancer的集成

为了方便使用Spring Cloud LoadBalancer,我们提供了可用于WebClientReactorLoadBalancerExchangeFilterFunction和可用于RestTemplateBlockingLoadBalancerClient。你可以在以下章节看到更多的信息和使用实例:

3.3. Spring Cloud LoadBalancer 缓存

除了每次需要选择实例时通过DiscoveryClient检索实例的基本ServiceInstanceListSupplier实现之外,我们还提供了两种缓存实现。

3.3.1. Caffeine-backed LoadBalancer缓存实现

如果你的classpath中有com.github.ben-manes.caffeine:caffeine,将使用基于Caffeine的实现。有关如何配置的信息,请参阅LoadBalancerCacheConfiguration部分。

如果你使用Caffeine,你也可以通过在spring.cloud.loadbalancer.cache.caffeine.spec属性中传递你自己的Caffeine Specification来覆盖LoadBalancer的默认Caffeine缓存设置。

WARN: 传递您自己的Caffeine规范将覆盖任何其他LoadBalancerCache设置,包括一般LoadBalancer Cache配置字段,如ttl容量(capacity)

3.3.2. 默认的LoadBalancer Cache实现

如果你的classpath中没有Caffeine,将使用spring-cloud-starter-loadbalancer自动附带的DefaultLoadBalancerCache。请参阅LoadBalancerCacheConfiguration部分,了解如何配置它的信息。

3.3.3. 负载均衡器缓存配置

您可以通过传递符合Spring Boot StringDuration转换语法的String,来设置您自己的ttl值(写入后条目应过期的时间),以Duration表示。作为pring.cloud.loadbalancer.cache.ttl属性的值。您还可以通过设置spring.cloud.loadbalancer.cache.capacity属性的值来设置您自己的LoadBalancer缓存初始容量。

默认设置包括ttl设置为35秒,默认初始容量(initialCapacity)为256。

你也可以通过将spring.cloud.loadbalancer.cache.enabled的值设置为false来完全禁用loadBalancer缓存。

尽管基本的、非缓存的实现对于原型设计和测试很有用,但它的效率远低于缓存版本,因此我们建议在生产中始终使用缓存版本。如果缓存已经由DiscoveryClient实现完成,例如EurekaDiscoveryClient,则应禁用负载平衡器缓存以防止双重缓存。

3.4. 基于区域的负载平衡

为了实现基于区域的负载平衡,我们提供ZonePreferenceServiceInstanceListSupplier。我们使用DiscoveryClient特定的区域配置(例如,eureka.instance.metadata-map.zone)来挑选客户端试图过滤可用服务实例的区域。

你也可以通过设置spring.cloud.loadbalancer.zone属性的值来重写DiscoveryClient特定的区域设置。

目前,只有Eureka发现客户端被用来设置LoadBalancer区域。对于其他发现客户端,设置spring.cloud.loadbalancer.zone属性。更多的工具很快就会出现。

为了确定一个被检索的服务实例的区域,我们检查metadata map中 "zone "键下的值。

ZonePreferenceServiceInstanceListSupplier过滤检索到的实例,只返回同一区域内的实例。如果区域是空的或在同一区域内没有实例,它将返回所有检索到的实例。

为了使用基于分区的负载平衡方法,你必须在custom configuration中实例化一个ZonePreferenceServiceInstanceListSupplier Bean。

我们使用委托来处理ServiceInstanceListSupplier Bean。我们建议在ZonePreferenceServiceInstanceListSupplier的构造函数中传递一个DiscoveryClientServiceInstanceListSupplier委托,并反过来用CachingServiceInstanceListSupplier包装后者,以利用LoadBalancer缓存机制( LoadBalancer caching mechanism)。

你可以使用这个样本配置来设置它:

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                    .withDiscoveryClient()
                    .withZonePreference()
                    .withCaching()
                    .build(context);
    }
}

3.5. 负载平衡器的实例健康检查

我们可以为LoadBalancer启用预定的健康检查。HealthCheckServiceInstanceListSupplier就是为此提供的。它定期检查由委托的ServiceInstanceListSupplier提供的实例是否仍然活着,并且只返回健康的实例,除非没有--那么它将返回所有检索到的实例。

这个机制在使用SimpleDiscoveryClient时特别有用。对于由实际的服务注册中心支持的客户端,没有必要使用,因为我们在查询外部ServiceDiscovery后已经得到了健康的实例。也就是说我们如果用的其他替他提供的自定义客户端,里面已经做处理了。

这个supplier也被推荐用于每个服务有少量实例的设置,以避免在失败的实例上重试调用。

如果使用任何由服务发现支持的供应商,通常不需要添加这种健康检查机制,因为我们直接从服务注册中心检索实例的健康状态。

HealthCheckServiceInstanceListSupplier依赖于拥有由delegate flux提供的更新实例。在极少数情况下,当你想使用一个即使实例列表(远端)发生变化也没刷新实例(本地)的委托(如我们提供的 DiscoveryClientServiceInstanceListSupplier)时,你可以将spring.cloud.loadbalancer.health-check.refetch-instances 设置为 true,让 HealthCheckServiceInstanceListSupplier刷新实例列表。然后,您还可以通过修改 HealthCheckServiceInstanceListSupplier 的值来调整刷新间隔,并选择通过将 spring.cloud.loadbalancer.health-check.repeat-health-check 设置为false来禁用额外的健康检查重复,因为每次实例刷新也将触发一次健康检查。

如果你依赖默认路径(/actuator/health),请确保将spring-boot-starter-actuator添加到你的合作者的依赖项中,除非你打算自己添加这样一个端点。

为了使用健康检查调度器方法,你必须在 custom configuration中实例化一个HealthCheckServiceInstanceListSupplierBean。

我们使用委托来处理ServiceInstanceListSupplier Bean。我们建议在HealthCheckServiceInstanceListSupplier的构造函数中传递一个DiscoveryClientServiceInstanceListSupplier委托。

你可以使用这个示例配置来设置它:

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                    .withDiscoveryClient()
                    .withHealthChecks()
                    .build(context);
        }
    }

​ 对于非反应堆栈,用withBlockingHealthChecks()创建这个供应商。你也可以传递你自己的WebClientRestTemplate实例来用于检查。

HealthCheckServiceInstanceListSupplier有自己的基于Reactor Flux replay()的缓存机制。因此,如果正在使用它,你可能想跳过可以用CachingServiceInstanceListSupplier来包装该供应商。

3.6. LoadBalancer的相同实例偏好

你可以将LoadBalancer设置成这样一种方式,即如果之前选择的实例可用,它就会优先选择该实例。

为此,您需要使用 SameInstancePreferenceServiceInstanceListSupplier。你可以通过将 spring.cloud.loadbalancer.configurations 的值设置为same-instance-preference或提供你自己的ServiceInstanceListSupplier Bean 来配置它 - 例如:

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                    .withDiscoveryClient()
                    .withSameInstancePreference()
                    .build(context);
        }
    }

这也是Zookeeper StickyRule的替代品。

3.7. 基于请求的LoadBalancer粘性会话

你可以将LoadBalancer设置成这样一种方式,即它更倾向于使用请求cookie中提供的instanceId的实例。如果请求是通过ClientRequestContextServerHttpRequestContext传递给LoadBalancer的,我们目前支持这种方式,SC LoadBalancer交换过滤功能和过滤器。

为此,你需要使用 RequestBasedStickySessionServiceInstanceListSupplier。你可以通过将 spring.cloud.loadbalancer.configurations的值设置为 request-based-sticky-session或提供你自己的ServiceInstanceListSupplier Bean 来配置它 - 例如:

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                    .withDiscoveryClient()
                    .withRequestBasedStickySession()
                    .build(context);
        }
    }

为了实现这一功能,在向前发送请求之前,更新所选的服务实例(如果该实例不可用,它可以与原始请求 cookie 中的实例不同)是非常有用的。要做到这一点,请将spring.cloud.loadbalancer.sticky-session.add-service-instance-cookie的值设置为true

默认情况下,cookie 的名称是sc-lb-instance-id。你可以通过改变 spring.cloud.loadbalancer.instance-id-cookie-name属性的值来修改它。

该功能目前支持由WebClient支持的负载平衡。

3.8. Spring Cloud LoadBalancer提示

Spring Cloud LoadBalancer让你设置字符串提示,这些提示在Request对象中传递给LoadBalancer,以后可以在ReactiveLoadBalancer实现中使用,这些实现可以处理它们。

您可以通过设置 spring.cloud.loadbalancer.hint.default属性的值为所有服务设置一个默认提示。您还可以通过设置 spring.cloud.loadbalancer.hint.[SERVICE_ID]属性的值为任何给定的服务设置一个特定的值,用您的服务的正确ID 代替 [SERVICE_ID]。如果用户没有设置提示,则使用默认值。

3.9. 基于Hint的负载平衡

我们还提供了一个HintBasedServiceInstanceListSupplier,它是一个ServiceInstanceListSupplier实现,用于基于提示的实例选择。

HintBasedServiceInstanceListSupplier检查提示请求头(默认头名是X-SC-LB-Hint,但你可以通过改变spring.cloud.loadbalancer.hint-header-name属性的值来修改它),如果它找到一个提示请求头,则使用头中传递的提示值来过滤服务实例。

如果没有添加提示头,HintBasedServiceInstanceListSupplier使用来自属性的提示值来过滤服务实例。

如果没有设置提示,无论是通过头还是通过属性,都会返回由委托人提供的所有服务实例。

在过滤时,HintBasedServiceInstanceListSupplier会寻找在其metadataMap中的提示键下设置有匹配值的服务实例。如果没有找到匹配的实例,就会返回该委托人提供的所有实例。

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                    .withDiscoveryClient()
                    .withHints()
                    .withCaching()
                    .build(context);
    }
}

3.10. 转换负载平衡的HTTP请求

你可以使用选定的ServiceInstance来转换负载平衡的HTTP请求。

对于RestTemplate,你需要实现并定义LoadBalancerRequestTransformer,如下所示。

@Bean
public LoadBalancerRequestTransformer transformer() {
    return new LoadBalancerRequestTransformer() {
        @Override
        public HttpRequest transformRequest(HttpRequest request, ServiceInstance instance) {
            return new HttpRequestWrapper(request) {
                @Override
                public HttpHeaders getHeaders() {
                    HttpHeaders headers = new HttpHeaders();
                    headers.putAll(super.getHeaders());
                    headers.add("X-InstanceId", instance.getInstanceId());
                    return headers;
                }
            };
        }
    };
}

对于WebClient,你需要实现和定义LoadBalancerClientRequestTransformer,如下所示。

@Bean
public LoadBalancerClientRequestTransformer transformer() {
    return new LoadBalancerClientRequestTransformer() {
        @Override
        public ClientRequest transformRequest(ClientRequest request, ServiceInstance instance) {
            return ClientRequest.from(request)
                    .header("X-InstanceId", instance.getInstanceId())
                    .build();
        }
    };
}

如果定义了多个转化器,它们将按照定义Bean的顺序来应用。另外,你可以使用LoadBalancerRequestTransformer.DEFAULT_ORDERLoadBalancerClientRequestTransformer.DEFAULT_ORDER来指定顺序。

3.11. Spring Cloud LoadBalancer Starter

我们还提供了一个启动器,允许你在Spring Boot应用中轻松添加Spring Cloud LoadBalancer。为了使用它,只需在构建文件中将org.springframework.cloud:spring-cloud-starter-loadbalancer添加到你的Spring Cloud依赖项中。

Spring Cloud LoadBalancer starter includes Spring Boot Caching and Evictor.

3.12. 传递你自己的Spring Cloud LoadBalancer配置

你也可以使用@LoadBalancerClient注解来传递你自己的负载均衡器客户端配置,传递负载均衡器客户端的名称和配置类,如下所示:

@Configuration
@LoadBalancerClient(value = "stores", configuration = CustomLoadBalancerConfiguration.class)
public class MyConfiguration {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}

为了使你自己的LoadBalancer配置工作更加容易,我们在ServiceInstanceListSupplier类中增加了一个builder()方法。

您还可以使用我们的替代预定义配置来代替默认配置,方法是将 spring.cloud.loadbalancer.configuration属性的值设置为zone-preference,以使用带缓存的ZonePreferenceServiceInstanceListSupplier,或设置为health-check,以使用带缓存的 HealthCheckServiceInstanceListSupplier

你可以使用这个功能来实例化ServiceInstanceListSupplierReactorLoadBalancer的不同实现,可以由你来写,也可以由我们提供替代方案(例如ZonePreferenceServiceInstanceListSupplier)来覆盖默认设置。

你可以在这里看到一个自定义配置的例子

注释值参数(上面例子中的商店)指定了我们应该用给定的自定义配置发送请求的服务的服务ID。

你也可以通过@LoadBalancerClients注解传递多个配置(用于多个负载均衡器客户端),如下例所示。

@Configuration
@LoadBalancerClients({@LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class), @LoadBalancerClient(value = "customers", configuration = CustomersLoadBalancerClientConfiguration.class)})
public class MyConfiguration {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}

你作为@LoadBalancerClient@LoadBalancerClients配置参数传递的类不应该用@Configuration来注解,或者是在组件扫描范围之外。

3.13. Spring Cloud LoadBalancer 生命周期

使用Custom LoadBalancer configuration注册的一种Bean类型可能是LoadBalancerLifecycle。

LoadBalancerLifecycleBean提供了回调方法,名为onStart(Request<RC> request)、onStartRequest(Request<RC> request, Response<T> lbResponse)onComplete(CompletionContext<RES, T, RC> completionContext),你应该实现这些方法来指定在负载均衡之前和之后应该采取什么行动。

onStart(Request<RC> request)接收一个Request对象作为参数。它包含用于选择适当实例的数据,包括下游客户端的请求和提示。onStartRequest也接受Request对象,此外,还接受Response对象作为参数。另一方面,completionContext对象被提供给onComplete(completionContext<RES, T, RC> completionContext)方法。它包含了LoadBalancer的响应,包括选定的服务实例、针对该服务实例执行的请求的状态和(如果有)返回给下游客户端的响应,以及(如果发生了异常)相应的Throwable。

supports(Class requestContextClass, Class responseClass, Class serverTypeClass)方法可以用来确定有关的处理器是否处理所提供类型的对象。如果没有被用户重写,它将返回true

在前面的方法调用中,RC表示RequestContext类型,RES表示客户端响应类型,而T表示返回的服务器类型。

3.14. Spring Cloud LoadBalancer统计

我们提供了一个名为MicrometerStatsLoadBalancerLifecycleLoadBalancerLifecycle Bean,它使用Micrometer来为负载均衡调用提供统计数据。

为了让这个Bean添加到你的应用上下文中,请将spring.cloud.loadbalancer.stats.micrometer.enabled的值设置为true,并有一个MeterRegistry可用(例如,通过添加Spring Boot Actuator到你的项目)。

MicrometerStatsLoadBalancerLifecycleMeterRegistry中注册了以下仪表:

  • loadbalancer.requests.active: 一个允许你监控每一个服务实例的当前活动请求数的仪表(服务实例数据可通过标签获得)。
  • loadbalancer.requests.success: 一个计时器,用于测量每一个负载均衡请求的执行时间,这些请求最终将响应传递给了底层客户端
  • loadbalancer.requests.failed: 衡量任何以异常结束的负载均衡请求的执行时间的计时器。
  • loadbalancer.requests.discard:衡量被丢弃的负载均衡请求数量的计数器,即负载均衡器没有检索到运行该请求的服务实例的请求。

关于服务实例、请求数据和响应数据的额外信息在任何时候都会通过标签添加到度量中。

对于一些实现,如BlockingLoadBalancerClient,请求和响应数据可能不可用,因为我们从参数中建立了通用类型,可能无法确定类型和读取数据。

当至少有一条记录被添加到某一仪表上时,该仪表才被登记在注册表上。

你可以通过添加MeterFilters进一步配置这些指标的行为(例如,添加发布百分比和柱状图)。

3.15. 配置单个LoadBalancerClients

单个负载均衡器客户端可以用不同的前缀spring.cloud.loadbalancer.clients.<clientId>进行单独配置,其中 clientId 是负载均衡器的名称。默认配置值可以在spring.cloud.loadbalancer. namespace中设置,并将与客户机特定值优先合并。

Example 5. application.yml

spring:
  cloud:
    loadbalancer:
      health-check:
        initial-delay: 1s
      clients:
        myclient:
          health-check:
            interval: 30s

YAML 复制 全屏

上述例子将产生一个合并的健康检查@ConfigurationProperties对象,初始延迟=1s,间隔=30s。

除了以下全局属性外,每个客户的配置属性对大部分属性都有效:

  • spring.cloud.loadbalancer.enabled - 全局启用或禁用负载平衡
  • spring.cloud.loadbalancer.retry.enabled - 全局启用或禁用负载均衡重试。如果你在全局范围内启用它,你仍然可以使用客户端前缀属性禁用特定客户端的重试,但不能反过来。
  • spring.cloud.loadbalancer.cache.enabled - 全局启用或禁用LoadBalancer的缓存。如果你全局启用它,你仍然可以通过创建一个自定义配置,在ServiceInstanceListSupplier委托层次结构中不包括CachingServiceInstanceListSupplier来禁用特定客户端的缓存,但反过来就不行了。
  • spring.cloud.loadbalancer.stats.micrometer.enabled - 全局启用或禁用LoadBalancer Micrometer指标

对于已经使用地图的属性,你可以在不使用client关键字的情况下为每个客户指定不同的值(例如,hintshealth-check.path),我们保留了这种行为,以便保持库的向后兼容。它将在下一个主要版本中被修改。