Spring Cloud 配置客户端

Spring Boot 应用程序可以立即利用 Spring 配置服务器(或应用程序开发者提供的其他外部属性源)。它还会获取一些与Environment更改事件相关的额外有用功能。

Spring Boot 配置数据导入

Spring Boot 2.4 引入了一种通过spring.config.import属性导入配置数据的新方法。这现在是绑定到配置服务器的默认方法。

要选择性地连接到配置服务器,请在 application.properties 中设置以下内容:

application.properties
spring.config.import=optional:configserver:

这将连接到默认位置为“https://127.0.0.1:8888”的配置服务器。删除optional:前缀将导致配置客户端在无法连接到配置服务器时失败。要更改配置服务器的位置,请设置spring.cloud.config.uri或将 url 添加到spring.config.import语句中,例如,spring.config.import=optional:configserver:http://myhost:8888。导入属性中的位置优先于 uri 属性。

Spring Boot 配置数据通过两步过程解析配置。首先,它使用default配置文件加载所有配置。这允许 Spring Boot 收集所有可能激活任何其他配置文件的配置。在收集所有激活的配置文件后,它将加载活动配置文件的任何其他配置。因此,您可能会看到向 Spring Cloud 配置服务器发出多个请求以获取配置。这是正常的,并且是使用spring.config.import时 Spring Boot 加载配置的副作用。在早期版本的 Spring Cloud 配置中,只有一个请求,但这意味着您无法从来自配置服务器的配置中激活配置文件。现在,仅使用“default”配置文件的额外请求使这成为可能。

对于通过spring.config.import导入的 Spring Boot 配置数据方法,**不需要**bootstrap文件(属性或 yaml)。

配置优先引导

要使用连接到配置服务器的旧版引导方式,必须通过属性或spring-cloud-starter-bootstrap启动器启用引导。该属性是spring.cloud.bootstrap.enabled=true。它必须设置为系统属性或环境变量。启用引导后,类路径上具有 Spring Cloud 配置客户端的任何应用程序都将按如下方式连接到配置服务器:当配置客户端启动时,它绑定到配置服务器(通过spring.cloud.config.uri引导配置属性)并使用远程属性源初始化 Spring Environment

此行为的最终结果是,所有想要使用配置服务器的客户端应用程序都需要一个bootstrap.yml(或环境变量),其中在spring.cloud.config.uri中设置了服务器地址(默认为“https://127.0.0.1:8888”)。

发现优先查找

除非您使用配置优先引导,否则您需要在配置属性中具有带有optional:前缀的spring.config.import属性。例如,spring.config.import=optional:configserver:

如果您使用DiscoveryClient实现,例如 Spring Cloud Netflix 和 Eureka 服务发现或 Spring Cloud Consul,您可以让配置服务器向发现服务注册。

如果您更喜欢使用DiscoveryClient来查找配置服务器,您可以通过设置spring.cloud.config.discovery.enabled=true来实现(默认为false)。例如,对于 Spring Cloud Netflix,您需要定义 Eureka 服务器地址(例如,在eureka.client.serviceUrl.defaultZone中)。使用此选项的代价是在启动时需要额外的网络往返来查找服务注册。好处是,只要发现服务是一个固定点,配置服务器就可以更改其坐标。默认服务 ID 是configserver,但您可以通过设置spring.cloud.config.discovery.serviceId(以及在服务器上,以服务通常的方式,例如通过设置spring.application.name)在客户端上更改它。

发现客户端实现都支持某种元数据映射(例如,对于 Eureka,我们有eureka.instance.metadataMap)。可能需要在配置服务器的服务注册元数据中配置配置服务器的一些其他属性,以便客户端能够正确连接。如果配置服务器使用 HTTP Basic 进行保护,则可以将凭据配置为userpassword。此外,如果配置服务器具有上下文路径,则可以设置configPath。例如,以下 YAML 文件适用于作为 Eureka 客户端的配置服务器

eureka:
  instance:
    ...
    metadataMap:
      user: osufhalskjrtl
      password: lviuhlszvaorhvlo5847
      configPath: /config

使用 Eureka 和 WebClient 的发现优先引导

如果您使用 Spring Cloud Netflix 中的 Eureka DiscoveryClient,并且想要使用 WebClient 而不是 Jersey 或 RestTemplate,则需要在类路径中包含 WebClient,并设置 eureka.client.webclient.enabled=true

配置客户端快速失败

在某些情况下,如果服务无法连接到配置服务器,您可能希望使其启动失败。如果需要此行为,请设置 bootstrap 配置属性 spring.cloud.config.fail-fast=true,使客户端以异常停止。

要使用 spring.config.import 获取类似的功能,只需省略 optional: 前缀。

配置客户端重试

如果您预计配置服务器在应用程序启动时可能偶尔不可用,您可以使其在失败后继续尝试。首先,您需要设置 spring.cloud.config.fail-fast=true。然后,您需要将 spring-retryspring-boot-starter-aop 添加到您的类路径中。默认行为是重试六次,初始回退间隔为 1000 毫秒,后续回退的指数乘数为 1.1。您可以通过设置 spring.cloud.config.retry.* 配置属性来配置这些属性(以及其他属性)。要使用随机指数回退策略,请将 spring.cloud.config.retry.useRandomPolicy 设置为 true

spring.cloud.config.retry.useRandomPolicytrue 时,即使使用随机指数回退策略,max-attemptsinitial-intervalmax-intervalmultiplier 属性仍然有效。有关如何使用它们的详细信息,请参阅 Spring Retry 中的 ExponentialRandomBackOffPolicyExponentialBackOffPolicy
要完全控制重试行为并使用旧版 bootstrap,请添加一个类型为 RetryOperationsInterceptor 且 ID 为 configServerRetryInterceptor@Bean。Spring Retry 有一个 RetryInterceptorBuilder 支持创建它。

使用 spring.config.import 进行配置客户端重试

重试适用于 Spring Boot spring.config.import 语句,并且正常的属性有效。但是,如果导入语句位于配置文件中(例如 application-prod.properties),则需要不同的方法来配置重试。配置需要作为 URL 参数放在导入语句中。

application-prod.properties
spring.config.import=configserver:http://configserver.example.com?fail-fast=true&max-attempts=10&max-interval=1500&multiplier=1.2&initial-interval=1100"

这设置了 spring.cloud.config.fail-fast=true(请注意上面缺少的前缀)和所有可用的 spring.cloud.config.retry.* 配置属性。

查找远程配置资源

配置服务从 /{application}/{profile}/{label} 提供属性源,其中客户端应用程序中的默认绑定如下所示

  • "application" = ${spring.application.name}

  • "profile" = ${spring.profiles.active}(实际上是 Environment.getActiveProfiles()

  • "label" = "master"

设置属性 ${spring.application.name} 时,不要在应用程序名称前添加保留字 application-,以防止解析正确的属性源时出现问题。

您可以通过设置 spring.cloud.config.*(其中 *nameprofilelabel)来覆盖所有这些属性。label 可用于回滚到配置的先前版本。使用默认的配置服务器实现,它可以是 Git 标签、分支名称或提交 ID。标签也可以作为逗号分隔的列表提供。在这种情况下,将逐一尝试列表中的项目,直到一个成功为止。在处理功能分支时,此行为非常有用。例如,您可能希望将配置标签与您的分支对齐,但使其可选(在这种情况下,使用 spring.cloud.config.label=myfeature,develop)。

为配置服务器指定多个 URL

为了确保高可用性,当您部署了多个配置服务器实例并预期一个或多个实例偶尔不可用或无法处理请求时(例如,如果 Git 服务器宕机),您可以指定多个 URL(作为 spring.cloud.config.uri 属性下的逗号分隔列表),或者让所有实例注册到服务注册中心(如 Eureka)(如果使用 Discovery-First Bootstrap 模式)。

spring.cloud.config.uri 下列出的 URL 将按列出的顺序尝试。默认情况下,配置客户端将尝试从每个 URL 获取属性,直到尝试成功以确保高可用性。

但是,如果您只想在配置服务器未运行时(即应用程序已退出)或发生连接超时时确保高可用性,请将 spring.cloud.config.multiple-uri-strategy 设置为 connection-timeout-only。(spring.cloud.config.multiple-uri-strategy 的默认值为 always。)例如,如果配置服务器返回 500(内部服务器错误)响应或配置客户端从配置服务器接收 401(由于凭据错误或其他原因),配置客户端不会尝试从其他 URL 获取属性。400 错误(可能除了 404 之外)表示用户问题而不是可用性问题。请注意,如果配置服务器设置为使用 Git 服务器并且对 Git 服务器的调用失败,则可能会发生 404 错误。

可以在单个 spring.config.import 密钥下指定多个位置,而不是 spring.cloud.config.uri。位置将按定义的顺序处理,以后的导入优先。但是,如果 spring.cloud.config.fail-fasttrue,则如果第一次配置服务器调用因任何原因不成功,配置客户端将失败。如果 fail-fastfalse,它将尝试所有 URL,直到一个调用成功,而不管失败的原因是什么。(在 spring.config.import 下指定 URL 时,spring.cloud.config.multiple-uri-strategy 不适用。)

如果您在配置服务器上使用 HTTP 基本安全,则当前仅当您将凭据嵌入到您在 spring.cloud.config.uri 属性下指定的每个 URL 中时,才有可能支持每个配置服务器的认证凭据。如果您使用任何其他类型的安全机制,则您当前无法支持每个配置服务器的身份验证和授权。

配置超时

如果您想配置超时阈值

  • 可以使用属性 spring.cloud.config.request-read-timeout 配置读取超时。

  • 可以使用属性 spring.cloud.config.request-connect-timeout 配置连接超时。

安全

如果您在服务器上使用 HTTP 基本安全,客户端需要知道密码(如果用户名不是默认值,则还需要知道用户名)。您可以通过配置服务器 URI 或通过单独的用户名和密码属性指定用户名和密码,如下例所示

spring:
  cloud:
    config:
     uri: https://user:[email protected]

以下示例显示了传递相同信息的另一种方法

spring:
  cloud:
    config:
     uri: https://myconfig.mycompany.com
     username: user
     password: secret

spring.cloud.config.passwordspring.cloud.config.username 值会覆盖 URI 中提供的任何内容。

如果您在 Cloud Foundry 上部署应用程序,提供密码的最佳方法是通过服务凭据(例如在 URI 中,因为它不需要位于配置文件中)。以下示例适用于本地和 Cloud Foundry 上名为 configserver 的用户提供的服务

spring:
  cloud:
    config:
     uri: ${vcap.services.configserver.credentials.uri:http://user:password@localhost:8888}

如果配置服务器需要客户端 TLS 证书,您可以通过属性配置客户端 TLS 证书和信任库,如下例所示

spring:
  cloud:
    config:
      uri: https://myconfig.myconfig.com
      tls:
        enabled: true
        key-store: <path-of-key-store>
        key-store-type: PKCS12
        key-store-password: <key-store-password>
        key-password: <key-password>
        trust-store: <path-of-trust-store>
        trust-store-type: PKCS12
        trust-store-password: <trust-store-password>

需要将 spring.cloud.config.tls.enabled 设置为 true 以启用配置客户端 TLS。当省略 spring.cloud.config.tls.trust-store 时,将使用 JVM 默认信任库。spring.cloud.config.tls.key-store-typespring.cloud.config.tls.trust-store-type 的默认值为 PKCS12。当省略密码属性时,将假定为空密码。

如果您使用其他形式的安全,您可能需要 ConfigServicePropertySourceLocator 提供 RestTemplate(例如,通过在 bootstrap 上下文中获取它并注入它)。

健康指标

配置客户端提供一个 Spring Boot 健康指标,该指标尝试从配置服务器加载配置。可以通过设置 health.config.enabled=false 来禁用健康指标。出于性能原因,响应也会被缓存。默认缓存生存时间为 5 分钟。要更改该值,请设置 health.config.time-to-live 属性(以毫秒为单位)。

提供自定义 RestTemplate

在某些情况下,您可能需要自定义从客户端到配置服务器的请求。通常,这样做涉及传递特殊的 Authorization 标头以对服务器的请求进行身份验证。

使用配置数据提供自定义 RestTemplate

使用配置数据提供自定义 RestTemplate

  1. 创建一个实现 BootstrapRegistryInitializer 的类

    CustomBootstrapRegistryInitializer.java
    public class CustomBootstrapRegistryInitializer implements BootstrapRegistryInitializer {
    
    	@Override
    	public void initialize(BootstrapRegistry registry) {
    		registry.register(RestTemplate.class, context -> {
    			RestTemplate restTemplate = new RestTemplate();
    			// Customize RestTemplate here
    			return restTemplate;
    		});
    	}
    
    }
  2. resources/META-INF 中,创建一个名为 spring.factories 的文件,并指定您的自定义配置,如下例所示

    spring.factories
    org.springframework.boot.BootstrapRegistryInitializer=com.my.config.client.CustomBootstrapRegistryInitializer

使用 Bootstrap 提供自定义 RestTemplate

使用 Bootstrap 提供自定义 RestTemplate

  1. 创建一个具有 PropertySourceLocator 实现的新配置 bean,如下例所示

    CustomConfigServiceBootstrapConfiguration.java
    @Configuration
    public class CustomConfigServiceBootstrapConfiguration {
        @Bean
        public ConfigServicePropertySourceLocator configServicePropertySourceLocator() {
            ConfigClientProperties clientProperties = configClientProperties();
           ConfigServicePropertySourceLocator configServicePropertySourceLocator =  new ConfigServicePropertySourceLocator(clientProperties);
            configServicePropertySourceLocator.setRestTemplate(customRestTemplate(clientProperties));
            return configServicePropertySourceLocator;
        }
    }
    对于添加 Authorization 标头的简化方法,可以使用 spring.cloud.config.headers.* 属性。
  2. resources/META-INF 中,创建一个名为 spring.factories 的文件,并指定您的自定义配置,如下例所示

    spring.factories
    org.springframework.cloud.bootstrap.BootstrapConfiguration = com.my.config.client.CustomConfigServiceBootstrapConfiguration

Vault

当使用 Vault 作为配置服务器的后端时,客户端需要为服务器提供令牌以从 Vault 检索值。可以通过在 bootstrap.yml 中设置 spring.cloud.config.token 来在客户端中提供此令牌,如下例所示

spring:
  cloud:
    config:
      token: YourVaultToken

Vault 中的嵌套密钥

Vault 支持在存储在 Vault 中的值中嵌套密钥的功能,如下例所示

echo -n '{"appA": {"secret": "appAsecret"}, "bar": "baz"}' | vault write secret/myapp -

此命令将 JSON 对象写入您的 Vault。要在 Spring 中访问这些值,您可以使用传统的点 (.) 注释,如下例所示

@Value("${appA.secret}")
String name = "World";

前面的代码会将 name 变量的值设置为 appAsecret

AOT 和原生镜像支持

4.0.0 开始,Spring Cloud Config Client 支持 Spring AOT 变换和 GraalVM 原生镜像。

对于 配置优先 bootstrap(使用 spring.config.use-legacy-processing=true),AOT 和原生镜像支持不可用。
原生镜像不支持刷新范围。如果您要将配置客户端应用程序作为原生镜像运行,请确保将 spring.cloud.refresh.enabled 属性设置为 false
构建包含 Spring Cloud Config Client 的项目时,必须确保它连接到的配置数据源(例如 Spring Cloud Config Server、Consul、Zookeeper、Vault 等)可用。例如,如果您从 Spring Cloud Config Server 检索配置数据,请确保其实例正在运行并且可在配置客户端设置中指示的端口上访问。这是必要的,因为应用程序上下文在构建时正在被优化,并且需要解析目标环境。
由于 AOT 和原生模式下,配置在构建时被处理,上下文被优化,任何会影响 Bean 创建的属性(例如在引导上下文内使用的属性)都应该在构建时和运行时设置为相同的值,以避免出现意外行为。
由于 Config Client 在从原生镜像启动时连接到正在运行的数据源(例如 Config Server),因此快速启动时间将因此网络通信所需的时间而减慢。

附录

可观测性元数据

可观测性 - 指标

下面您可以找到该项目声明的所有指标的列表。

环境仓库

围绕 EnvironmentRepository 创建的观察。

指标名称 spring.cloud.config.environment.find(由约定类 org.springframework.cloud.config.server.environment.ObservationEnvironmentRepositoryObservationConvention 定义)。类型 timer

指标名称 spring.cloud.config.environment.find.active(由约定类 org.springframework.cloud.config.server.environment.ObservationEnvironmentRepositoryObservationConvention 定义)。类型 长任务计时器

在启动观察后添加的 KeyValues 可能缺失于 *.active 指标中。
Micrometer 内部使用纳秒作为基本单位。但是,每个后端都会确定实际的基本单位。(例如,Prometheus 使用秒)

封闭类的全限定名 org.springframework.cloud.config.server.environment.DocumentedConfigObservation

所有标签都必须以spring.cloud.config.environment前缀开头!
表 1. 低基数键

名称

描述

spring.cloud.config.environment.application (必填)

正在为其查询属性的应用程序名称。

spring.cloud.config.environment.class (必填)

EnvironmentRepository 的实现。

spring.cloud.config.environment.label (必填)

正在为其查询属性的标签。

spring.cloud.config.environment.profile (必填)

正在为其查询属性的应用程序名称。

可观测性 - 跨度

下面您可以找到该项目声明的所有跨度的列表。

环境仓库跨度

围绕 EnvironmentRepository 创建的观察。

跨度名称 spring.cloud.config.environment.find(由约定类 org.springframework.cloud.config.server.environment.ObservationEnvironmentRepositoryObservationConvention 定义)。

封闭类的全限定名 org.springframework.cloud.config.server.environment.DocumentedConfigObservation

所有标签都必须以spring.cloud.config.environment前缀开头!
表 2. 标签键

名称

描述

spring.cloud.config.environment.application (必填)

正在为其查询属性的应用程序名称。

spring.cloud.config.environment.class (必填)

EnvironmentRepository 的实现。

spring.cloud.config.environment.label (必填)

正在为其查询属性的标签。

spring.cloud.config.environment.profile (必填)

正在为其查询属性的应用程序名称。