应用配置支持

本文介绍 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>5.22.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 2.x,请确保将 spring-cloud-azure-dependencies 版本设置为 4.20.0。 有关用于此 BOM 的版本的详细信息,请参阅应使用哪个版本的 Spring Cloud Azure

以下示例演示了使用 应用程序配置 的基本 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);
    }
}

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

spring.cloud.azure.appconfiguration.stores[0].connection-string=${CONFIG_STORE_CONNECTION_STRING}

CONFIG_STORE_CONNECTION_STRING是一个环境变量,其中包含Azure 应用程序配置应用商店的连接字符串。 可以使用以下命令访问连接字符串:

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

注意

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

默认情况下,如果未设置任何配置,以 /application/ 开头的配置将加载默认标签 (No Label),除非设置了 Spring Profile(此时默认标签是 Spring Profile)。 由于存储为空,因此不会加载任何配置,但仍会生成Azure 应用程序配置属性源。

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

加载配置

该库支持加载一个或多个应用程序配置存储。 如果键跨多个存储区复制,则加载所有存储会导致加载优先级最高的存储区配置。 最后一个赢了。 以下示例演示了此过程:

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

在此示例中,如果第一和第二个存储区具有相同的配置,则第二个存储区中的配置具有最高优先级,最后一个存储将获胜。

注意

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

选择配置

配置根据键和标签进行加载。 默认情况下,加载以密钥 /application/ 开头的配置。 默认标签为 ${spring.profiles.active}. 如果未设置 ${spring.profiles.active} ,则会加载带有 null 标签的配置。 null 标签在 Azure 门户中显示为 (No Label)

可以通过选择不同的键和标签筛选器来配置加载的配置,如以下示例所示:

spring.cloud.azure.appconfiguration.stores[0].selects[0].key-filter=[my-key]
spring.cloud.azure.appconfiguration.stores[0].selects[0].label-filter=[my-label]

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 和标签筛选器,并且需要从 null开头,则标签筛选器需要用单引号括起来,如以下示例所示:

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

注意

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

Spring Profiles

默认情况下, spring.profiles.active 设置为所有所选配置的默认值 label-filter 。 可以使用 label-filter 覆盖此功能。 通过使用 ${spring.profiles.active},可以在 label-filter 中使用 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 中,带 null 标签的所有配置都会加载,之后加载与 Spring Profile 匹配的所有配置。 Spring Profiles 优先于 null 配置,因为它们位于末尾。

在第二个 label-filter 中,字符串 _local 会追加到 Spring Profile 的末尾,但仅追加到最后一个 Spring Profile。

已禁用的存储区

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

除了可以禁用商店之外,您还可以配置商店在加载失败时自动被禁用。 对于此配置,请使用 spring.cloud.azure.appconfiguration.stores[0].fail-fast。 通过将 fail-fast 设置为 false 来禁用它时,RuntimeException 会导致应用程序存储区被禁用,并且不会从中加载任何配置。 如果在启动时禁用配置存储,则刷新时不会检查是否存在更改。 此外,如果更新配置,则不会尝试从中加载值。

如果在刷新检测时或尝试重新加载配置时发生导致 RuntimeException 的错误,刷新操作将会结束,并在refresh-interval 时间过后重试。

身份验证

该库支持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 进行身份验证。 使用以下 DefaultAzureCredential 方法进行身份验证:

  • 环境认证凭证
  • 托管标识凭据
  • Azure Developer CLI 凭据
  • IntelliJ 凭据
  • Azure CLI 凭据
  • Azure PowerShell 身份验证凭据

需要分配标识(例如系统分配的标识)来读取配置。 可以使用以下命令创建此分配:

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 来修改采用不同方法的存储区。

注意

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

异地复制

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

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

如果库检测到下述任一情况,可能会发生故障转移:

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

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

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

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

使用配置存储区副本

创建副本后,可以在应用程序中使用它。 与源存储区一样,可以使用 Microsoft Entra ID 或连接字符串连接到副本。

注意

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

若要使用 Microsoft Entra ID 连接到副本,需要列出配置存储区实例的 endpoints,如以下示例所示:

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

可以列出与副本数量相同的终结点。 这个库会尝试按照列出的顺序连接到这些终结点。 如果库无法连接到副本,它将尝试列表中的下一个副本。 经过一段时间后,库会尝试重新连接到首选终结点。

密钥值

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

占位符

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

注意

要移除的前缀与值 spring.cloud.azure.appconfiguration.stores[0].selects[0].key-filter匹配。

JSON

具有内容类型 application/json 的配置将作为 JSON 对象进行处理。 使用此功能可将一个配置映射到内部 @ConfigurationProperties的复杂对象。 例如,请考虑具有以下值的 JSON 键 /application/config.colors

{
 "Red": {
  "value": [255, 0, 0]
 },
 "Blue": {
  "value": [0, 255, 0]
 },
 "Green": {
  "value": [0, 0, 255]
 }
}

此键映射到以下代码:

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

    private Map<String, Color> colors;

}

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。

解析非密钥库机密

应用程序配置库提供了一种方法,用于本地解析没有与之关联的密钥库的机密。 此解决方案通过 KeyVaultSecretProvider 来完成。 如果未为 Key Vault 引用提供 TokenCredential,则会调用 KeyVaultSecretProvider。 提供了密钥库引用的 URI,返回的值将成为机密的值。

警告

如果使用 KeyVaultSecretProvider,则不会自动使用系统分配的托管标识。 若要同时使用这两者,需要使用 KeyVaultCredentialProvider 和返回 null 需要解析的 URI。

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 来添加更多功能,例如功能门

可以使用键/标签筛选器启用功能标志。 默认情况下,分配了一个 null 标签(被视为 (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

功能管理基础知识

功能标志

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

功能筛选器

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

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

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

功能标志声明

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

feature-management:
  feature-t: false
  feature-u:
    enabled-for:
    - name: Random
  feature-v:
    enabled-for:
    - name: TimeWindowFilter
      parameters:
        Start: "Wed, 01 May 2019 13:59:59 GMT"
        End: "Mon, 01 July 2019 00:00:00 GMT"
  feature-w:
    evaluate: false
    enabled-for:
    - 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 提供了检查标志状态的异步方法。

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

功能标志检查

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

@Autowired
FeatureManager featureManager;

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

注意

FeatureManger还有一个名为isEnabledAsyncisEnabled异步版本。

如果尚未配置功能管理或功能标志不存在, isEnabled 则始终返回 false。 如果现有功能标志配置了未知功能筛选器,则会引发 FilterNotFoundException。 可以通过将fail-fast配置为false来更改此行为以返回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 包。 这些功能筛选器不会自动添加,但你可以在一个 @Configuration中设置它们。

AlwaysOnFilter

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

百分比过滤器

每次用户发出请求时,评估 PercentageFilter 都可以返回不同的结果。 可以使用 FeatureManagementSnapshot 来规避这种不一致,该机制会为每个用户缓存功能标志的结果。 此功能可确保用户具有一致的体验,即使用户必须重新发送请求。

feature-management:
  feature-v:
    enabled-for:
    - name: PercentageFilter
      parameters:
        Value: 50

时间窗口过滤器

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

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

目标筛选器

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

  • 用户在用户部分中直接指定。
  • 用户被纳入任何组推出百分比中。
  • 用户在默认推出百分比范围内。
feature-management: 
  target:
    enabled-for:
    - 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 启用此功能。 若要设置这种情况,可以设计功能筛选器以预期参数。 这些参数将在功能配置中指定,并且在代码中可以通过 evaluateFeatureFilterEvaluationContext 参数进行访问。 FeatureFilterEvaluationContext 具有一个属性 parameters,即一个 HashMap<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 getContextAsync(TargetingContext context) {
        context.setUserId("Jeff");
        ArrayList<String> groups = new ArrayList<String>();
        groups.add("Ring0");
        context.setGroups(groups);
    }

}

目标评估选项

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

    @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 中的终结点。

手动

在仅 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 setup(ConfigurationClientBuilder builder, String endpoint);
}

public interface SecretClientCustomizer {
    public void setup(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();
    }

}