JavaScript 功能管理库提供了基于功能标志开发和公开应用程序功能的方法。 开发新功能后,许多应用程序都有特殊要求,例如应何时启用此功能以及在哪些条件下启用该功能。 此库提供了一种方法来定义这些关系,并且已集成到常见的 JavaScript 代码模式中,使公开这些功能成为可能。
功能标志为 JavaScript 应用程序提供了一种动态打开或关闭功能的方法。 开发人员可以在简单的用例(如条件语句)中使用功能标志。
下面是使用 JavaScript功能管理库的一些优势:
- 功能管理的常见约定
- 低准入门槛
- 支持 JSON 对象和基于映射的功能标志源
- 支持在 Node.js 和浏览器环境中使用
- 使用 Azure 应用程序配置进行功能标志生存期管理
- 配置值可以实时更改
- 涵盖从简单到复杂的场景
- 通过声明性配置文件打开/关闭功能
- 基于对服务器的调用来动态评估功能的状态
JavaScript 功能管理库是开源的。 有关详细信息,请访问 GitHub 存储库。
注释
建议将功能管理库与 Azure 应用配置一起使用。 Azure 应用程序配置提供集中管理应用程序设置和功能标志的解决方案。 有关更多详细信息,请参阅本部分。
功能标志
功能标志由两个部分组成,即名称和用于打开功能的功能筛选器列表。
功能筛选器
功能筛选器定义应启用功能的场景。 当评估某项功能是应打开还是关闭时,将遍历其功能筛选器列表,直到其中一个筛选器确定应启用该功能。 此时,该功能被视为已启用,并停止遍历功能筛选器。 如果没有功能筛选器指示应启用该功能,则它被视为已禁用。
例如,可以设计 Microsoft Edge 浏览器功能筛选器。 只要 HTTP 请求来自 Microsoft Edge,此功能筛选器就会激活所附加的任何功能。
功能标志配置
在 JavaScript 中,开发人员通常使用对象或映射作为主要数据结构来表示配置。 JavaScript 功能管理库支持这两种配置方法,使开发人员能够灵活选择最适合其需求的选项。
FeatureManager
可以使用内置的 ConfigurationObjectFeatureFlagProvider
和 ConfigurationMapFeatureFlagProvider
,从不同类型的配置中读取功能标志位。
const config = new Map([
["feature_management", {
"feature_flags": [
{
"id": "FeatureT",
"enabled": true
},
{
"id": "FeatureU",
"enabled": false
}
]
}],
["some other configuration", " some value"]
]);
import { ConfigurationMapFeatureFlagProvider, FeatureManager } from "@microsoft/feature-management";
const featureProvider = new ConfigurationMapFeatureFlagProvider(config);
const featureManager = new FeatureManager(featureProvider);
从 Azure 应用程序配置使用功能标志
建议不要将功能标志硬编码到应用程序中,而应将功能标志保留在应用程序外部并单独对其进行管理。 这样便可以随时修改标志状态,并使这些更改在应用程序中立即生效。 Azure 应用配置服务提供了一个专用门户 UI,用于管理所有功能标志。 请参阅教程。
Azure 应用配置服务还通过其 JavaScript 客户端库 @azure/app-configuration-provider 直接将功能标志传送到应用程序。 以下示例显示了如何使用库。
应用配置 JavaScript 提供程序提供 Map
对象中的功能标志。 内置 ConfigurationMapFeatureFlagProvider
有助于在本例中加载功能标志。
import { DefaultAzureCredential } from "@azure/identity";
import { load } from "@azure/app-configuration-provider";
import { ConfigurationMapFeatureFlagProvider, FeatureManager } from "@microsoft/feature-management";
const appConfig = await load("YOUR_APP-CONFIG-ENDPOINT",
new DefaultAzureCredential(), // For more information: https://learn.microsoft.com/javascript/api/overview/azure/identity-readme
{ featureFlagOptions: { enabled: true } }); // load feature flags from Azure App Configuration service
const featureProvider = new ConfigurationMapFeatureFlagProvider(appConfig);
const featureManager = new FeatureManager(featureProvider);
使用 Azure 应用配置动态控制功能标志的状态
Azure 应用配置不仅是一种解决方案,用于外部化存储和集中管理功能标志,还允许你动态打开/关闭功能标志。
若要为功能标志启用动态刷新,需要在从 Azure 应用配置加载功能标志时配置 refresh
的属性 featureFlagOptions
。
const appConfig = await load("YOUR_APP-CONFIG-ENDPOINT", new DefaultAzureCredential(), {
featureFlagOptions: {
enabled: true,
refresh: {
enabled: true, // enable the dynamic refresh for feature flags
refreshIntervalInMs: 30_000
}
}
});
const featureProvider = new ConfigurationMapFeatureFlagProvider(appConfig);
const featureManager = new FeatureManager(featureProvider);
需要调用 refresh
该方法以获取最新的功能标志状态。
await appConfig.refresh(); // Refresh to get the latest feature flags
const isBetaEnabled = await featureManager.isEnabled("Beta");
console.log(`Beta is enabled: ${isBetaEnabled}`);
注释
有关如何将功能管理库与 Azure 应用配置配合使用的详细信息,请转到快速入门。
功能标志声明
以下示例显示了用于在 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"
}
}
]
}
}
]
}
}
按照约定,feature_management
部分用于加载功能标志设置。
feature_flags
部分是加载到库中的功能标志的列表。 在上面的部分中,我们看到了三个不同的功能。 这些功能使用 client_filters
(而不是 conditions
)属性来定义其功能筛选器。 在 FeatureT
的功能筛选器中,我们看到 enabled
为 true
,且没有定义筛选器,导致 FeatureT
始终返回 true
。
FeatureU
与 FeatureT
相同,但 enabled
为 false
,导致功能始终返回 false
。
FeatureV
指定名为 Microsoft.TimeWindow
的功能筛选器。
FeatureV
是可配置的功能筛选器的示例。 在该示例中可以看到筛选器具有 parameters
属性。
parameters
属性用于配置筛选器。 在本例中,配置功能激活的开始和结束时间。
可在feature_management
找到 部分的详细架构。
高级:禁止在功能标志名称中使用冒号“:”。
要求类型
功能标志的 requirement_type
属性用于确定筛选器在评估功能状态时应使用 Any
还是 All
逻辑。 如果未指定 requirement_type
,则默认值为 Any
。
-
Any
表示只需一个筛选器评估为 true 即可启用该功能。 -
All
表示每个筛选器都需要评估为 true 才能启用该功能。
requirement_type
的一个 All
会更改遍历。 首先,如果没有筛选器,则该功能处于禁用状态。 然后,将遍历功能筛选器,直到其中一个筛选器确定应禁用该功能。 如果没有筛选器指示应禁用该功能,则它被视为已启用。
{
"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_type
为 All
,这意味着所有筛选器都必须评估为 true 才能启用该功能。 在本例中,在指定时间范围内为 50% 的用户启用了该功能。
消耗
功能管理的基本形式是检查是否启用了功能标志,然后根据结果执行操作。 通过 FeatureManager
的 isEnabled
方法检查功能标志的状态。
import { ConfigurationMapFeatureFlagProvider, FeatureManager } from "@microsoft/feature-management";
const featureProvider = new ConfigurationMapFeatureFlagProvider(config);
const featureManager = new FeatureManager(featureProvider);
const isBetaEnabled = await featureManager.isEnabled("Beta");
if (isBetaEnabled) {
// Do something
}
实现功能筛选器
创建功能筛选器提供了一种基于所定义条件启用功能的方法。 若要实现功能筛选器,必须实现 IFeatureFilter
接口。
IFeatureFilter
具有 name
属性和名为 evaluate
的方法。 应在配置中使用 name
来引用功能标志中的功能筛选器。 当某项功能可以为功能筛选器启用时,将调用evaluate
方法。 如果 evaluate
返回 true
,则表示应启用该功能。
interface IFeatureFilter {
name: string;
evaluate(context: IFeatureFilterEvaluationContext, appContext?: unknown): boolean | Promise<boolean>;
}
以下代码片段演示如何实现名称为 MyCriteria
的自定义功能筛选器。
class MyCriteriaFilter {
name = "MyCriteria";
evaluate(context, appContext) {
if (satisfyCriteria()) {
return true;
}
else {
return false;
}
}
}
需要在传递给 customFilters
构造函数的 FeatureManagerOptions
对象的 FeatureManager
属性下注册自定义筛选器。
const featureManager = new FeatureManager(ffProvider, {
customFilters: [
new MyCriteriaFilter() // add custom feature filters under FeatureManagerOptions.customFilters
]
});
参数化功能筛选器
某些功能筛选器需要参数来确定是否应打开某项功能。 例如,浏览器功能筛选器可能会为一组特定浏览器打开某项功能。 Edge 和 Chrome 浏览器可能需要启用某项功能,而 Firefox 则不需要。 为此,可以将功能筛选器设计为预期参数。 这些参数将在功能配置中指定,并且在代码中可以通过 IFeatureFilterEvaluationContext
的 IFeatureFilter.Evaluate
参数进行访问。
interface IFeatureFilterEvaluationContext {
featureName: string;
parameters?: unknown;
}
IFeatureFilterEvaluationContext
具有名为 parameters
的属性。 这些参数表示原始配置,功能筛选器可以使用该配置来确定如何评估是否应启用该功能。 再次以浏览器功能筛选器为例,该筛选器可以使用 parameters
提取一组为该功能指定的允许使用的浏览器,然后检查请求是否是从其中一个浏览器发送的。
使用应用程序上下文进行功能评估
功能筛选器可能需要运行时应用程序上下文来评估功能标志。 调用 isEnabled
时,可以将上下文作为参数传入。
featureManager.isEnabled("Beta", { userId : "Sam" })
功能筛选器可以利用调用 isEnabled
时传入的上下文。 应用程序上下文将作为 IFeatureFilter.Evaluate
的第二个参数传入。
内置功能筛选器
FeatureManagement
包附带两个功能筛选器:TimeWindowFilter
和 TargetingFilter
。 构造FeatureManager
时,默认情况下会添加所有内置功能筛选器。
每个内置功能筛选器都有自己的参数。 以下是功能筛选器的列表和示例。
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"
}
}
]
可以将时间窗口配置为定期重复出现。 对于可能需要在一天或一周中某些天的低流量或高流量期间启用功能的情况,这非常有用。 若要将单个时间窗口扩展为定期时间窗口,应在 Recurrence
参数中指定重复执行规则。
注释
必须同时指定 Start
和 End
才能启用 Recurrence
。
"client_filters": [
{
"name": "Microsoft.TimeWindow",
"parameters": {
"Start": "Fri, 22 Mar 2024 20:00:00 GMT",
"End": "Sat, 23 Mar 2024 02:00:00 GMT",
"Recurrence": {
"Pattern": {
"Type": "Daily",
"Interval": 1
},
"Range": {
"Type": "NoEnd"
}
}
}
}
]
Recurrence
设置由两个部分组成:Pattern
(时间窗口重复的频率)和 Range
(重复执行模式重复的时长)。
定期模式
有两种可能的重复执行模式类型:Daily
和 Weekly
。 例如,时间窗口可以“每天”、“每三天”、“每星期一”或“每隔一个星期五”重复。
根据类型,Pattern
的某些字段是必填、可选或可忽略的。
Daily
每日重复执行模式会导致时间窗口根据每次出现间隔的天数进行重复。
资产 相关性 DESCRIPTION 类型 必选 必须设置为 Daily
。Interval 可选 指定每次出现之间的天数。 默认值为 1。 Weekly
每周重复执行模式会导致时间窗口在一周中的同一天或几天重复,具体取决于每组重复间隔的周数。
资产 相关性 DESCRIPTION 类型 必选 必须设置为 Weekly
。DaysOfWeek 必选 指定事件发生在星期几。 Interval 可选 指定每组重复间隔的周数。 默认值为 1。 FirstDayOfWeek 可选 指定哪一天被视为一周的第一天。 默认值为 Sunday
。以下示例每隔一个星期一和星期二重复一次时间窗口
"Pattern": { "Type": "Weekly", "Interval": 2, "DaysOfWeek": ["Monday", "Tuesday"] }
注释
Start
必须是符合重复执行模式的第一个有效匹配项。 此外,时间窗口的持续时间不能超过它的发生频率。 例如,每天重复 25 小时的时间窗口无效。
重复执行范围
有三种可能的重复执行范围类型:NoEnd
、EndDate
和 Numbered
。
NoEnd
NoEnd
范围会导致重复执行无限期地发生。资产 相关性 DESCRIPTION 类型 必选 必须设置为 NoEnd
。EndDate
EndDate
范围会导致时间窗口在符合适用模式的所有日期发生,直到结束日期为止。资产 相关性 DESCRIPTION 类型 必选 必须设置为 EndDate
。EndDate 必选 指定停止应用模式的日期时间。 只要最后一次发生的开始时间在结束日期之前,就允许该次发生的结束时间超出该日期。 以下示例将每天重复时间窗口,直到最后一次事件发生在 2024 年 4 月 1 日。
"Start": "Fri, 22 Mar 2024 18:00:00 GMT", "End": "Fri, 22 Mar 2024 20:00:00 GMT", "Recurrence":{ "Pattern": { "Type": "Daily", "Interval": 1 }, "Range": { "Type": "EndDate", "EndDate": "Mon, 1 Apr 2024 20:00:00 GMT" } }
Numbered
Numbered
范围会导致时间窗口出现固定次数(基于模式)。资产 相关性 DESCRIPTION 类型 必选 必须设置为 Numbered
。NumberOfOccurrences 必选 指定发生次数。 以下示例将在星期一和星期二重复时间窗口,直到出现三次,分别发生在 4 月 1 日(星期一)、4 月 2 日(星期二)和 4 月 8 日(星期一)。
"Start": "Mon, 1 Apr 2024 18:00:00 GMT", "End": "Mon, 1 Apr 2024 20:00:00 GMT", "Recurrence":{ "Pattern": { "Type": "Weekly", "Interval": 1, "DaysOfWeek": ["Monday", "Tuesday"] }, "Range": { "Type": "Numbered", "NumberOfOccurrences": 3 } }
若要创建重复执行规则,必须同时指定 Pattern
和 Range
。 任何模式类型都可以与任何范围类型一起使用。
高级:Start
属性的时区偏移量将应用于重复执行设置。
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 版”功能的渐进式推出示例:
- 个人用户 Jeff 和 Alicia 有权访问 Beta 版。
- 另一位用户 Mark 请求加入并被接纳。
- 一组称为“Ring1”的用户中有 20% 被包含在 Beta 版中。
- Beta 版中包含的“Ring1”用户数量增加到 100%。
- Beta 版中包含 5% 的用户群。
- 推出百分比将提升到 100%,该功能已完全推出。
此功能推出策略通过随附的 Microsoft.Targeting 功能筛选器内置到库中。
面向具有目标定位上下文的用户
目标筛选器依赖于目标上下文来评估是否应启用某项功能。 此目标上下文包含的信息,包括当前正在评估的特定用户,以及用户所属的群组。 调用 isEnabled
时,必须直接传递目标上下文。
featureManager.isEnabled("Beta", { userId: "Aiden", groups: ["Ring1"] })
目标排除
定义受众时,可以从该受众中排除用户和组。 如果要向一组用户推出某项功能,但需要从推出中排除一些用户或组,则排除功能非常有用。 通过将用户和组列表添加到受众的 Exclusion
属性来定义排除。
"Audience": {
"Users": [
"Jeff",
"Alicia"
],
"Groups": [
{
"Name": "Ring0",
"RolloutPercentage": 100
}
],
"DefaultRolloutPercentage": 0,
"Exclusion": {
"Users": [
"Mark"
]
}
}
在上面的示例中,为名为 Jeff
和 Alicia
的用户启用功能。 它还为名为 Ring0
的组中的用户启用该功能。 但是,如果用户命名为 Mark
,则会禁用该功能,而不考虑其是否位于组 Ring0
中。 排除项优先于目标定位过滤器的其他部分。
Web 应用程序中的目标
此示例项目中提供了使用目标功能筛选器的示例 Web 应用程序。
在 Web 应用程序(尤其是具有多个组件或层的应用程序)中,将目标上下文(userId
和 groups
)传递给每个功能标志检查的操作可能会变得繁琐且重复。 这种场景称为“环境目标上下文”,其中的用户标识信息已经在应用程序上下文(例如会话数据或身份验证上下文)中提供,但需要能够在整个应用程序中供功能管理评估访问。
ITargetingContextAccessor
该库通过 ITargetingContextAccessor
模式提供解决方案。
interface ITargetingContext {
userId?: string;
groups?: string[];
}
interface ITargetingContextAccessor {
getTargetingContext: () => ITargetingContext | undefined;
}
可以提供一个知道如何从应用程序的上下文中检索当前用户的目标信息的函数,而不必在每次进行 isEnabled
或 getVariant
调用时都显式传递目标上下文:
import { FeatureManager, ConfigurationObjectFeatureFlagProvider } from "@microsoft/feature-management";
// Create a targeting context accessor that uses your application's auth system
const targetingContextAccessor = {
getTargetingContext: () => {
// In a web application, this might access request context or session data
// This is just an example - implement based on your application's architecture
return {
userId: getCurrentUserId(), // Your function to get current user
groups: getUserGroups() // Your function to get user groups
};
}
};
// Configure the feature manager with the accessor
const featureManager = new FeatureManager(featureProvider, {
targetingContextAccessor: targetingContextAccessor
});
// Now you can call isEnabled without explicitly providing targeting context
// The feature manager will use the accessor to get the current user context
const isBetaEnabled = await featureManager.isEnabled("Beta");
此模式在服务器端 Web 应用程序中特别有用,这些程序中的用户上下文可以在请求范围内使用,也可以在集中管理用户标识的客户端应用程序中使用。
将 AsyncLocalStorage 用于请求上下文
实现目标上下文访问器模式时的一个常见挑战是在整个异步调用链中维护请求上下文。 在 Node.js Web 应用程序中,用户标识信息通常在请求对象中可用,但在输入异步作后不可访问。
Node.js 提供 AsyncLocalStorage
模块中的 async_hooks
来解决此问题。 它创建一个存储区,可以在同一逻辑“上下文”内跨异步操作保持持久性 - 非常适合在整个请求生命周期内维护请求数据。
下面介绍如何在 Express 应用程序中使用 AsyncLocalStorage 实现目标上下文访问器:
import { AsyncLocalStorage } from "async_hooks";
import express from "express";
const requestAccessor = new AsyncLocalStorage();
const app = express();
// Middleware to store request context
app.use((req, res, next) => {
// Store the request in AsyncLocalStorage for this request chain
requestAccessor.run(req, () => {
next();
});
});
// Create targeting context accessor that retrieves user data from the current request
const targetingContextAccessor = {
getTargetingContext: () => {
// Get the current request from AsyncLocalStorage
const request = requestAccesor.getStore();
if (!request) {
return undefined; // Return undefined if there's no current request
}
// Extract user data from request (from session, auth token, etc.)
return {
userId: request.user?.id,
groups: request.user?.groups || []
};
}
};
变量
将新功能添加到应用程序时,有时某项功能具有多个不同的建议设计选项。 决定设计的一个常见解决方案是某种形式的 A/B 测试,它涉及向不同的用户群体提供不同的功能版本,并根据用户交互选择版本。 在此库中,通过用变体表示功能的不同配置来启用此功能。
变体使功能标志变得不仅仅是一个简单的开/关标志。 变体表示功能标志的值,可以是字符串、数字、布尔值,甚至是配置对象。 用于声明变体的功能标志应定义每个变体的使用条件,这将在分配变体部分中进行更详细的介绍。
获取具有目标定位上下文的变体
对于每个功能,可以使用 FeatureManager
的 getVariant
方法检索变体。 变体分配取决于当前正在评估的用户,并且该信息是从您提供的定位上下文中获取的。 如果您已向 FeatureManager
注册了一个目标上下文访问器,则系统将自动从其中检索目标上下文。 但是,在调用 getVariant
时,仍可以通过手动传递目标上下文来替代它。
const variant = await featureManager.getVariant("MyVariantFeatureFlag", { userId: "Sam" });
const variantName = variant.name;
const variantConfiguration = variant.configuration;
// Do something with the resulting variant and its configuration
变种功能标志声明
与普通功能标志相比,变体功能标志具有两个附加属性:variants
和 allocation
。
variants
属性是一个数组,其中包含为此功能定义的变体。
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
属性将为 undefined
。
已为 variants
属性下的每个功能定义所有可能的变体的列表。
{
"feature_management": {
"feature_flags": [
{
"id": "MyVariantFeatureFlag",
"variants": [
{
"name": "Big",
"configuration_value": {
"Size": 500
}
},
{
"name": "Small",
"configuration_value": {
"Size": 300
}
}
]
}
]
}
}
分配变体
分配功能变体的过程由该功能的 allocation
属性确定。
"allocation": {
"default_when_disabled": "Small",
"default_when_enabled": "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
设置具有以下属性:
资产 | DESCRIPTION |
---|---|
default_when_disabled |
指定在该功能被视为已禁用的情况下请求变体时应使用哪个变体。 |
default_when_enabled |
指定在以下情况下请求变体时应使用哪个变体,即如果该功能被视为已启用,并且没有向用户分配其他变体。 |
user |
指定变体以及应将该变体分配到的用户列表。 |
group |
指定变量和组列表。 如果用户至少位于其中一个组,则会分配该变体。 |
percentile |
指定一个变体和一个百分比范围,计算出的用户百分比必须处于此范围内才能分配该变体。 |
seed |
percentile 的百分比计算所基于的值。 如果使用相同的 seed 值,则特定用户的百分比计算在所有功能中都是相同的。 如果未指定 seed ,则会根据功能名称创建默认种子。 |
如果未启用该功能,则功能管理器会将标记为 default_when_disabled
的变体分配给当前用户,在本例中为 Small
。
如果已启用该功能,则功能管理器将按该顺序检查 user
、group
和 percentile
分配,以分配变体。 对于此特定示例,如果接受评估的用户在名为 Marsha
的组中命名为 Ring1
,或者该用户恰好位于 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
,因此该功能现在被视为已禁用。
遥测
部署功能标志更改时,分析其对应用程序的影响通常很重要。 例如,可能会出现以下几个问题:
- 我的标志是否按预期启用/禁用?
- 目标用户是否按预期获得对特定功能的访问权限?
- 特定用户看到的是哪个变体?
可以通过功能标志评估事件的发出和分析来回答这些类型的问题。
启用遥测
默认情况下,功能标志不发出遥测。 若要发布给定功能标志的遥测,该标志必须声明它已启用遥测发出。
对于在 JSON 中定义的功能标志,可使用 telemetry
属性启用。
{
"feature_management": {
"feature_flags": [
{
"id": "MyFeatureFlag",
"enabled": true,
"telemetry": {
"enabled": true
}
}
]
}
}
上面的代码片段定义了一个名为 MyFeatureFlag
的功能标志,该标志已启用遥测。
telemetry
对象的 enabled
属性设置为 true
。
enabled
属性的值必须为 true
才能发布标志的遥测。
功能标志的 telemetry
部分具有以下属性:
资产 | DESCRIPTION |
---|---|
enabled |
指定是否应为功能标志发布遥测。 |
metadata |
键值对的集合(建模为字典),可用于将有关功能标志的自定义元数据附加到评估事件。 |
自定义遥测发布
创建 onFeatureEvaluated
时,可以注册 FeatureManager
回调函数。 每当评估功能标志并为该标志启用遥测时,都会调用此回调。 回调函数将功能评估结果作为参数。
以下示例演示如何实现自定义回调函数,以便使用从功能评估结果中提取的信息发送遥测数据,并将其注册到功能管理器。
const sendTelemetry = (evaluationResult) => {
const featureId = evaluationResult.feature.id;
const featureEnabled = evaluationResult.enabled;
const targetingId = evaluationResult.targetingId;
const variantName = evaluationResult.variant?.name;
const variantAssignmentReason = evaluationResult.variantAssignmentReason;
// custom code to send the telemetry
// ...
}
const featureManager = new FeatureManager(featureProvider, { onFeatureEvaluated : sendTelemtry});
Application Insights 集成
JavaScript 功能管理库提供与 Application Insights SDK 集成的扩展包。
Application Insights 为 Web 和 Node.js 方案提供不同的 SDK。 请为应用程序选择正确的扩展包。
如果应用程序在浏览器中运行,请安装 "@microsoft/feature-management-applicationinsights-browser"
包。 以下示例演示如何创建内置的 Application Insights 遥测发布者,并将其注册到功能管理器。
import { ApplicationInsights } from "@microsoft/applicationinsights-web"
import { FeatureManager, ConfigurationObjectFeatureFlagProvider } from "@microsoft/feature-management";
import { createTelemetryPublisher, trackEvent } from "@microsoft/feature-management-applicationinsights-browser";
const appInsights = new ApplicationInsights({ config: {
connectionString: "<APPINSIGHTS_CONNECTION_STRING>"
}});
appInsights.loadAppInsights();
const publishTelemetry = createTelemetryPublisher(appInsights);
const provider = new ConfigurationObjectFeatureFlagProvider(jsonObject);
const featureManager = new FeatureManager(provider, {onFeatureEvaluated: publishTelemetry});
// FeatureEvaluation event will be emitted when a feature flag is evaluated
featureManager.getVariant("TestFeature", {userId : TARGETING_ID}).then((variant) => { /* do something*/ });
// Emit a custom event with targeting id attached.
trackEvent(appInsights, TARGETING_ID, {name: "TestEvent"}, {"Tag": "Some Value"});
当评估启用了遥测的功能标志时,遥测发布者会将 FeatureEvaluation
自定义事件发送到 Application Insights。 自定义事件遵循 FeatureEvaluationEvent 架构。
目标遥测处理器
如果已实现 ITargetingContextAccessor
,可以使用内置的 Application Insights 遥测处理器通过调用 createTargetingTelemetryProcessor
函数自动将目标 ID 信息附加到所有遥测。
const appInsights = require("applicationinsights");
appInsights.setup(process.env.APPINSIGHTS_CONNECTION_STRING).start();
const { createTargetingTelemetryProcessor } = require("@microsoft/feature-management-applicationinsights-node");
appInsights.defaultClient.addTelemetryProcessor(
createTargetingTelemetryProcessor(targetingContextAccessor)
);
这可确保发送到 Application Insights 的每个遥测项都包含用户的目标 ID 信息(userId 和组),使你能够将功能标志使用情况与分析中的特定用户或组相关联。
如果使用目标遥测处理器,而不是调用 trackEvent
功能管理包提供的方法,可以直接从 Application Insights SDK 调用 trackEvent
该方法。 目标 ID 信息会自动附加到自定义事件遥测的 customDimensions
。
// Instead of calling trackEvent and passing the app insights client
// trackEvent(appInsights.defaultClient, "<TARGETING_ID>", {name: "TestEvent", properties: {"Tag": "Some Value"}});
// directly call trackEvent method provided by App Insights SDK
appInsights.defaultClient.trackEvent({ name: "TestEvent" });
后续步骤
若要了解如何在应用程序中使用功能标志,请继续阅读以下快速入门。
若要了解如何使用功能筛选器的信息,请继续学习以下教程。