你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

Python 功能管理

Python 功能管理库提供了基于功能标志开发和公开应用程序功能的方法。 开发新功能后,许多应用程序都有特殊要求,例如应何时启用此功能以及在哪些条件下启用该功能。 此库提供了一种方法来定义这些关系,并且已集成到常见的 Python 代码模式中,使公开这些功能成为可能。

功能标志为 Python 应用程序提供了一种动态打开或关闭功能的方法。 开发人员可以在简单的用例(如条件语句)中使用功能标志。

下面是使用 Python 功能管理库的一些优势:

  • 功能管理的常见约定

  • 低准入门槛

    • 支持 JSON 功能标志设置
  • 功能标志生存期管理

    • 配置值可以实时更改;功能标志可以在整个请求中保持一致
  • 涵盖简单到复杂的方案

    • 通过声明性配置文件打开/关闭功能
    • 基于对服务器的调用来动态评估功能的状态

    Python 功能管理库是开源的。 有关详细信息,请访问 GitHub 存储库

特性标志

功能标志由两个部分组成,即名称和用于打开功能的功能筛选器列表。

功能筛选器

功能筛选器定义应启用功能的场景。 当评估某项功能是应打开还是关闭时,将遍历其功能筛选器列表,直到其中一个筛选器确定应启用该功能。 此时,该功能被视为已启用,并停止遍历功能筛选器。 如果没有功能筛选器指示应启用该功能,则它被视为已禁用。

例如,可以设计 Microsoft Edge 浏览器功能筛选器。 只要 HTTP 请求来自 Microsoft Edge,此功能筛选器就会激活所附加的任何功能。

功能标志配置

Python 字典用于定义功能标志。 该字典由作为键的功能名称和作为值的功能标志对象组成。 功能标志对象是包含 conditions 键的字典,它本身包含 client_filters 键。 client_filters 键是用于确定是否应启用该功能的功能筛选器列表。

功能标志声明

功能管理库支持 JSON 作为功能标志源。 下面是用于在 JSON 文件中设置功能标志的格式的示例。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureT",
                "enabled": "true"
            },
            {
                "id": "FeatureU",
                "enabled": "false"
            },
            {
                "id": "FeatureV",
                "enabled": "true",
                "conditions": {
                    "client_filters": [
                        {
                            "name": "Microsoft.TimeWindow",
                            "parameters": {
                                "Start": "Wed, 01 May 2019 13:59:59 GMT",
                                "End": "Mon, 01 Jul 2019 00:00:00 GMT"
                            }
                        }
                    ]
                }
            }
        ]
    }
}

按照约定,json 文档的 feature_management 部分用于加载功能标志设置。 feature_flags 部分是加载到库中的功能标志的列表。 在上面的部分中,我们看到了三个不同的功能。 这些功能使用 client_filters(而不是 conditions)属性来定义其功能筛选器。 在 FeatureT 的功能筛选器中,我们看到 enabled 为“开启”且没有定义筛选器,导致 FeatureT 始终返回 trueFeatureUFeatureT 相同,但 enabledfalse,导致功能始终返回 falseFeatureV 指定名为 Microsoft.TimeWindow 的功能筛选器。 FeatureV 是可配置的功能筛选器的示例。 在该示例中可以看到筛选器具有 parameters 属性。 parameters 属性用于配置筛选器。 在本例中,将配置功能处于活动状态的开始和结束时间。

可在此处找到 feature_management 部分的详细架构。

高级:禁止在功能标志名称中使用冒号“:”。

开/关声明

以下代码片段演示了一种替代方法来定义可用于开/关功能的功能。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureT",
                "enabled": "true"
            },
            {
                "id": "FeatureX",
                "enabled": "false"
            }
        ]
    }
}

Requirement_type

功能标志的 requirement_type 属性用于确定筛选器在评估功能状态时应使用 Any 还是 All 逻辑。 如果未指定 requirement_type,则默认值为 Any

  • Any 表示只需一个筛选器评估为 true 即可启用该功能。
  • All 表示每个筛选器都需要评估为 true 才能启用该功能。

如果 requirement_typeAll,则会更改遍历。 首先,如果没有筛选器,则该功能处于禁用状态。 然后,将遍历功能筛选器,直到其中一个筛选器确定应禁用该功能。 如果没有筛选器指示应禁用该功能,则它被视为已启用。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureW",
                "enabled": "true",
                "conditions": {
                    "requirement_type": "All",
                    "client_filters": [
                        {
                            "name": "Microsoft.TimeWindow",
                            "parameters": {
                                "Start": "Wed, 01 May 2019 13:59:59 GMT",
                                "End": "Mon, 01 Jul 2019 00:00:00 GMT"
                            }
                        },
                        {
                            "name": "Percentage",
                            "parameters": {
                                "Value": "50"
                            }
                        }
                    ]
                }
            },
        ]
    }
}

在上面的示例中,FeatureW 指定 requirement_typeAll,这意味着所有筛选器都必须评估为 true 才能启用该功能。 在本例中,在指定时间范围内为 50% 的用户启用了该功能。

消耗

功能管理的基本形式是检查是否启用了功能标志,然后根据结果执行操作。 通过 FeatureManageris_enabled 方法检查功能标志的状态。

…
feature_manager = FeatureManager(feature_flags)
…
if feature_manager.is_enabled("FeatureX"):
    # Do something

提供给 FeatureManagerfeature_flags 可以是 AzureAppConfigurationProvider,也可以是功能标志字典。

实现功能筛选器

创建功能筛选器提供了一种基于所定义条件启用功能的方法。 若要实现功能筛选器,必须实现 FeatureFilter 接口。 FeatureFilter 有一个名为 evaluate 的方法。 当功能指定可为功能筛选器启用它时,将调用 evaluate 方法。 如果 evaluate 返回 true,则表示应启用该功能。

以下代码片段演示如何添加自定义功能筛选器 MyCustomFilter

feature_manager = FeatureManager(feature_flags, feature_filters=[MyCustomFilter()])

创建 FeatureManager 时,向功能筛选器提供属性 feature_filters 以注册功能筛选器。 如果自定义功能筛选器需要任何上下文,则可以在使用 kwargs 调用 is_enabled 时传入它们。

筛选器别名属性

为功能标志注册功能筛选器时,筛选器的名称默认用作别名。

可以使用 @FeatureFilter.alias("MyFilter") 替代功能筛选器的标识符。 可以使用此属性修饰功能筛选器,以声明应在配置中使用的名称,从而在功能标志中引用此功能筛选器。

缺少功能筛选器

如果将某项功能配置为针对特定功能筛选器启用,并且该功能筛选器未注册,则会在评估该功能时引发 ValueError 异常。

内置功能筛选器

有两个功能筛选器附带 FeatureManagement 包:TimeWindowFilterTargetingFilter

每个内置功能筛选器都有自己的参数。 以下是功能筛选器的列表和示例。

Microsoft.TimeWindow

此筛选器提供根据时间窗口来启用某项功能的功能。 如果仅指定了 End,则在此时间之前该功能被视为已启用。 如果仅指定了 Start,则在该时间之后的所有时间点,该功能被视为已启用。

"client_filters": [
    {
        "name": "Microsoft.TimeWindow",
        "parameters": {
            "Start": "Wed, 01 May 2019 13:59:59 GMT",
            "End": "Mon, 01 Jul 2019 00:00:00 GMT"
        }
    }
]     

Microsoft.Targeting

此筛选器提供为目标受众启用某项功能的功能。 以下目标部分对此进行了深入说明。 筛选器参数包括一个 Audience 对象,该对象描述用户、组、已排除的用户/组,以及应该有权访问该功能的用户群的默认百分比。 Groups 部分中列出的每个组对象还必须指定应具有访问权限的组成员的百分比。 如果直接在 Exclusion 部分中指定了用户,或者该用户在排除的组中,则该功能将被禁用。 否则,如果直接在 Users 部分中指定了用户,或者如果用户处于任何组推出包含的百分比中,或者处于默认推出百分比中,则此用户将启用该功能。

"client_filters": [
    {
        "name": "Microsoft.Targeting",
        "parameters": {
            "Audience": {
                "Users": [
                    "Jeff",
                    "Alicia"
                ],
                "Groups": [
                    {
                        "Name": "Ring0",
                        "RolloutPercentage": 100
                    },
                    {
                        "Name": "Ring1",
                        "RolloutPercentage": 50
                    }
                ],
                "DefaultRolloutPercentage": 20,
                "Exclusion": {
                    "Users": [
                        "Ross"
                    ],
                    "Groups": [
                        "Ring2"
                    ]
                }
            }
        }
    }
]

目标设定

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

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

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

此功能推出策略通过随附的 Microsoft.Targeting 功能筛选器内置到库中。

将用户设为目标

可以直接在 is_enabled 调用中指定用户,也可以使用 TargetingContext 来指定用户和可选组。

# Directly specifying the user
result = is_enabled(feature_flags, "test_user")

# Using a TargetingContext
result = is_enabled(feature_flags, TargetingContext(user_id="test_user", groups=["Ring1"]))

目标排除

定义受众时,可以从该受众中排除用户和组。 如果要向一组用户推出某项功能,但需要从推出中排除一些用户或组,则排除功能非常有用。 通过将用户和组列表添加到受众的 Exclusion 属性来定义排除。

"Audience": {
    "Users": [
        "Jeff",
        "Alicia"
    ],
    "Groups": [
        {
            "Name": "Ring0",
            "RolloutPercentage": 100
        }
    ],
    "DefaultRolloutPercentage": 0
    "Exclusion": {
        "Users": [
            "Mark"
        ]
    }
}

在上面的示例中,为名为 JeffAlicia 的用户启用功能。 它还为名为 Ring0 的组中的用户启用该功能。 但是,如果用户命名为 Mark,则会禁用该功能,而不考虑其是否位于组 Ring0 中。 排除的优先级高于目标筛选器的其余部分。

变量

将新功能添加到应用程序时,有时某项功能具有多个不同的建议设计选项。 决定设计的常见解决方案是采用某种形式的 A/B 测试。 A/B 测试涉及向不同的用户群体提供不同的功能版本,并根据用户交互选择版本。 在此库中,通过用变体表示功能的不同配置来启用此功能。

变体使功能标志变得不仅仅是一个简单的开/关标志。 变体表示功能标志的值,可以是字符串、数字、布尔值,甚至是配置对象。 用于声明变体的功能标志应定义每个变体的使用条件,这将在分配变体部分中进行更详细的介绍。

class Variant:
    def __init__(self, name: str, configuration: Any):
        self._name = name
        self._configuration = configuration

    @property
    def name(self) -> str:
        """
        The name of the variant.
        :rtype: str
        """
        return self._name

    @property
    def configuration(self) -> Any:
        """
        The configuration of the variant.
        :rtype: Any
        """
        return self._configuration

获取变体

对于每个功能,可以使用 FeatureManagerget_variant 方法检索变体。

…
variant = print(feature_manager.get_variant("TestVariants", TargetingContext(user_id="Adam"))

variantConfiguration = variant.configuration;

// Do something with the resulting variant and its configuration

返回的变体取决于当前正在评估的用户,并且该信息是从 TargetingContext 的实例中获取的。

变体功能标志声明

与普通功能标志相比,变体功能标志具有两个附加属性:variantsallocationvariants 属性是一个数组,其中包含为此功能定义的变体。 allocation 属性定义为该功能分配这些变体的方式。 与声明普通功能标志一样,可以在 JSON 文件中设置变体功能标志。 以下是变体功能标志的示例。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyVariantFeatureFlag",
                "enabled": true,
                "allocation": {
                    "default_when_enabled": "Small",
                    "group": [
                        {
                            "variant": "Big",
                            "groups": [
                                "Ring1"
                            ]
                        }
                    ]
                },
                "variants": [
                    { 
                        "name": "Big"
                    },  
                    { 
                        "name": "Small"
                    } 
                ]
            }
        ]
    }
}

定义变体

每个变体都有两个属性:名称和配置。 名称用于引用特定变体,而配置则是该变体的值。 可以使用 configuration_value 属性设置配置。 configuration_value 是一个内联配置,它可以是字符串、数字、布尔值或配置对象。 如果未指定 configuration_value,则返回的变体的 Configuration 属性将为 None

已为 variants 属性下的每个功能定义所有可能的变体的列表。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyVariantFeatureFlag",
                "variants": [
                    { 
                        "name": "Big", 
                        "configuration_value": {
                            "Size": 500
                        }
                    },  
                    { 
                        "name": "Small", 
                        "configuration_value": {
                            "Size": 300
                        }
                    } 
                ]
            }
        ]
    }
}

分配变体

分配功能变体的过程由该功能的 allocation 属性确定。

"allocation": { 
    "default_when_enabled": "Small", 
    "default_when_disabled": "Small",  
    "user": [ 
        { 
            "variant": "Big", 
            "users": [ 
                "Marsha" 
            ] 
        } 
    ], 
    "group": [ 
        { 
            "variant": "Big", 
            "groups": [ 
                "Ring1" 
            ] 
        } 
    ],
    "percentile": [ 
        { 
            "variant": "Big", 
            "from": 0, 
            "to": 10 
        } 
    ], 
    "seed": "13973240" 
},
"variants": [
    { 
        "name": "Big", 
        "configuration_value": "500px"
    },  
    { 
        "name": "Small", 
        "configuration_value": "300px"
    } 
]

功能的 allocation 设置具有以下属性:

properties 说明
default_when_disabled 指定在该功能被视为已禁用的情况下请求变体时应使用哪个变体。
default_when_enabled 指定在以下情况下请求变体时应使用哪个变体,即如果该功能被视为已启用,并且没有向用户分配其他变体。
user 指定变体以及应将该变体分配到的用户列表。
group 指定变体和组列表。 如果用户至少位于其中一个组,则会分配该变体。
percentile 指定一个变体和一个百分比范围,计算出的用户百分比必须处于此范围内才能分配该变体。
seed percentile 的百分比计算所基于的值。 如果使用相同的 seed 值,则特定用户的百分比计算在所有功能中都是相同的。 如果未指定 seed,则会根据功能名称创建默认种子。

如果未启用该功能,则功能管理器会将标记为 default_when_disabled 的变体分配给当前用户,在本例中为 Small

如果已启用该功能,则功能管理器将按该顺序检查 usergrouppercentile 分配,以分配变体。 对于此特定示例,如果接受评估的用户在名为 Ring1 的组中命名为 Marsha,或者该用户恰好位于 0 和第 10 个百分位数之间,则会将指定的变体分配给该用户。 在这种情况下,所有分配的用户都会返回 Big 变体。 如果这些分配都不匹配,则会为用户分配 default_when_enabled 变体,它是 Small

分配逻辑类似于 Microsoft.Targeting 功能筛选器,但有一些存在于目标中的参数不在分配中,反之亦然。 目标和分配的结果不相关。

使用变体替代已启用状态

可以使用变体来替代功能标志的已启用状态。 替代为变体提供了扩展功能标志评估的机会。 使用变体对标志调用 is_enabled 时,功能管理器将检查分配给当前用户的变体是否配置为替代结果。 替代是使用可选变量属性 status_override 完成的。 默认情况下,此属性设置为 None,这意味着变体不会影响标志是被视为已启用还是已禁用。 将 status_override 设置为 Enabled 允许所选变体替代要启用的标志。 将 status_override 设置为 Disabled 会提供相反的功能,因此在选择变体时会禁用标志。 无法替代 enabled 状态为 false 的功能。

如果使用具有二进制变体的功能标志,则 status_override 属性非常有用。 它可让你继续在应用程序中使用 is_enabled 等 API,同时受益于变体附带的新功能,例如百分位数分配和种子。

{
    "id": "MyVariantFeatureFlag",
    "enabled": true,
    "allocation": {
        "percentile": [
            {
                "variant": "On",
                "from": 10,
                "to": 20
            }
        ],
        "default_when_enabled":  "Off",
        "seed": "Enhanced-Feature-Group"
    },
    "variants": [
        {
            "name": "On"
        },
        {
            "name": "Off",
            "status_override": "Disabled"
        }
    ]
}

在上面的示例中,始终启用该功能。 如果当前用户在计算出的第 10 到第 20 百分位数范围内,则会返回 On 变体。 否则,将返回 Off 变体,因为 status_override 等于 Disabled,因此该功能现在被视为已禁用。

遥测

部署功能标志更改时,分析其对应用程序的影响通常很重要。 例如,可能会出现以下几个问题:

  • 我的标志是否按预期启用/禁用?
  • 目标用户是否按预期获得对特定功能的访问权限?
  • 特定用户看到的是哪个变体?

可以通过功能标志评估事件的发出和分析来回答这类问题。 此库提供 AzureMonitor 选项,可在功能标志评估期间通过 OpenTelemetry 生成跟踪遥测数据。

启用遥测

默认情况下,功能标志不发出遥测。 若要发布给定功能标志的遥测,该标志必须声明已启用遥测发出

对于在 JSON 中定义的功能标志,可使用 telemetry 属性启用。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyFeatureFlag",
                "enabled": true,
                "telemetry": {
                    "enabled": true
                }
            }
        ]
    }
}

上面的代码片段定义了一个名为 MyFeatureFlag 的功能标志,该标志已启用遥测。 telemetry 对象的 enabled 属性设置为 trueenabled 属性的值必须为 true 才能发布标志的遥测。

功能标志的 telemetry 部分具有以下属性:

properties 说明
enabled 指定是否应为功能标志发布遥测。
metadata 键值对的集合(建模为字典),可用于将有关功能标志的自定义元数据附加到评估事件。

此外,创建 FeatureManager 时,必须注册回调来处理遥测事件。 每当评估功能标志并为该标志启用遥测时,都会调用此回调。

feature_manager = FeatureManager(feature_flags, on_feature_evaluated=publish_telemetry)

Application Insights 遥测

功能管理库提供内置的遥测发布者服务器,用于将功能标志评估数据发送到 Application Insights。 若要启用 Application Insights,可以使用 Azure Monitor 通过 pip install FeatureManagement[AzureMonitor] 安装功能管理库。 此命令将安装 azure-monitor-events-extension 包,该包用于使用 OpenTelemetry 将遥测样式设置为 Application Insights。

注意

azure-monitor-events-extension 包仅将遥测添加到开放遥测管道。 仍需要注册 Application Insights。

from azure.monitor.opentelemetry import configure_azure_monitor

configure_azure_monitor(
        connection_string="InstrumentationKey=00000000-0000-0000-0000-000000000000"
    )

自定义遥测发布

由于遥测回调是一个函数,因此可以对其进行自定义,以便将遥测数据发布到任何所需目标。 例如,可以将遥测发布到日志记录服务、数据库或自定义遥测服务。

评估功能标志并启用遥测时,功能管理器将使用 EvaluationEvent 参数调用遥测回调。 EvaluationEvent 包含以下属性:

标签 说明
feature 使用的功能标志。
user 用于定向的用户 ID。
enabled 是否评估功能标志是否已启用。
Variant 分配的变体。
VariantAssignmentReason 分配变体的原因。

后续步骤

若要了解如何在应用程序中使用功能标志,请继续阅读以下快速入门。

若要了解如何使用功能筛选器的信息,请继续学习以下教程。