# 云原生应用程序

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

这些特性中的许多都包含在Spring Boot (opens new window)中, Spring Cloud 就是建立在这些特性上的。 Spring Cloud 以两个库的形式提供了更多的功能: Spring Cloud Context 和 Spring Cloud Commons。 Spring Cloud Context 为 Spring Cloud应用程序的ApplicationContext提供实用工具和特殊服务(Bootstrap 上下文、加密、刷新范围和环境端点)。 Spring Cloud Commons 是在不同的 Spring Cloud实现中使用的一组抽象和公共类(例如 Spring Cloud Netflix 和 Spring Cloud Consul)。

如果由于“非法密钥大小”而导致异常,并且使用了 Sun 的 JDK,则需要安装 JCE 的无限强度管辖权策略文件。有关更多信息,请参见以下链接:

将这些文件解压缩到 JDK/JRE/lib/security 文件夹中,用于你使用的任何版本的 JRE/JDK x64/x86。

Spring Cloud 是在非限制性的 Apache2.0 许可下发布的。
如果你想对文档的这一部分做出贡献,或者如果你发现了一个错误,那么你可以在{docslink}[github]上找到该项目的源代码并发布该项目的追踪器。

# 1. Spring Cloud Context: Application Context Services

Spring 对于如何使用 Spring 构建应用程序,Boot 持有一种固执己见的观点。例如,它具有用于公共配置文件的常规位置,并且具有用于公共管理和监视任务的端点。 Spring Cloud在此基础上构建,并添加了一些系统中的许多组件将使用或偶尔需要的功能。

# 1.1.引导程序应用程序上下文

Spring Cloud应用程序通过创建“引导”上下文来操作,该上下文是主应用程序的父上下文。这个上下文负责从外部源加载配置属性,并对本地外部配置文件中的属性进行解密。这两个上下文共享一个Environment,它是任何 Spring 应用程序的外部属性的源。默认情况下,Bootstrap 属性(不是bootstrap.properties,而是在 Bootstrap 阶段加载的属性)是以较高的优先级添加的,因此它们不能被本地配置覆盖。

与主应用程序上下文相比,引导程序上下文使用不同的约定来定位外部配置。而不是application.yml(或.properties),你可以使用bootstrap.yml,从而将引导程序和主上下文的外部配置很好地分开。下面的清单展示了一个示例:

示例 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.应用程序上下文层次结构

如果你从SpringApplicationSpringApplicationBuilder构建应用程序上下文,则引导程序上下文将作为父上下文添加到该上下文。 Spring 的一个特性是,子上下文从其父上下文继承属性源和配置文件,因此,与在没有 Spring Cloud配置的情况下构建相同的上下文相比,“主”应用程序上下文包含额外的属性源。额外的财产来源是:

  • “bootstrap”:如果在 bootstrap 上下文中找到任何PropertySourceLocators,并且它们具有非空属性,则会以高优先级出现可选的CompositePropertySource。一个例子是来自 Spring Cloud Config 服务器的属性。有关如何自定义此属性源的内容,请参见“自定义引导程序属性源”。

  • “ApplicationConfig:[ Classpath:bootstrap.yml]”(如果 Spring profiles 处于活动状态,则包含相关文件):如果你有bootstrap.yml(或.properties),则这些属性将用于配置 bootstrap 上下文。然后,当设置其父上下文时,它们被添加到子上下文中。它们的优先级低于application.yml(或.properties)和作为创建 Spring 引导应用程序过程的正常部分添加到子级的任何其他属性源。有关如何自定义这些属性源的内容,请参见“更改 BootStrap 属性的位置”。

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

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

请注意,SpringApplicationBuilder允许你在整个层次结构中共享Environment,但这不是默认的。因此,兄弟上下文(尤其是)不需要具有相同的概要文件或属性源,即使它们可能与父上下文共享公共值。

# 1.3.更改 BootStrap 属性的位置

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。实际上,它们是用来设置 BootstrapApplicationContext的,方法是在其Environment中设置这些属性。如果有一个活动配置文件(从spring.profiles.active或通过你正在构建的上下文中的EnvironmentAPI),则该配置文件中的属性也将被加载,这与常规 Spring 启动应用程序中的属性相同——例如,对于development配置文件,从bootstrap-development.properties开始。

# 1.4.重写远程属性的值

通过引导程序上下文添加到应用程序中的属性源通常是“远程”的(例如,来自 Spring Cloud Config Server)。默认情况下,不能在本地重写它们。如果你想让你的应用程序用它们自己的系统属性或配置文件重写远程属性,那么远程属性源必须通过设置spring.cloud.config.allowOverride=true来授予它权限(在本地设置它是不起作用的)。一旦设置了该标志,两个更细粒度的设置将控制远程属性相对于系统属性和应用程序本地配置的位置:

  • spring.cloud.config.overrideNone=true:覆盖任何本地属性源。

  • spring.cloud.config.overrideSystemProperties=false:只有系统属性、命令行参数和环境变量(但不包括本地配置文件)应覆盖远程设置。

# 1.5.自定义引导程序配置

通过在名为org.springframework.cloud.bootstrap.BootstrapConfiguration的键下向/META-INF/spring.factories添加条目,可以将引导程序上下文设置为执行任何你喜欢的操作。这包含一个用逗号分隔的列表 Spring @Configuration类,这些类用于创建上下文。可以在这里创建你希望在主应用程序上下文中可用以进行自动连接的任何 bean。有一份@BeansApplicationContextInitializer的特别合同。如果要控制启动序列,可以使用@Order注释标记类(默认顺序是last)。

在添加自定义BootstrapConfiguration时,请注意,你添加的类并不是错误地将@ComponentScanned添加到你的“主”应用程序上下文中,
对引导配置类使用一个单独的包名,并确保该名称尚未被@ComponentScan@SpringBootApplication注释的配置类覆盖。

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

# 1.6.自定义引导程序属性源

由 BootStrap 进程添加的用于外部配置的默认属性源是 Spring Cloud Config 服务器,但是你可以通过将类型为PropertySourceLocator的 bean 添加到 BootStrap 上下文(通过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"));
    }

}

传入的Environment是即将被创建的ApplicationContext的属性——换句话说,是我们为其提供额外属性源的属性。它已经具有其正常的 Spring 引导提供的属性源,因此你可以使用这些源来定位特定于Environment的属性源(例如,通过在spring.application.name上键入它,就像在默认的 Spring Cloud Config Server 属性源定位器中所做的那样)。

如果在其中创建一个 jar,然后添加一个包含以下设置的META-INF/spring.factories,则customProperty``PropertySource将出现在其 Classpath 上包含该 jar 的任何应用程序中:

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

# 1.7.日志配置

如果你使用 Spring 引导来配置日志设置,那么如果你希望将此配置应用于所有事件,则应将其放置在bootstrap.[yml | properties]中。

对于 Spring Cloud 要正确初始化日志配置,不能使用自定义前缀。例如,在初始化日志系统时,使用不被 Spring Cloud 识别。

# 1.8.环境变化

应用程序监听EnvironmentChangeEvent并以两种标准方式对更改做出反应(额外的ApplicationListeners可以以正常方式添加为@Beans)。当观察到EnvironmentChangeEvent时,它具有已更改的键值列表,应用程序将这些值用于:

  • 在上下文中重新绑定任何@ConfigurationPropertiesbean。

  • logging.level.*中为任何属性设置记录器级别。

请注意, Spring Cloud Config 客户机在默认情况下不会轮询Environment中的更改。通常,我们不建议使用这种方法来检测更改(尽管你可以使用@Scheduled注释对其进行设置)。如果你有一个扩展的客户机应用程序,那么最好向所有实例广播EnvironmentChangeEvent,而不是让它们轮询更改(例如,通过使用Spring Cloud Bus (opens new window))。

EnvironmentChangeEvent涵盖了一大类刷新用例,只要你可以实际对Environment进行更改并发布事件。请注意,这些 API 是公共的,并且是 CORE 的一部分 Spring)。你可以通过访问/configprops端点(一种标准的 Spring 引导执行器功能)来验证这些更改是否绑定到@ConfigurationPropertiesbean。例如,DataSource可以在运行时更改其maxPoolSize(由 Spring 引导创建的默认DataSource@ConfigurationProperties Bean)并动态地增加容量。重新绑定@ConfigurationProperties并不包括另一大类用例,其中你需要对刷新进行更多控制,并且需要对整个ApplicationContext进行原子级更改。为了解决这些问题,我们有@RefreshScope

# 1.9.刷新范围

当存在配置更改时,标记为@RefreshScope的 Spring @Bean得到特殊处理。这个特性解决了有状态 bean 仅在初始化时才注入配置的问题。例如,如果当通过Environment更改数据库 URL 时,DataSource具有打开的连接,那么你可能希望这些连接的持有者能够完成他们正在做的事情。然后,下一次当某个对象从池中借用一个连接时,它将获得一个带有新 URL 的连接。

有时,在某些只能初始化一次的 bean 上应用@RefreshScope注释甚至是强制性的。如果 Bean 是“不可变的”,则必须用@RefreshScope注释 Bean,或者在属性键下指定类名:spring.cloud.refresh.extra-refreshable

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

Refresh Scope bean 是惰性代理,它在使用它们时(即调用方法时)进行初始化,并且作用域充当初始化值的缓存。要强制 Bean 在下一个方法调用时重新初始化,你必须使其缓存条目无效。

RefreshScope是上下文中的 Bean,并具有一个公共refreshAll()方法,可以通过清除目标缓存来刷新范围内的所有 bean。/refresh端点公开此功能(通过 HTTP 或 JMX)。要按名称刷新单个 Bean,还存在refresh(String)方法。

要公开/refresh端点,需要向应用程序添加以下配置:

management:
  endpoints:
    web:
      exposure:
        include: refresh
@RefreshScope@Configuration类上工作(技术上),但它可能会导致令人惊讶的行为。
例如,这并不意味着在该类中定义的所有@Beans本身都在@RefreshScope中。
特别地,任何依赖于这些 bean 的东西都不能依赖于它们在启动刷新时被更新,除非它本身在@RefreshScope中。
在这种情况下,它在刷新时被重建,并且它的依赖项被重新注入。
在这一点上,它们是从刷新的@Configuration中重新初始化的。

# 1.10.加密和解密

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

如果由于“非法密钥大小”而导致异常,并且使用了 Sun 的 JDK,则需要安装 JCE 的无限强度管辖权策略文件。有关更多信息,请参见以下链接:

将这些文件解压缩到 JDK/JRE/lib/security 文件夹中,用于你使用的任何版本的 JRE/JDK x64/x86。

# 1.11.端点

Spring 对于引导执行器应用程序,一些额外的管理端点是可用的。你可以使用:

  • POST/actuator/env来更新Environment并重新绑定@ConfigurationProperties和日志级别。要启用此端点,你必须设置management.endpoint.env.post.enabled=true

  • /actuator/refresh重新加载引导表带上下文并刷新@RefreshScopebean。

  • /actuator/restart关闭ApplicationContext并重新启动它(默认禁用)。

  • /actuator/pause/actuator/resume用于在ApplicationContext上调用Lifecycle方法(stop()start())。

如果禁用/actuator/restart端点,则/actuator/pause/actuator/resume端点
也将被禁用,因为它们只是/actuator/restart的特殊情况。

# 2. Spring Cloud Commons: Common Abstractions

诸如服务发现、负载平衡和断路器之类的模式将自身扩展到一个公共抽象层,该抽象层可以被所有 Spring Cloud客户机使用,而不依赖于实现(例如,使用 Eureka 或 Consul 的发现)。

# [2.1。@EnableDiscoveryClient注释](#discovery-client)

Spring Cloud Commons 提供了@EnableDiscoveryClient注释。这将寻找DiscoveryClientReactiveDiscoveryClientMETA-INF/spring.factories接口的实现方式。发现客户端的实现在org.springframework.cloud.client.discovery.EnableDiscoveryClient键下向spring.factories添加一个配置类。DiscoveryClient实现的示例包括Spring Cloud Netflix Eureka (opens new window)Spring Cloud Consul Discovery (opens new window)Spring Cloud Zookeeper Discovery (opens new window)

Spring 默认情况下,云将同时提供阻塞和反应服务发现客户端。通过设置spring.cloud.discovery.blocking.enabled=falsespring.cloud.discovery.reactive.enabled=false,你可以轻松地禁用阻塞和/或反应客户端。要完全禁用服务发现,只需设置spring.cloud.discovery.enabled=false

默认情况下,DiscoveryClient的实现方式是用远程发现服务器自动注册本地 Spring 引导服务器。可以通过在@EnableDiscoveryClient中设置autoRegister=false来禁用此行为。

@EnableDiscoveryClient不再是必需的。
你可以在 Classpath 上放置一个DiscoveryClient实现,以使 Spring 引导应用程序向服务发现服务器注册。

# 2.1.1.健康指标

Commons 自动配置以下 Spring 引导健康指示器。

# 发现潜在的指示剂

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

  • 要完全禁用,请设置spring.cloud.discovery.client.health-indicator.enabled=false

  • 要禁用 description 字段,请设置spring.cloud.discovery.client.health-indicator.include-description=false。否则,它可以像卷起的descriptionHealthIndicator那样冒泡。

  • 要禁用服务检索,请设置spring.cloud.discovery.client.health-indicator.use-services-query=false。默认情况下,指示器调用客户机的getServices方法。在使用许多注册服务的部署中,在每次检查期间检索所有服务的成本可能太高。这将跳过服务检索,而是使用客户机的probe方法。

# 发现复合健康贡献者

这个复合健康指示器基于所有注册的DiscoveryHealthIndicatorbean。要禁用,请设置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.orderfor Eureka)属性设置为所需的值。

# 2.1.3.简单的发现

如果在 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)等方法,这些方法允许你提供自定义注册服务。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实现。

  • ZookeeperRegistrationZookeeperServiceRegistry连用

  • EurekaRegistrationEurekaServiceRegistry连用

  • ConsulRegistrationConsulServiceRegistry连用

如果你正在使用ServiceRegistry接口,则需要为你正在使用的ServiceRegistry实现传递正确的Registry实现。

# 2.2.1.ServiceRegistry 自动注册

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

# ServiceRegistry 自动注册事件

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

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

# 2.2.2.服务注册中心执行器端点

Spring Cloud Commons 提供了/service-registry执行器端点。这个端点依赖于 Spring 应用程序上下文中的Registration Bean。用 get 调用/service-registry返回Registration的状态。使用 POST 到带有 JSON 主体的相同端点将当前Registration的状态更改为新值。JSON 主体必须包含带有首选值的status字段。请参阅ServiceRegistry实现的文档,该实现用于更新状态时所允许的值以及为状态返回的值。例如,Eureka 支持的状态是UPDOWNOUT_OF_SERVICEUNKNOWN

# 2.3. Spring RestTemplate as a Load Balancer Client

你可以将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 starter添加到你的项目中才能使用它。

# 2.4. Spring WebClient as a Load Balancer Client

你可以将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负载平衡器用于创建完整的物理地址。

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

# 2.4.1.重试失败的请求

可以将负载平衡的RestTemplate配置为重试失败的请求。默认情况下,此逻辑是禁用的。对于非反应性版本(带有RestTemplate),你可以通过在应用程序的 Classpath 中添加Spring Retry (opens new window)来启用它。对于反应式版本(带有WebTestClient), you need to set Spring.cloud.loadBalancer.retry.enabled=true`)。

如果希望在 Classpath 上使用 Spring 重试或反应式重试禁用重试逻辑,则可以设置spring.cloud.loadbalancer.retry.enabled=false

对于非反应性实现,如果你希望在重试中实现BackOffPolicy,则需要创建类型LoadBalancedRetryFactory的 Bean 并覆盖createBackOffPolicy()方法。

对于反应式实现,你只需要通过将spring.cloud.loadbalancer.retry.backoff.enabled设置为false来启用它。

你可以设置:

  • spring.cloud.loadbalancer.retry.maxRetriesOnSameServiceInstance-表示在同一个ServiceInstance上应该重试请求多少次(对于每个选定的实例,单独计算)

  • spring.cloud.loadbalancer.retry.maxRetriesOnNextServiceInstance-表示新选择的ServiceInstance请求应该重试多少次

  • spring.cloud.loadbalancer.retry.retryableStatusCodes-总是要重试失败请求的状态代码。

对于反应式实现,你可以另外设置:

  • spring.cloud.loadbalancer.retry.backoff.minBackoff-设置最小退避持续时间(默认情况下为 5 毫秒)
  • spring.cloud.loadbalancer.retry.backoff.maxBackoff-设置最大退避持续时间(默认情况下,最大长值为毫秒)
  • spring.cloud.loadbalancer.retry.backoff.jitter-设置用于计算的抖动 g 每个调用的实际退避持续时间(默认情况下为 0.5)。

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

单独的 loadbalancer 客户机可以单独配置,具有与上面相同的属性,但前缀是spring.cloud.loadbalancer.clients.<clientId>.*,其中clientId是 loadbalancer 的名称。
对于负载平衡的重试,默认情况下,我们用ServiceInstanceListSupplier Bean 包装RetryAwareServiceInstanceListSupplier,以从先前选择的实例中选择不同的实例(如果可用的话)。可以通过将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对象](#multiple-resttemplate-objects)

如果你想要一个不是负载平衡的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 WebFluxWebClient作为负载均衡器客户端]

Spring WebFlux 可以同时处理反应性和非反应性WebClient配置,正如主题所描述的那样:

  • [ Spring WebFluxWebClientwithReactorLoadBalancerExchangeFilterFunction](#WebFlux-with-ractive-loadBalancer)

  • [[负载平衡器-交换-过滤器-功能负载平衡器-交换-过滤器-功能]](# 负载平衡器-交换-过滤器-功能负载平衡器-交换-过滤器-功能)

# [2.7.1。 Spring WebFluxWebClientwithReactorLoadBalancerExchangeFilterFunction](#WebFlux-with-ractive-loadBalancer)

你可以将WebClient配置为使用ReactiveLoadBalancer。如果将Spring Cloud LoadBalancer starter添加到项目中,并且spring-webflux位于 Classpath 上,则ReactorLoadBalancerExchangeFilterFunction将自动配置。下面的示例展示了如何配置WebClient以使用无功负载均衡器:

public class MyClass {
    @Autowired
    private ReactorLoadBalancerExchangeFilterFunction lbFunction;

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

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

# [2.7.2。 Spring WebFluxWebClient带有无功负载均衡器的客户端](# 负载均衡器-交换-过滤器-函数)

如果spring-webflux在 Classpath 上,则LoadBalancerExchangeFilterFunction是自动配置的。然而,请注意,这使用了一个无反应的客户端。下面的示例展示了如何配置WebClient以使用负载均衡器:

public class MyClass {
    @Autowired
    private LoadBalancerExchangeFilterFunction lbFunction;

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

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

警告:这种方法现在已经过时了。我们建议你使用带无功负载均衡器的 WebFlux代替。

# 2.8.忽略网络接口

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

示例 2.application.yml

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

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

示例 3.bootstrap.yml

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

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

示例 4.application.yml

spring:
  cloud:
    inetutils:
      useOnlySiteLocalInterfaces: true

有关什么是站点本地地址的更多详细信息,请参见iNet4address.html.issitelocaladdress() (opens new window)

# 2.9.HTTP 客户端工厂

Spring Cloud Commons 提供了用于同时创建 Apache HTTP 客户端()和 OK HTTP 客户端()的 bean。只有当确定的 HTTP jar 在 Classpath 上时,才会创建OkHttpClientFactory Bean。此外, Spring Cloud Commons 提供了用于创建两个客户端使用的连接管理器的 bean:用于 Apache HTTP 客户端的和用于 OK HTTP 客户端的。如果你想定制如何在下游项目中创建 HTTP 客户机,那么你可以提供你自己的这些 bean 的实现。此外,如果你提供了类型HttpClientBuilderOkHttpClient.Builder的 Bean,则默认工厂将这些构建器用作将构建器返回到下游项目的基础。还可以通过将spring.cloud.httpclientfactories.apache.enabledspring.cloud.httpclientfactories.ok.enabled设置为false来禁用这些 bean 的创建。

# 2.10.已启用的功能

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

# 2.10.1.特征类型

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

抽象特性是定义了接口或抽象类并创建了实现的特性,例如DiscoveryClientLoadBalancerClientLockService。抽象类或接口用于在上下文中查找该类型的 Bean。显示的版本是bean.getClass().getPackage().getImplementationVersion()

命名特性是指不具有它们实现的特定类的特性。这些功能包括“断路器”、“API 网关”、“ Spring Cloud总线”等。这些特征需要一个名称和 Bean 类型。

# 2.10.2.声明功能

任何模块都可以声明任意数量的HasFeaturebean,如下例所示:

@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();
}

这些 bean 中的每一个都应该有适当的保护@Configuration

# 2.11. Spring Cloud Compatibility Verification

由于一些用户在设置 Spring Cloud应用程序时存在问题,因此我们决定添加一个兼容性验证机制。如果你当前的设置与 Spring Cloud需求不兼容,那么它将会中断,同时还会出现一份报告,显示出到底出了什么问题。

目前,我们正在验证将哪个版本的 Spring 启动添加到你的 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 启动版本,只需用逗号分隔的兼容 Spring 启动版本列表设置spring.cloud.compatibility-verifier.compatible-boot-versions属性。

# 3. Spring Cloud LoadBalancer

Spring Cloud提供了其自己的客户端负载均衡器的抽象和实现。对于负载平衡机制,添加了ReactiveLoadBalancer接口,并为其提供了基于循环随机实现方式。为了得到要从反应中选择的实例ServiceInstanceListSupplier是使用的。目前,我们支持基于服务发现的ServiceInstanceListSupplier实现,该实现使用 Classpath 中可用的发现客户端从服务发现中检索可用实例。

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

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

默认情况下使用的ReactiveLoadBalancer实现是RoundRobinLoadBalancer。要切换到不同的实现,对于选定的服务或所有服务,可以使用自定义负载平衡器配置机制

例如,可以通过@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 integrations

Spring 为了使云负载平衡器易于使用,我们提供了ReactorLoadBalancerExchangeFilterFunction可以与WebClientBlockingLoadBalancerClient一起使用的RestTemplate。你可以在以下部分中看到更多信息和使用示例:

# 3.3. Spring Cloud LoadBalancer Caching

除了基本的ServiceInstanceListSupplier实现外,我们还提供了两种缓存实现,这种实现在每次必须选择实例时都通过DiscoveryClient检索实例。

# 3.3.1. Caffeine (opens new window)-支持 loadBalancer 缓存实现

如果在 Classpath 中有,则将使用基于咖啡因的实现方式。有关如何配置它的信息,请参见负荷平衡状态部分。

如果你正在使用咖啡因,还可以通过在spring.cloud.loadbalancer.cache.caffeine.spec属性中传递你自己的咖啡因规格 (opens new window)来覆盖负载平衡器的默认咖啡因缓存设置。

警告:传递你自己的咖啡因规范将覆盖任何其他 loadBalancerCache 设置,包括通用负载平衡器缓存配置字段,例如ttlcapacity

# 3.3.2.默认的 LoadBalancer 缓存实现

如果在 Classpath 中没有咖啡因,则将使用DefaultLoadBalancerCache,它自动带有spring-cloud-starter-loadbalancer。有关如何配置它的信息,请参见负荷平衡状态部分。

要使用咖啡因而不是默认的缓存,请在 Classpath 中添加com.github.ben-manes.caffeine:caffeine依赖项。

# 3.3.3.LoadBalancer 缓存配置

你可以将自己的ttl值(写完之后条目应该过期的时间)表示为Duration,方法是将符合[ Spring bootStringString转换语法传递到Duration转换语法](https://DOCS. Spring. Spring-boot/DOCS/current/reference/html/ Spring-boot-features.html-externets-#confeatures-conversion-duration)。还可以通过设置spring.cloud.loadbalancer.cache.capacity属性的值来设置自己的 LoadBalancer 缓存初始容量。

默认设置包括将ttl设置为 35 秒,而默认的initialCapacity256

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

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

# 3.4.基于区域的负载平衡

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

你还可以通过设置spring.cloud.loadbalancer.zone属性的值来覆盖DiscoveryClient特定的区域设置。
目前,只有 Eureka 发现客户机被检测来设置 loadBalancer 区域。对于其他发现客户端,设置spring.cloud.loadbalancer.zone属性。不久还会有更多的测试。
为了确定检索到的ServiceInstance的区域,我们检查其元数据映射中"zone"键下的值。

ZonePreferenceServiceInstanceListSupplier过滤检索到的实例,并且只返回同一区域内的实例。如果区域是null,或者同一区域内没有实例,则返回所有检索到的实例。

为了使用基于区域的负载平衡方法,你必须在自定义配置中实例化ZonePreferenceServiceInstanceListSupplier Bean。

我们使用委托来处理ServiceInstanceListSupplierbean。我们建议在ZonePreferenceServiceInstanceListSupplier的构造函数中传递一个DiscoveryClientServiceInstanceListSupplier委托,然后用CachingServiceInstanceListSupplier包装后者,以利用负载平衡器缓存机制

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

public class CustomLoadBalancerConfiguration {

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

# 3.5.LoadBalancer 的实例健康检查

可以为 loadBalancer 启用计划的 HealthCheck。为此提供了HealthCheckServiceInstanceListSupplier。它会定期验证委托ServiceInstanceListSupplier提供的实例是否还活着,并且只返回健康的实例,除非没有-然后返回所有检索到的实例。

这种机制在使用SimpleDiscoveryClient时特别有用。对于由实际服务注册中心支持的
客户机,没有必要使用它,因为在查询外部服务发现之后,我们已经获得了
健康的实例。
对于每个服务
具有少量实例的设置,也建议使用此供应商,以避免在失败的实例上重试调用。
如果使用任何服务发现支持的供应商,通常不需要添加这种健康检查机制,因为我们直接从服务注册中心检索实例的健康状态
HealthCheckServiceInstanceListSupplier依赖于由委托通量提供的更新实例。在极少数情况下,当你希望使用不刷新实例的委托时,即使实例列表可能会更改(例如我们提供的DiscoveryClientServiceInstanceListSupplier),你可以将spring.cloud.loadbalancer.health-check.refetch-instances设置为true,以便通过HealthCheckServiceInstanceListSupplier刷新实例列表。然后,你还可以通过修改spring.cloud.loadbalancer.health-check.refetch-instances-interval的值来调整刷新间隔,并 OPT 通过将spring.cloud.loadbalancer.health-check.repeat-health-check设置为false来禁用额外的 HealthCheck 重复,因为每个实例 refetch
也将触发 HealthCheck。

HealthCheckServiceInstanceListSupplier使用带spring.cloud.loadbalancer.health-check前缀的属性。你可以为调度程序设置initialDelayinterval。你可以通过设置spring.cloud.loadbalancer.health-check.path.default属性的值来设置 HealthCheck URL 的默认路径。还可以通过设置spring.cloud.loadbalancer.health-check.path.[SERVICE_ID]属性的值,用服务的正确 ID 替换[SERVICE_ID],为任何给定的服务设置特定值。如果没有指定[SERVICE_ID],则默认使用/actuator/health。如果[SERVICE_ID]被设置为null或作为一个值为空,那么将不执行健康检查。还可以通过设置spring.cloud.loadbalancer.health-check.port的值来为健康检查请求设置自定义端口。如果没有设置,则请求的服务在服务实例中可用的端口。

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

为了使用健康检查计划程序方法,你必须在自定义配置中实例化HealthCheckServiceInstanceListSupplier Bean。

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

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

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                    .withDiscoveryClient()
                    .withHealthChecks()
                    .build(context);
        }
    }
对于非反应堆栈,使用withBlockingHealthChecks()创建此供应商。
你还可以传递你自己的WebClientRestTemplate实例用于检查。
HealthCheckServiceInstanceListSupplier有自己的基于反应堆流量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);
        }
    }
这也是 ZooKeeperStickyRule的替代品。

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

你可以以这样一种方式设置 loadBalancer,即它更喜欢请求 cookie 中提供的带有instanceId的实例。如果请求通过ClientRequestContextServerHttpRequestContext传递到负载平衡器,我们目前支持这一点,SC 负载平衡器交换过滤器函数和过滤器使用这些函数。

为此,你需要使用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 Hints

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

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

# 3.9.基于提示的负载平衡

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

HintBasedServiceInstanceListSupplier检查提示请求标头(默认标头名称为X-SC-LB-Hint,但可以通过更改spring.cloud.loadbalancer.hint-header-name属性的值来修改它),如果它发现了提示请求标头,则使用标头中传递的提示值来过滤服务实例。

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

如果没有设置任何提示,则返回委托提供的所有服务实例,不管是通过头还是通过属性。

在筛选过程中,HintBasedServiceInstanceListSupplier查找在hint键下具有匹配值集的服务实例。如果没有找到匹配的实例,则返回委托提供的所有实例。

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

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 启动应用程序中轻松添加 Spring Cloud LoadBalancer。为了使用它,只需在构建文件中的 Spring Cloud依赖项中添加org.springframework.cloud:spring-cloud-starter-loadbalancer

Spring Cloud负载平衡器启动器包括Spring Boot Caching (opens new window)Evictor (opens new window)

# 3.12. Passing Your Own Spring Cloud LoadBalancer Configuration

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

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

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

TIP

为了使在自己的 loadBalancer 配置上工作更容易,我们向ServiceInstanceListSupplier类添加了一个builder()方法。

TIP

你还可以将spring.cloud.loadbalancer.configurations属性的值设置为zone-preference,从而在缓存中使用ZonePreferenceServiceInstanceListSupplier,或者在缓存中使用health-check,从而替代默认的预定义配置。

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

你可以看到一个自定义配置here的示例。

注释value参数(在上面的示例中为stores)指定了我们应该通过给定的自定义配置向其发送请求的服务的服务 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 Lifecycle

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对象作为参数。它包含用于选择适当实例的数据,包括下游客户机请求和hintonStartRequest也接受Request对象,另外,Response<T>对象作为参数。另一方面,将CompletionContext对象提供给onComplete(CompletionContext<RES, T, RC> completionContext)方法。它包含 loadBalancerResponse,包括所选择的服务实例、针对该服务实例执行的请求的Status和(如果可用的话)返回到下游客户端的响应,以及(如果发生了异常)相应的Throwable

supports(Class requestContextClass, Class responseClass, Class serverTypeClass)方法可用于确定问题处理器是否处理所提供类型的对象。如果未被用户重写,则返回true

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

# 3.14. Spring Cloud LoadBalancer Statistics

我们提供了一个名为LoadBalancerLifecycle Bean 的MicrometerStatsLoadBalancerLifecycle,它使用 Micrometer 为负载平衡调用提供统计信息。

为了将此 Bean 添加到你的应用程序上下文中,将spring.cloud.loadbalancer.stats.micrometer.enabled的值设置为true,并使MeterRegistry可用(例如,通过向你的项目添加Spring Boot Actuator (opens new window))。

MicrometerStatsLoadBalancerLifecycleMeterRegistry中记录以下仪表:

  • loadbalancer.requests.active:允许你监视任何服务实例当前活动请求的数量(通过标记可获得的服务实例数据)的度量标准;

  • loadbalancer.requests.success:一个计时器,用于度量以将响应传递给底层客户端而结束的任何负载平衡请求的执行时间;

  • loadbalancer.requests.failed:一个计时器,用于度量任何负载平衡请求的执行时间,这些请求以异常结束;

  • loadbalancer.requests.discard:一种计数器,用于测量被丢弃的负载平衡请求的数量,即负载平衡器尚未检索到要在其上运行请求的服务实例的请求。

有关服务实例、请求数据和响应数据的附加信息将随时通过标记添加到度量中。

对于某些实现方式,例如BlockingLoadBalancerClient,请求和响应数据可能不可用,因为我们从参数建立泛型类型,并且可能无法确定类型和读取数据。
当一个给定的计价器至少增加了一条记录时,计价器在注册表中进行注册。
你可以通过[添加MeterFilters](https://DOCS. Spring.io/ Spring-boot/DOCS/current/reference/html/production-ready-features.html#production-ready-metrics-per-meter-properties)来进一步配置这些指标的行为(例如,添加发布百分位和直方图 (opens new window))。

# 3.15.配置单个 loadbalancerclient

单独的 loadbalancer 客户机可以单独配置不同的前缀spring.cloud.loadbalancer.clients.<clientId>.,其中clientId是 loadbalancer 的名称。默认配置值可以在spring.cloud.loadbalancer. 名称空间中设置,并将优先与客户机特定的值合并。

示例 5.application.yml

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

上面的示例将产生一个合并的健康检查@ConfigurationProperties对象,其中initial-delay=1sinterval=30s

除了以下全局属性外,每个客户机配置属性对大多数属性都有效:

  • spring.cloud.loadbalancer.enabled-全局启用或禁用负载平衡

  • spring.cloud.loadbalancer.retry.enabled-全局启用或禁用负载平衡重试。如果全局启用它,仍然可以使用client-前缀属性禁用特定客户机的重试,但不能使用相反的方法

  • spring.cloud.loadbalancer.cache.enabled-全局启用或禁用 LoadBalancer 缓存。如果你在全局范围内启用它,那么你仍然可以通过创建自定义配置来禁用特定客户机的缓存,该命令不包括CachingServiceInstanceListSupplier委托层次结构中的CachingServiceInstanceListSupplier,但不是相反。

  • spring.cloud.loadbalancer.stats.micrometer.enabled-全局启用或禁用负载平衡器千分尺指标

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

# 4. Spring Cloud Circuit Breaker

# 4.1.导言

Spring Cloud断路器提供了跨越不同断路器实现方式的抽象。它提供了在应用程序中使用的一致的 API,允许你(开发人员)选择最适合你的应用程序需求的断路器实现。

# 4.1.1.支持的实现

Spring Cloud支持以下断路器实现方式:

# 4.2.核心概念

要在代码中创建断路器,可以使用CircuitBreakerFactoryAPI。当你在 Classpath 上包含 Spring Cloud断路器启动器时,将自动为你创建实现此 API 的 Bean。下面的示例展示了如何使用此 API 的一个简单示例:

@Service
public static class DemoControllerService {
    private RestTemplate rest;
    private CircuitBreakerFactory cbFactory;

    public DemoControllerService(RestTemplate rest, CircuitBreakerFactory cbFactory) {
        this.rest = rest;
        this.cbFactory = cbFactory;
    }

    public String slow() {
        return cbFactory.create("slow").run(() -> rest.getForObject("/slow", String.class), throwable -> "fallback");
    }

}

CircuitBreakerFactory.createAPI 创建了一个名为CircuitBreaker的类的实例。run方法接受SupplierFunctionSupplier是要在断路器中封装的代码。Function是当断路器跳闸时运行的回退。传递函数Throwable,从而触发回退。如果你不想提供备份,则可以选择排除备份。

# 4.2.1.无功码中的断路器

如果 Project Reactor 在类路径上,你也可以使用ReactiveCircuitBreakerFactory作为你的反应代码。下面的示例展示了如何做到这一点:

@Service
public static class DemoControllerService {
    private ReactiveCircuitBreakerFactory cbFactory;
    private WebClient webClient;

    public DemoControllerService(WebClient webClient, ReactiveCircuitBreakerFactory cbFactory) {
        this.webClient = webClient;
        this.cbFactory = cbFactory;
    }

    public Mono<String> slow() {
        return webClient.get().uri("/slow").retrieve().bodyToMono(String.class).transform(
        it -> cbFactory.create("slow").run(it, throwable -> return Mono.just("fallback")));
    }
}

ReactiveCircuitBreakerFactory.createAPI 创建了一个名为ReactiveCircuitBreaker的类的实例。run方法获取MonoFlux并将其封装在断路器中。你可以选择配置一个回退Function,如果断路器跳闸并通过导致故障的Throwable,将调用该回退。

# 4.3.配置

你可以通过创建类型Customizer的 bean 来配置断路器。Customizer接口有一个用于自定义Object的方法(称为customize)。

有关如何定制给定实现的详细信息,请参见以下文档:

一些CircuitBreaker实现方式如Resilience4JCircuitBreaker每次调用customize方法都调用CircuitBreaker#run。这可能是低效的。在这种情况下,可以使用CircuitBreaker#once方法。在多次调用customize没有意义的情况下,例如在消费弹性 4J 的事件 (opens new window)的情况下,它是有用的。

下面的示例显示了每个io.github.resilience4j.circuitbreaker.CircuitBreaker消耗事件的方式。

Customizer.once(circuitBreaker -> {
  circuitBreaker.getEventPublisher()
    .onStateTransition(event -> log.info("{}: {}", event.getCircuitBreakerName(), event.getStateTransition()));
}, CircuitBreaker::getName)

# 5.cachedrandomPropertySource

Spring Cloud上下文提供了一个PropertySource,该PropertySource基于键缓存随机值。在缓存功能之外,它的工作原理与 Spring boot 的[RandomValuePropertySource](https://github.com/ Spring-projects/ Spring-boot/blob/main/ Spring-boot-project/ Spring-boot/SRC/main/java/org/springframework/boot/ENV/randomvalutypropertysource.java)相同。如果你想要一个即使在 Spring 应用程序上下文重新启动之后仍然保持一致的随机值,那么这个随机值可能是有用的。属性值采取cachedrandom.[yourkey].[type]的形式,其中yourkey是缓存中的键。type值可以是 Spring boot 的RandomValuePropertySource支持的任何类型。

myrandom=${cachedrandom.appname.value}

# 6.安全

# 6.1.单点登录

在版本 1.3 中,所有的 OAuth2SSO 和资源服务器功能都移动到了 Spring boot
。你可以在Spring Boot user guide (opens new window)中找到文档。

# 6.1.1.客户端令牌中继

如果你的应用程序是一个面向 OAuth2 客户端的用户(即声明了@EnableOAuth2Sso@EnableOAuth2Client),那么它在 Spring 启动的请求范围内具有OAuth2ClientContext。你可以从这个上下文和一个自动连接的OAuth2RestTemplate创建自己的OAuth2ProtectedResourceDetails,然后上下文将始终向下游转发访问令牌,如果过期,还将自动刷新访问令牌。(这些是 Spring 安全性和 Spring 引导的功能。

# 6.1.2.资源服务器令牌中继

如果你的应用程序有@EnableResourceServer,那么你可能希望将传入的令牌向下游中继到其他服务。如果你使用RestTemplate来联系下游服务,那么这只是一个如何在正确的上下文中创建模板的问题。

如果你的服务使用UserInfoTokenServices来验证传入的令牌(即它正在使用security.oauth2.user-info-uri配置),那么你可以简单地使用自动连接OAuth2RestTemplate创建OAuth2RestTemplate(它将在到达后端代码之前由身份验证过程填充)。等效地(对于 Spring Boot1.4),你可以在配置中注入一个UserInfoRestTemplateFactory并获取其OAuth2RestTemplate。例如:

MyConfiguration.java

@Bean
public OAuth2RestTemplate restTemplate(UserInfoRestTemplateFactory factory) {
    return factory.getUserInfoRestTemplate();
}

然后,这个 REST 模板将具有与身份验证筛选器使用的相同的OAuth2ClientContext(请求作用域),因此你可以使用它发送具有相同访问令牌的请求。

如果你的应用程序没有使用UserInfoTokenServices,但仍然是一个客户端(即它声明@EnableOAuth2Client@EnableOAuth2Sso),那么使用 Spring 安全云,用户从@Autowired``OAuth2Context创建的任何OAuth2RestOperations也将转发令牌。默认情况下,此功能是作为 MVC 处理程序拦截器实现的,因此它仅在 Spring MVC 中工作。如果不使用 MVC,则可以使用自定义过滤器或 AOP 拦截器包装AccessTokenContextRelay来提供相同的功能。

下面是一个基本示例,展示了在其他地方创建的自动连线 REST 模板的使用情况(“foo.com”是一个资源服务器,接受与周围应用程序相同的令牌):

mycontroller.java

@Autowired
private OAuth2RestOperations restTemplate;

@RequestMapping("/relay")
public String relay() {
    ResponseEntity<String> response =
      restTemplate.getForEntity("https://foo.com/bar", String.class);
    return "Success! (" + response.getBody() + ")";
}

如果你不想转发令牌(这是一个有效的选择,因为你可能希望充当你自己的角色,而不是向你发送令牌的客户机),那么你只需要创建自己的OAuth2Context,而不是自动布线默认的。

假客户端还将获取使用OAuth2ClientContext的拦截器(如果可用的话),因此他们还应该在RestTemplate可以进行令牌中继的任何地方进行令牌中继。

# 7.配置属性

要查看所有 Spring Cloud Commons 相关配置属性的列表,请检查附录页