通过


应用配置支持

本文介绍 Spring Cloud Azure 应用程序配置 库。 此库从 Azure 应用程序配置 服务加载配置和功能标志。 该库生成 PropertySource 抽象以匹配 Spring 环境已经生成的抽象,例如环境变量、命令行配置、本地配置文件等。

Spring 是 VMware 开发的开源应用程序框架,它提供简化的模块化方法来创建 Java 应用程序。 Spring Cloud Azure 是一个开源项目,提供 Spring 与 Azure 服务的无缝集成。

先决条件

设置应用程序配置存储区

使用以下命令创建Azure 应用程序配置存储:

az appconfig create \
    --resource-group <your-resource-group> \
    --name <name-of-your-new-store> \
    --sku Standard

此命令将创建新的空配置存储。 可以使用以下导入命令上传配置:

az appconfig kv import \
    --name <name-of-your-new-store> \
    --source file \
    --path <location-of-your-properties-file> \
    --format properties \
    --prefix /application/

在加载之前确认你的配置。 可以通过将格式更改为 YAML 来上传 YAML 文件。 前缀字段很重要,因为它是客户端库加载的默认前缀。

图书馆使用情况

若要在应用程序中使用该功能,可以将其生成为 Spring Boot 应用程序。 添加依赖项的最方便方法是使用 Spring Boot 启动器 com.azure.spring:spring-cloud-azure-starter-appconfiguration-config。 以下示例pom.xml文件使用Azure 应用程序配置:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>{spring-boot-version}</version>
    <relativePath />
</parent>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.azure.spring</groupId>
      <artifactId>spring-cloud-azure-dependencies</artifactId>
      <version>7.0.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.azure.spring</groupId>
        <artifactId>spring-cloud-azure-starter-appconfiguration-config</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
    </plugins>
</build>

以下示例演示了使用 应用程序配置 的基本 Spring Boot 应用程序:

@SpringBootApplication
@RestController
public class Application {

    @RequestMapping("/")
    public String home() {
        return "Hello World!";
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

对于此示例, application.properties 文件包含以下行:

spring.config.import=azureAppConfiguration
spring.cloud.azure.appconfiguration.stores[0].endpoint=${CONFIG_STORE_ENDPOINT}

CONFIG_STORE_ENDPOINT 是一个环境变量,其中包含 Azure 应用配置存储的终结点 URL。

注意

Microsoft 建议使用最安全的可用身份验证流。 此过程中所述的身份验证流(例如数据库、缓存、消息传送或 AI 服务)需要高度信任应用程序,并且存在其他流中不存在的风险。 仅当更安全的选项(例如无密码连接或无密钥连接的托管标识)不可行时,才使用此流。 对于本地计算机操作,首选无密码连接或无密钥连接的用户标识。

默认情况下,如果未设置任何配置,以 /application/ 开头的配置将加载默认标签 (No Label),除非设置了 Spring Profile(此时默认标签是 Spring Profile)。

将创建一个名为 /application/https://<name-of-your-store>.azconfig.io/ 的属性源,其中包含该存储的属性。 请求中使用的标签将追加到名称的末尾。 如果未设置标签,则字符 \0 显示为空白。

加载配置

该库支持加载一个或多个应用程序配置存储。 在多个商店中复制密钥的情况下,最后一个密钥将获胜。

spring.cloud.azure.appconfiguration.stores[0].endpoint=[first-store-endpoint]
spring.cloud.azure.appconfiguration.stores[1].endpoint=[second-store-endpoint]

在此示例中,如果两个存储区具有相同的配置键,则第二个存储区中的配置具有最高优先级。

注意

可以像使用其他 Spring 配置一样使用 Azure 应用程序配置设置。 有关详细信息,请参阅 Spring Boot 文档或快速入门中的核心功能:使用 Azure 应用程序配置 创建 Java Spring 应用。

选择配置

库使用密钥和标签或快照加载配置。 如果未使用选择方法,库将加载所有带有前缀 /application/ 和标签 \0 的键,这在 Azure 门户中显示为 (No Label)

默认键筛选器为 /application/*. 默认标签筛选器是 \0,它在 Azure 门户中显示为 (No Label)。 如果设置了 Spring Profile,并且未提供任何标签,则默认标签为您的 Spring Profile,即 ${spring.profiles.active}

此外,还可以从特定快照加载配置。 快照是给定时间点配置的静态视图。 从快照加载时,会在创建快照时加载配置,在创建新快照并从中加载之前,它们不会更新。 快照需要是组合类型Key,可以使用snapshot属性指定要加载的快照。


You can configure which configurations are loaded by selecting different key and label filters:

```properties
spring.cloud.azure.appconfiguration.stores[0].selects[0].key-filter=[my-key]
spring.cloud.azure.appconfiguration.stores[0].selects[0].label-filter=[my-label]
spring.cloud.azure.appconfiguration.stores[0].selects[1].snapshot=[snapshot-name]

注意

不能将快照选择与键和标签筛选器组合在一起。 使用快照选择时,将加载快照中的所有密钥。

key-filter 属性支持以下筛选器:

关键筛选器 效果
* 匹配任何键。
abc 匹配名为 abc 的键。
abc* 匹配以 abc 开头的密钥名称。
abc,xyz 匹配密钥名称 abcxyz。 限制为五个逗号分隔的值。

label-filter 属性支持以下筛选器:

标签 说明
* 匹配任何标签,包括 \0
\0 匹配 null 标签,它们在 Azure 门户中显示为 (No Label)
1.0.0 完全匹配标签 1.0.0
1.0.* 匹配以 1.0.* 开头的标签。
,1.0.0 匹配标签 null1.0.0。 限制为五个逗号分隔的值。

如果使用标签过滤器的 YAML,并且想要加载没有标签的配置以及更多带有其他标签的配置,则需要包含一个空的,。 例如,,dev 匹配 \0dev。 在本例中,用单引号将标签筛选器括起来。 通过此值,可以在同一筛选器中加载不带标签的配置,后跟具有特定标签的配置:

spring:
  cloud:
    azure:
      appconfiguration:
        stores:
        - selects:
          - label-filter: ',1.0.0'

注意

不能将*,组合在筛选器中。 在这种情况下,需要使用其他选择值。

在标签筛选器中使用 * 时,加载具有相同键的多个配置时,会按字母顺序加载这些配置,最后按字母顺序加载标签。

弹簧型材

默认情况下, spring.profiles.active 设置为所有所选配置的默认值 label-filter 。 可以使用 label-filter 覆盖此功能。 通过使用 label-filter,可以在 ${spring.profiles.active} 中使用 Spring Profile,如下例所示:

spring.cloud.azure.appconfiguration.stores[0].selects[0].label-filter=,${spring.profiles.active}
spring.cloud.azure.appconfiguration.stores[0].selects[1].label-filter=${spring.profiles.active}_local

在第一个label-filter中,库首先加载所有带\0标签的配置,然后加载与Spring Profiles匹配的所有配置。 Spring Profiles 优先于 \0 配置,因为它们位于末尾。

在第二个label-filter中,将_local追加到Spring Profiles的末尾,但如果有多个Spring Profiles,则只追加到最后一个。

已禁用的存储区

使用配置 spring.cloud.azure.appconfiguration.enabled,可以禁用所有配置存储的加载。 使用 spring.cloud.azure.appconfiguration.stores[0].enabled 配置,可以禁用单个存储。

注意

如果使用健康指标,仍会看到列出的商店,但值为NOT LOADED。 检查加载的属性源时,仍会看到它们已列出,但它们不包含任何值。 此行为是由于已设置的 spring.config.import 属性造成的。 如果azureAppConfiguration未设置,则spring.config.import不显示任何值。

身份验证

该库支持Azure Identity Library所支持的所有身份形式。 可以通过配置对连接字符串和托管标识进行身份验证。

注意

Microsoft 建议使用最安全的可用身份验证流。 此过程中所述的身份验证流(例如数据库、缓存、消息传送或 AI 服务)需要高度信任应用程序,并且存在其他流中不存在的风险。 仅当更安全的选项(例如无密码连接或无密钥连接的托管标识)不可行时,才使用此流。 对于本地计算机操作,首选无密码连接或无密钥连接的用户标识。

通过连接字符串进行身份验证是设置的最简单形式,但不建议这样做。 可以使用以下命令访问商店的连接字符串:

az appconfig credential list --name <name-of-your-store>

然后,可以将属性spring.cloud.azure.appconfiguration.stores[0].connection-string设置为连接字符串。 使用此方法时,强烈建议将本地配置文件中的连接字符串设置为映射到环境变量的占位符值。 使用此方法,可以避免将连接字符串添加到源代码管理。

Spring Cloud Azure 配置

可以使用 Spring Cloud Azure 配置 来配置库。 可以使用以下属性配置库:

spring.cloud.azure.appconfiguration.stores[0].endpoint= <URI-of-your-configuration-store>

仅设置终结点时,客户端库使用 DefaultAzureCredential 进行身份验证。

需要分配用于读取配置的标识。 可以使用以下命令创建此分配:

az role assignment create \
    --role "App Configuration Data Reader" \
    --assignee <your-client-ID> \
    --scope /subscriptions/<your-subscription>/resourceGroups/<your-stores-resource-group>/providers/Microsoft.AppConfiguration/configurationStores/<name-of-your-configuration-store>

注意

每个终结点只能定义一种身份验证方法:连接字符串、用户分配的标识或令牌凭据。 如果您需要组合搭配,可以使用 ConfigurationClientCustomizer 来修改 ConfigurationClientBuilder 以使用不同的方法。

注意

Microsoft 建议使用最安全的可用身份验证流。 此过程中所述的身份验证流(例如数据库、缓存、消息传送或 AI 服务)需要高度信任应用程序,并且存在其他流中不存在的风险。 仅当更安全的选项(例如无密码连接或无密钥连接的托管标识)不可行时,才使用此流。 对于本地计算机操作,首选无密码连接或无密钥连接的用户标识。

异地复制

该库支持Azure 应用程序配置的异地复制功能。 使用此功能可将数据复制到其他位置。 此功能可用于高可用性和灾难恢复。

创建的每个副本都有一个专用终结点。 如果应用程序位于多个地理位置,则可以将应用程序在某个位置的每项部署更新为连接到更靠近该位置的副本,这有助于最大限度地减少应用程序和应用程序配置之间的网络延迟。 由于每个副本都有其单独的请求配额,因此此设置还有助于应用程序的可伸缩性,同时应用程序扩展到多区域分布式服务。

默认情况下,库会自动发现配置存储的所有存在副本。 向提供的存储区发出请求并失败时,库会自动针对可用副本重试请求。

如果库监测到以下任一情况,则可能发生故障转移:

  • 从终结点接收带有“服务不可用”状态代码(HTTP 500 或更高)的响应。
  • 遇到网络连接问题。
  • 请求受到限制(HTTP 状态代码 429)。

在指定存储重新联机后,库会自动针对该存储重试请求。

如果要控制故障转移行为,可以手动提供用于故障转移的存储列表。

spring.cloud.azure.appconfiguration.stores[0].endpoints[0]=[your primary store endpoint]
spring.cloud.azure.appconfiguration.stores[0].endpoints[1]=[your replica store endpoint]

spring.cloud.azure.appconfiguration.stores[0].connection-strings[0]=[your primary store connection string]
spring.cloud.azure.appconfiguration.stores[0].connection-strings[1]=[your replica store connection string]

如果所有提供的副本终结点都失败,库将尝试连接到主存储的自动发现的副本。

可以使用设置 spring.cloud.azure.appconfiguration.stores[0].replica-discovery-enabled=false禁用复制。

使用异地复制创建配置存储

若要创建配置存储的副本,可以使用 Azure CLI 或 Azure 门户。 以下示例使用 Azure CLI 在美国东部 2 区域创建副本:

az appconfig replica create --location --name --store-name [--resource-group]

密钥值

Azure 应用程序配置支持多种类型的键值,其中一些键值内置了特殊功能。 Azure 应用程序配置内置支持 JSON 内容类型、Spring 占位符和密钥库引用。

剪裁键

默认情况下,从 Azure 应用程序配置加载密钥时,库会从密钥中剪裁由 spring.cloud.azure.appconfiguration.stores[0].selects[0].key-filter 定义的前缀。 例如,如果你有一个命名 /application/config.message 的键和键筛选器 /application/*,则库会剪裁前缀 /application/ ,并将密钥加载为 config.message。 通过此裁剪,可以更加方便地在代码中引用键。

可以通过将 spring.cloud.azure.appconfiguration.stores[0].trim-key-prefix[0] 设置为不同的值来覆盖此行为。 将此属性设置为空字符串将禁用剪裁行为,并且该密钥将加载其全名。

占位符

该库支持具有 ${}-style 环境占位符的配置。 使用占位符引用Azure 应用程序配置键时,请从引用中删除前缀。 例如, /application/config.message 引用为 ${config.message}.

注意

要移除的前缀与值 spring.cloud.azure.appconfiguration.stores[0].selects[0].key-filter匹配。 通过设置 spring.cloud.azure.appconfiguration.stores[0].trim-key-prefix[0] 的值,可以更改截取的前缀。

JSON

可以在应用配置 中创建 JSON 键值 。 从 Azure 应用配置加载键值时,配置提供程序会自动将配置设置转换为内部 @ConfigurationProperties的复杂对象。 例如,请考虑具有以下值的 JSON 键 /application/config.colors

{
    "key": "font",
    "label": null,
    "value": "{\r\n\t\"size\": 12,\r\n\t\"color\": \"red\"\r\n}",
    "content_type": "application/json"
}

此 JSON 内容导致键值加载为 { size: 12, color: "red" }

@ConfigurationProperties(prefix = "config")
public class MyConfigurations {

    private Font font;

}

注意

版本 6.0.0spring-cloud-azure-appconfiguration-config开始,配置提供程序允许在具有application/json内容类型的键值中使用(JSONC)中定义的注释。

Key Vault 引用

Azure 应用程序配置及其库支持引用存储在密钥库中的机密。 在应用程序配置中,可以使用映射到存储在密钥库中的机密的值创建密钥。 密钥保管库中仍保持安全,但在加载应用时,可以采用与任何其他配置相同的方式访问它们。

应用程序使用客户端提供程序检索 Key Vault 引用,就如同检索应用程序配置中存储的任何其他密钥一样。 由于客户端将密钥识别为 Key Vault 引用,这些密钥具有唯一的 content-type,客户端将连接到 Key Vault,为你检索它们的值。

注意

Key Vault 允许一次只检索一次机密,因此应用程序配置中存储的每次 Key Vault 引用都会导致对 Key Vault 进行拉取。

创建 Key Vault 引用

可以通过在 Azure 门户转到配置资源管理器>创建>密钥库引用来创建密钥库引用。 然后,可以从你有权访问的任何密钥库中选择一个要引用的机密。 还可以从“输入”选项卡创建任意密钥库引用。在Azure 门户中,输入有效的 URI。

还可以使用以下命令通过 Azure CLI 创建密钥库引用:

az appconfig kv set-keyvault \
    --name <name-of-your-store> \
    --key <key-name> \
    --secret-identifier <URI-to-your-secret>

可以通过 Azure CLI 创建任何机密标识符。 机密标识符只需要格式为{vault}/{collection}/{name}/{version?},其中版本部分是可选的。

使用 Key Vault 引用

可以使用 Spring Cloud Azure 配置 来配置库。 可以使用用于连接到应用程序配置的凭据来连接到 Azure Key Vault。

您还可以按照与创建 ConfigurationClientCustomizer 相同的方法创建一个 SecretClientCustomizer 来提供自己的身份验证方法。

解析非密钥库机密

应用配置库提供了一种方法来替代密钥保管库引用的解析。 例如,可以使用它在本地解析开发环境中的机密。 此解决方案通过 KeyVaultSecretProvider 来完成。 如果提供,将在每个密钥保管库引用上调用KeyVaultSecretProvider。 如果 getSecret 返回非 null 值,则将其用作机密值。 否则,Key Vault Reference 将会被正常解析。

public class MySecretProvider implements KeyVaultSecretProvider {

    @Override
    public String getSecret(String uri) {
        ...
    }

}

功能管理

功能管理为 Spring Boot 应用程序提供了动态访问内容的方法。 功能管理具有各种功能,例如以下功能:

  • 可启用或禁用内容的功能标志
  • 用于定向显示内容的特性筛选器
  • 自定义功能筛选器
  • 用于动态启用终结点的功能门

可以通过以下配置启用功能标志:

spring.cloud.azure.appconfiguration.stores[0].feature-flags.enabled= true

已启用的功能标志将加载到具有前缀 feature-management的 Spring 配置系统中。 还可以在本地配置文件中注册功能标志。 有关详细信息,请参阅 功能标志声明 部分。

使用功能管理的最简单方法是使用 spring-cloud-azure-feature-managementspring-cloud-azure-feature-management-web 库。 两个库之间的区别在于,spring-cloud-azure-feature-management-web 依赖于 spring-webspring-webmvc 来添加更多功能,例如功能门

默认情况下,加载带有 \0 标签的所有功能标志(如下所示 (No Label))。 可以通过设置标签筛选器来配置加载的功能标志,如以下示例所示:

spring.cloud.azure.appconfiguration.stores[0].feature-flags.selects[0].key-filter=A*
spring.cloud.azure.appconfiguration.stores[0].feature-flags.selects[0].label-filter= dev

功能标志遥测

启用功能标志遥测后,Azure 应用配置提供程序会向功能标志遥测数据注入其他属性。 这些属性提供有关功能标志及其评估的更多上下文:

  • AllocationID:表示功能标志分配状态的唯一标识符。
  • ETag:特性标志的当前 ETag。
  • FeatureFlagReference:对功能标志的引用,格式为 <your_store_endpoint>kv/<feature_flag_key>. 当存在标签时,引用将它作为查询参数包括: <your_store_endpoint>kv/<feature_flag_key>?label=<feature_flag_label>

功能管理基础知识

功能标志

功能标志由多个部分组成,包括用于打开该功能的功能筛选器的名称和列表。 功能标志可以具有打开或关闭的布尔状态,也可以具有功能筛选器列表。 功能标志会评估功能筛选器,直到返回 true。 如果没有任何功能筛选器返回true,那么功能标志返回false

功能筛选器

功能筛选器定义应启用功能的场景。 特征筛选器会被同步评估。

功能管理库附带了四个预定义筛选器:AlwaysOnFilterPercentageFilterTimeWindowFilterTargetingFilter

可以创建自定义功能筛选器。 例如,可以使用功能筛选器为使用 Microsoft Edge 浏览器的客户提供自定义体验。 例如,可以自定义此功能筛选器中的功能,以显示 Microsoft Edge 浏览器受众的特定标头。

功能标志声明

功能管理库支持 Azure 应用配置,以及 application.ymlapplication.properties 作为功能标志的源。 下面是用于在 application.yml 文件中设置功能标志的格式示例:

feature-management:
  feature_flags:
  - id: feature-t
    enabled: false
  - id: feature-u
    conditions:
      client_filters:
      - name: Random
  - id: feature-v
    conditions:
      client_filters:
      - name: TimeWindowFilter
        parameters:
          Start: "Wed, 01 May 2019 13:59:59 GMT"
          End: "Mon, 01 July 2019 00:00:00 GMT"

  - id: feature-w
    evaluate: false
    conditions:
      client_filters:
      - name: AlwaysOnFilter

此示例具有以下功能标志:

  • feature-t 设置为 false。 此设置始终返回功能标志的值。
  • feature-u 用于特性筛选。 这些筛选器是在enabled-for属性下定义的。 在这种情况下, feature-u 调用了一个功能筛选器 Random,它不需要任何配置,因此只需要 name 属性。
  • feature-v 指定名为 TimeWindowFilter 的功能筛选器。 此功能筛选器可以传递参数以用作配置。 在此示例中,一个 TimeWindowFilter 传递了功能激活时段的开始和结束时间。
  • feature-w 用于 AlwaysOnFilter,其结果始终计算为 true。 该 evaluate 字段用于停止对特征筛选器的计算,并导致功能筛选器始终返回 false

评估功能标志

spring-cloud-azure-feature-management 库提供 FeatureManager 以确定功能标志是否已启用。 FeatureManager 提供了检查标志状态的异步方法。

除了提供 spring-cloud-azure-feature-management-webFeatureManager 还包含 FeatureManagerSnapshot,它会缓存 @RequestScope 中先前评估的功能标志的状态,以确保所有请求返回相同的值。 此外,Web 库提供 @FeatureGate,它可以阻止或将 Web 请求重定向到不同的终结点。

功能标志检查

FeatureManager 是一个 @Bean,可以是 @Autowired,也可以注入到 @Component 类型对象。 FeatureManager 具有一个方法 isEnabled ,当传递功能标志的名称时,返回其状态。

@Autowired
FeatureManager featureManager;

...

if (featureManager.isEnabled("feature-t")) {
    // Do Something
}

注意

FeatureManager还有一个名为isEnabledisEnabledAsync异步版本。

如果没有功能管理配置或功能标志不存在, isEnabled 则始终返回 false。 如果现有功能标志配置了未知功能筛选器,则会引发 FilterNotFoundException。 可以通过将false配置为fail-fast来更改此行为以返回false。 下表描述了 fail-fast

名称 说明 必需 默认
spring.cloud.azure.feature.management.fail-fast 如果发生异常,会抛出一个RuntimeException。 如果此属性设置为 false,则 isEnabled 返回 false true

FeatureManagerSnapshotFeatureManager之间的唯一区别在于@RequestScope中的结果缓存。

功能门

使用功能管理 Web 库,可以要求启用给定功能才能执行终结点。 可以使用批注设置此要求 @FeatureGate ,如以下示例所示:

@GetMapping("/featureT")
@FeatureGate(feature = "feature-t")
@ResponseBody
public String featureT() {
    ...
}

启用“feature-t”后,您只能访问featureT终结点。

禁用的操作处理

当由于其指定的功能被禁用而导致终结点被阻止时,将调用 DisabledFeaturesHandler。 默认情况下,返回 HTTP 404。 可以通过实现 DisabledFeaturesHandler来替代此行为,如以下示例所示:

@Component
public class MyDisabledFeaturesHandler implements DisabledFeaturesHandler {

    @Override
    public HttpServletResponse handleDisabledFeatures(HttpServletRequest request, HttpServletResponse response) {
        ...
        return response;
    }

}
路由

功能可能会限制公开应用程序功能的某些路由。 如果禁用了某个功能,可以将这些路由重定向到另一终结点,如以下示例所示:

@GetMapping("/featureT")
@FeatureGate(feature = "feature-t" fallback= "/oldEndpoint")
@ResponseBody
public String featureT() {
    ...
}

@GetMapping("/oldEndpoint")
@ResponseBody
public String oldEndpoint() {
    ...
}

内置功能筛选器

有一些功能筛选器附带 spring-cloud-azure-feature-management 包。 将自动添加这些功能筛选器。

AlwaysOnFilter (永远在线过滤器)

此筛选器始终返回 true。 有关用法示例,请参阅 功能标志声明 部分。

百分比过滤器

PercentageFilter 可以在每次检查时返回不同的结果。 可以通过使用 FeatureManagementSnapshot来规避这种不一致,这会缓存每个请求的功能标志的结果。

feature-management:
  feature_flags:
  - name: feature-v
    conditions:
      client_filters:
      - name: PercentageFilter
        parameters:
          Value: 50

时间窗口过滤器

此筛选器提供根据时间窗口来启用某项功能的功能。 如果您仅指定 End,则该功能将一直开启直到该时间。 如果只指定Start,那么该功能会在该时间点之后的所有时刻开启。 如果同时指定这两个功能,则该功能在两次之间被视为有效。

feature-management:
  feature_flags:
  - name: feature-v
    conditions:
      client_filters:
      - name: TimeWindowFilter
        parameters:
          Start: "Wed, 01 May 2019 13:59:59 GMT"
          End: "Mon, 01 July 2019 00:00:00 GMT"

此筛选器还支持定期时间窗口筛选器。 它支持每日和每周重复周期,以及过期时间。

feature-management:
  feature_flags:
  - name: feature-v
    conditions:
      client_filters:
      - name: TimeWindowFilter
        parameters:
          Start: "Mon, 01 July 2019 00:00:00 GMT"
          End: "Mon, 01 July 2019 12:00:00 GMT"
          Recurrence:
            Pattern:
              Type: Weekly
              Interval: 1
              FirstDayOfWeek: Sunday
              DaysOfWeek:
              - Monday
              - Wednesday

此重复周期每周在周一和周三的GMT 00:00:00至GMT 12:00:00发生,并且不会过期。

feature-management:
  feature_flags:
  - name: feature-v
    conditions:
      client_filters:
      - name: TimeWindowFilter
        parameters:
          Start: "Mon, 01 July 2019 00:00:00 GMT"
          End: "Mon, 01 July 2019 12:00:00 GMT"
          Recurrence:
            Pattern:
              Type: Daily
              Interval: 2
            Range:
              Type: EndDate
              EndDate: "Fri, 15 Aug 2025 07:00:00 GMT"

此重复模式每隔一天在格林尼治标准时间00:00:00至12:00:00之间发生,直到结束日期。

目标筛选器

此筛选器提供为目标受众启用某项功能的功能。 有关目标的深入说明,请参阅 “目标”部分 。 筛选器参数包括一个受众对象,该对象描述用户、组和应有权访问该功能的用户群的默认百分比。 对于目标受众中列出的每个组对象,需要一个百分比,该百分比定义有权访问该功能的组成员的百分比。 以下情况下,用户已启用该功能:

  • 用户在用户部分中直接指定。
  • 用户被纳入任何组推出百分比中。
  • 用户在默认推出百分比范围内。
feature-management:
  feature_flags:
  - name: target
    conditions:
      client_filters:
      - name: targetingFilter
        parameters:
          users:
          - Jeff
          - Alicia
          groups:
          - name: Ring0
            rollout-percentage: 100
          - name: Ring1
            rolloutPercentage: 100
          default-rollout-percentage: 50

自定义功能筛选器

创建自定义功能筛选器提供了一种基于所定义条件启用功能的方法。 若要创建自定义功能筛选器,必须实现 FeatureFilter 接口。 FeatureFilter 具有单个方法 evaluate。 当某个功能指定可以使用功能筛选器启用该功能时,将调用该方法 evaluate 。 如果 evaluate 返回 true,则表示应启用该功能。 如果返回 false,它将继续评估功能筛选器,直到返回 true。 如果所有筛选器都返回 false,则该功能处于关闭状态。

特征筛选器被定义为 Spring Beans,因此它们要么在@Component中定义,要么在@Configuration中定义。

@Component("Random")
public class Random implements FeatureFilter {

    @Override
    public boolean evaluate(FeatureFilterEvaluationContext context) {
        double chance = Double.valueOf((String) context.getParameters().get("chance"));
        return Math.random() > chance / 100;
    }

}

参数化功能筛选器

某些功能筛选器需要参数来确定是否应启用某个功能。 例如,浏览器功能筛选器可能会为一组特定浏览器打开一项功能。 你可能希望为 Microsoft Edge 和 Chrome 浏览器启用一项功能,但不希望为 Firefox 启用此功能。 若要设置这种情况,可以设计功能筛选器以预期参数。 这些参数将在功能配置中指定,并且在代码中可以通过 FeatureFilterEvaluationContextevaluate 参数进行访问。 FeatureFilterEvaluationContext 具有一个属性 parameters,即一个 Map<String, Object>

目标设定

目标定位是一种功能管理策略,使开发人员能够逐步向其用户群推出新功能。 该策略建立在面向一组称为目标受众的用户的概念之上。 受众由特定用户、组和占整个用户群的指定百分比的人数组成。 受众中包含的组可以进一步细分为其成员总数的百分比。

以下步骤演示了新“Beta 版”功能的渐进式推出示例:

  1. 个人用户 Jeff 和 Alicia 有权访问 Beta 版。
  2. 另一位用户 Mark 请求加入并被接纳。
  3. Beta 版中包含名为“Ring1”用户的组的 20%。
  4. Beta 版中包含的“Ring1”用户数量增加到 100%。
  5. 包含在 beta 版中的用户群占 5%。
  6. 推出百分比将提升到 100%,该功能已完全推出。

此用于推出功能的策略通过包含 TargetingFilter 的功能筛选器内置到库中。

在应用程序中定位

示例项目中提供了使用目标功能筛选器的示例 Web 应用程序。

若要开始在应用程序中使用 TargetingFilter,必须将其添加为像任何其他功能筛选器一样的 @BeanTargetingFilter 依赖于另一个 @Bean 才能添加到应用程序 TargetingContextAccessor 中。 TargetingContextAccessor 允许定义当前用于设定用户 ID 和组的 TargetingContext,如以下示例所示:

public class MyTargetingContextAccessor implements TargetingContextAccessor {

    @Override
    public void configureTargetingContext(TargetingContext context) {
        context.setUserId("Jeff");
        ArrayList<String> groups = new ArrayList<String>();
        groups.add("Ring0");
        context.setGroups(groups);
    }

}

目标评估选项

选项可用于自定义如何在给定 TargetingFilter范围内执行目标评估。 可以在TargetingEvaluationOptions创建时设置TargetingFilter可选参数。

    @Bean
    public TargetingFilter targetingFilter(MyTargetingContextAccessor contextAccessor) {
        return new TargetingFilter(contextAccessor, new TargetingEvaluationOptions().setIgnoreCase(true));
    }

配置刷新

为配置启用配置刷新后,无需重启应用程序即可从应用程序配置存储区拉取其最新值。

若要启用刷新,需要启用监视和监视触发器。 监视触发器是一个键,其中包含一个可选标签,系统监视值更改以触发更新。 监视触发器的值可以是任何值,只要需要刷新时发生变化即可。

注意

更改监视触发器 ETag 的任何操作都会导致刷新,例如内容类型更改。

spring:
  cloud:
    azure:
      appconfiguration:
        stores:
        - monitoring:
          enabled: true
          triggers:
          - key: [my-watched-key]
            label: [my-watched-label]

若要触发配置刷新,请在配置存储中更改密钥的值。 然后,将其中一个监视键更新为新值。 此更改触发日志的创建。 例如,更改 /application/config.message 的值将触发以下日志消息:

INFO 17496 --- [TaskScheduler-1] o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [config.message]

应用程序生成日志后,它会刷新刷新范围中的所有@Bean

注意

默认情况下, @ConfigurationProperties 带批注的豆类包含在此范围内。

基于拉取的刷新

应用程序配置 Spring 库支持定期检查对监视触发器所做的更改的刷新间隔。 默认情况下,刷新间隔设置为 30 秒。 刷新间隔过后,在进行刷新尝试时,所有触发器都会在给定的存储中检查更改。 对密钥的任何更改将导致一个刷新操作被触发。 由于库与 Spring refresh 系统集成,因此任何刷新都会重新加载所有存储中的所有配置。 可以将刷新间隔设置为超过 1 秒的任何间隔。 刷新间隔支持单位分别为smhd、分钟、小时和天。 以下示例将刷新间隔设置为 5 分钟:

spring.cloud.azure.appconfiguration.stores[0].monitoring.refresh-interval= 5m

自动

使用 spring-cloud-azure-appconfiguration-config-web 库时,应用程序会在发生 servlet 请求时自动检查刷新,具体而言 ServletRequestHandledEvent。 此事件发送的最常用方式是向终结点 @RestController发出的请求。

Manual

在仅 spring-cloud-azure-appconfiguration-config使用的应用程序(例如控制台应用程序)中,可以通过调用 AppConfigurationRefresh的方法 refreshConfiguration 手动触发刷新。 AppConfigurationRefresh 是一个可以注入到任何 @Bean@Component

此外,由于库使用 Spring 的配置系统,因此触发刷新会导致刷新所有配置,而不仅仅是从 Azure 应用配置存储重新加载这些配置。

注意

不再建议使用此方法,但目前仍受支持。

可以设置 spring-cloud-azure-appconfiguration-config-web 库以接收来自 Azure 应用配置存储的推送通知以刷新配置值。 可以通过 Azure 事件网格 WebHook 设置此配置,该挂钩可配置为向指定密钥发送更改通知。 通过将 Spring 执行器库添加为依赖项,可以公开应用配置的刷新终结点。 有两个不同的终结点: appconfiguration-refreshappconfiguration-refresh-bus。 这些端点的工作方式与其对应的refreshrefresh-bus端点类似,其中应用配置端点允许刷新间隔自然到期,而不是在接收到数据时强制刷新。 你仍然可以使用 refreshrefresh-bus,但不能使用 WebHook 将其直接连接到 Azure 事件网格,因为它们需要设置中的响应。

appconfiguration-refresh 属性导致刷新间隔过期,因此,在下一次刷新校验之前不会等待剩余的刷新间隔。 该 appconfiguration-refresh-bus 属性向连接的消息传递服务(如 Azure 服务总线)发送通知,以通知应用程序的所有实例要刷新。 在这两种情况下,它不会在刷新间隔完全过期,但会以较小的抖动量关闭。 此抖动可确保应用程序的每个实例不会同时尝试刷新。

management.endpoints.web.exposure.include= appconfiguration-refresh, appconfiguration-refresh-bus

除了公开刷新终结点之外,库还需要一个查询参数以确保安全性。 默认情况下不存在令牌名称或值,但必须设置一个来使用终结点,如以下示例所示:

spring.cloud.azure.appconfiguration.stores[0].monitoring.push-notification.primary-token.name=[primary-token-name]
spring.cloud.azure.appconfiguration.stores[0].monitoring.push-notification.primary-token.secret=[primary-token-secret]
spring.cloud.azure.appconfiguration.stores[0].monitoring.push-notification.secondary-token.name=[secondary-token-name]
spring.cloud.azure.appconfiguration.stores[0].monitoring.push-notification.secondary-token.secret=[secondary-token-secret]

设置 Webhook

若要设置 Web 挂钩,请打开Azure 应用程序配置存储和从导航菜单中打开“事件”。 然后选择“ 事件订阅”。 设置事件的名称,并选择终结点类型为 WebHook。 选择 Webhook 后,会出现“终结点”选项。 选中“选择终结点”。 终结点应如以下示例所示: https://www.myaplication.com/actuator/appconfiguration-refresh?myTokenName=mySecret

确认选择 将设置通知发送到给定的 URI,并且需要响应。 如果未返回响应,安装程序将失败。 如果为应用程序配置了 Azure 应用程序配置存储区,终结点的 azure-spring-cloud-appconfiguration-web 库设置将返回正确的响应。 可以通过其他方式发送此确认。 有关 Web 挂钩传递的详细信息,请参阅 Webhook 事件传递

注意

此验证仅在创建或修改终结点时发生。

强烈建议设置筛选器,因为否则,每次创建和修改密钥后都会触发刷新。

强制客户端刷新

可以将库配置为按刷新间隔强制刷新所有配置。 下表描述了该 refresh-interval 属性:

名称 说明 必需 默认
spring.cloud.azure.appconfiguration.refresh-interval 刷新之间的标准时间量。 是 Duration null

使用 spring.cloud.azure.appconfiguration.refresh-interval 刷新时,不会检查任何已配置的监视密钥。 此属性用于确保密钥库机密保持最新状态,因为Azure 应用程序配置无法判断它们何时更新。

由于 Azure 密钥库将证书的公钥和私钥对存储为机密,因此应用程序可以将任何证书作为应用程序配置中的密钥库引用检索。 由于证书需要定期轮换,客户端应用程序需要同样频繁地进行更新,这可以通过使用客户端刷新间隔来完成。

功能标志刷新

如果同时启用功能标志和监视,则默认情况下,功能标志的刷新间隔设置为 30 秒。 刷新间隔结束时,系统会检查给定存储中的所有功能标志是否有更改。 对密钥的任何更改将导致一个刷新操作被触发。 由于库与 Spring refresh 系统集成,因此任何刷新都会重新加载所有存储中的所有配置。 可以将刷新间隔设置为超过 1 秒的任何间隔。 刷新间隔支持单位分别为smhd、分钟、小时和天。 以下示例将刷新间隔设置为 5 分钟:

spring.cloud.azure.appconfiguration.stores[0].monitoring.feature-flag-refresh-interval= 5m

健康指标

客户端库附带一个运行状况指示器,用于检查与 Azure 应用程序配置存储区的连接是否运行正常。 如果为每个存储区启用,则会提供以下状态值之一:

  • UP - 上次连接成功。
  • DOWN - 最后一个请求导致非 200 错误代码。 此状态可能是由于凭据过期到服务问题等问题造成的。 客户端库会在下次刷新间隔自动重试以连接到存储区。
  • NOT LOADED - 配置存储库在本地配置文件中列出,但在启动时未从文件中加载。 配置文件中禁用了配置存储,或者一个或多个配置在启动时加载失败,同时存储区的 fail-fast 配置设置为 false

可以通过设置 management.health.azure-app-configuration.enabled=true启用运行状况指示器。

客户端自定义

应用程序配置库使用 Azure SDK for Java 来连接到 Azure 应用配置 和 Azure 密钥保管库。 提供了两个接口,ConfigurationClientCustomizerSecretClientCustomizer,用于修改客户端。 每个接口都有一个 customize 方法,该方法接受各自的生成器以及为其配置客户端的 URI 的 String 值,如以下接口定义所示:

public interface ConfigurationClientCustomizer {
    public void customize(ConfigurationClientBuilder builder, String endpoint);
}

public interface SecretClientCustomizer {
    public void customize(SecretClientBuilder builder, String endpoint);
}

这些接口允许自定义 HTTP 客户端及其配置。 以下示例将默认的 HttpClient 替换为另一个使用代理传输所有定向到应用程序配置和 Key Vault 的流量的值。

注意

ConfigurationClientBuilderSecretClientBuilder已经设置好,可以在传入customize时使用。 对客户端所做的任何更改(包括凭据和重试策略)都会替代已设置的默认值。

还可以使用 Spring Cloud Azure 配置执行此配置

public class CustomClient implements ConfigurationClientCustomizer, SecretClientCustomizer {

    @Override
    public void customize(ConfigurationClientBuilder builder, String endpoint) {
        builder.httpClient(buildHttpClient());
    }

    @Override
    public void customize(SecretClientBuilder builder, String endpoint) {
        builder.httpClient(buildHttpClient());
    }

    private HttpClient buildHttpClient() {
        String hostname = System.getProperty("https.proxyHosts");
        String portString = System.getProperty("https.proxyPort");
        int port = Integer.valueOf(portString);

        ProxyOptions proxyOptions = new ProxyOptions(ProxyOptions.Type.HTTP,
                new InetSocketAddress(hostname, port));
        return new NettyAsyncHttpClientBuilder()
                .proxy(proxyOptions)
                .build();
    }

}