亚马逊AWS官方博客

新增功能 – Amazon CloudWatch Evidently – 实验和功能管理

作为一名开发人员,我很高兴地宣布推出 Amazon CloudWatch Evidently。这是 Amazon CloudWatch 的一项新功能,可让开发人员轻松在其应用程序代码中引入实验和功能管理。CloudWatch Evidently 可用于两个相似但不同的使用案例:实施黑暗启动(也称为功能标记)和 A/B 测试。

功能标记是一项软件开发技术,可让您启用或禁用功能,而无需部署您的代码。它将功能部署与版本分离。代码中的功能会在实际发布之前进行部署。它们隐藏在 if-then-else 语句背后。在运行时,应用程序代码将查询远程服务。该服务决定暴露于新功能的用户的百分比。您还可以为某些特定客户(例如 Beta 测试人员)配置应用程序行为。

使用功能标记时,您可以在启动之前部署新代码。然后,您可以逐步向一小部分客户推出新功能。在发布期间,您可以监控自己的技术和业务指标。只要一切顺利,您就可以增加流量,向其他用户公开新功能。如果出现问题,您只需单击一下或调用 API 即可修改服务器端路由,以便仅向客户展示以前的(和正在运行的)体验。这样,您就可以恢复用户体验,而不需要进行回滚部署。

A/B 测试与功能标志标记有许多相似之处,但仍有不同的用途。A/B 测试由使用多个变体的随机实验组成。通过 A/B 测试,您可以比较单个功能的多个版本,方法通常为测试主题对变体 A 和与变体 B 的响应,然后确定两者中的哪一个更有效。例如,想象有一个电子商务网站(在 Amazon 我们非常了解的一种场景)。您可能想为结账按钮尝试不同的形状、大小或颜色,然后衡量哪种变体对收入影响最大。

进行 A/B 测试所需的基础设施类似于功能标记所需的基础设施。您可以在应用程序中部署多个场景,并控制如何将部分客户流量路由到一个或另一个场景。然后,执行深入统计分析以比较变体的影响。CloudWatch Evidently 可以在不需要高级统计知识的情况下帮助解释实验结果并采取行动。在实验进行期间,您可以使用 Evidently 的统计引擎提供的洞察,例如随时 p 值置信区间进行决策。

在 Amazon,我们广泛使用功能标记来控制我们的启动,并使用 A/B 测试来尝试新想法。我们在构建开发人员的工具和库以及大规模维护和运营实验服务方面积累了多年的经验。现在您可以从我们的经验中受益。

CloudWatch Evidently 将术语“启动”用于功能标记,将“实验”用于 A/B 测试,我在本文的其余部分也是这样用的。

让我们从应用程序开发人员的角度来看看它是如何工作的。

启动的实际操作
在此次演示中,我使用了简单的 Guestbook Web 应用程序。到目前为止,留言簿页面是只读的,评论只能从我们的后端输入。我开发了一项新功能,让客户可以在留言簿页面上输入评论。我想在一周内逐步推出这项新功能,并保留在影响重要的技术或业务指标(例如 p95 延迟、客户参与度、页面浏览量等)时还原更改的能力。用户已经过身份验证,我将根据用户 ID 对用户进行细分。

启动之前:
Evidently - 实验关闭
启动后:
Evidently - 实验开启

创建项目
首先来配置 Evidently。我打开 AWS 管理控制台并导航至 CloudWatch Evidently。然后,我选择 Create a project(创建项目)。

Evidently - 创建项目

我输入项目名称和描述。

Evidently 让您可以选择性地将事件存储到 CloudWatch 日志或 S3 中,以便您可以将它们移动到 Amazon Redshift 等系统中来执行分析操作。在此次演示中,我选择不存储事件。完成后,我选择 Create project(创建项目)。

Evidently - 创建项目的第二部分

添加功能
接下来,我通过选择 Add feature(添加功能)来为此项目创建一项功能。我输入功能名称功能描述。接下来,我定义自己的功能变体。 本示例中有两个变体,我使用的是布尔类型。true 表示留言簿可编辑,false 表示留言簿为只读。变体类型可以是 booleandoublelongstring

Evidently - 创建功能我可以定义覆盖。覆盖允许我为所选用户预定义变体。我希望用户“seb”(我的 beta 版测试人员)始终能够收到可编辑的变体。

Evidently - 创建功能 - 覆盖控制台共享要添加到我的应用程序中的 JavaScript 和 Java 代码段。

Evidently - 代码段说到代码段,我们来看看代码级别的更改。

检测我的应用程序代码
我使用一个简单的 Web 应用程序进行此演示。我使用 JavaScript 编写了此应用程序的代码。我使用适用于 JavaScript 的 AWS 开发工具包Webpack 对我的代码进行打包。我还使用 JQuery 操作 DOM 来隐藏或显示元素。我设计这个应用程序是为了使用标准的 JavaScript 和最少数量的框架来使此示例涵盖全部。在现实的项目中,请随意使用更高级别的工具和框架,例如 ReactAngular

我首先对 Evidently 客户端进行了初始化。与其他 AWS 服务一样,我必须提供访问密钥和秘密访问密钥进行身份验证。我们暂时把身份验证部分排除在外。我在本文末尾添加了一个注释来讨论您拥有的选项。在此示例中,我使用 Amazon Cognito 身份池接收临时凭证。

// Initialize the Amazon CloudWatch Evidently client
const evidently = new AWS.Evidently({
    endpoint: EVIDENTLY_ENDPOINT,
    region: 'us-east-1',
    credentials: fromCognitoIdentityPool({
        client: new CognitoIdentityClient({ region: 'us-west-2' }),
        identityPoolId: IDENTITY_POOL_ID
    }),
});

有了这个客户端,我的代码可以调用 EvaluateFeature API 来决定要向客户显示的变体。entityId 是任何基于字符串的属性,用于细分客户。它可能是会话 ID、客户 ID,甚至更好的是它们的哈希值。featureName 参数包含要评估的功能的名称。在本示例中,我传递了值 EditableGuestBook

const evaluateFeature = async (entityId, featureName) => {

    // API request structure
    const evaluateFeatureRequest = {
        // entityId for calling evaluate feature API
        entityId: entityId,
        // Name of my feature
        feature: featureName,
        // Name of my project
        project: "AWSNewsBlog",
    };

    // Evaluate feature
    const response = await evidently.evaluateFeature(evaluateFeatureRequest).promise();
    console.log(response);
    return response;
}

响应包含来自 Evidently 的分配决策,这些决策基于服务器端定义的流量规则。

{
 details: {
   launch: "EditableGuestBook", group: "V2"},
   reason: "LAUNCH_RULE_MATCH",
   value: {boolValue: false},
   variation: "readonly"
}}

最后一部分基于上面收到的 value 包含用户界面的隐藏或显示部分。使用基本 JQuery DOM 操作,该部分将类似于以下内容:

window.aws.evaluateFeature(entityId, 'EditableGuestbook').then((response, error) => {
    if (response.value.boolValue) {
        console.log('Feature Flag is on, showing guest book');
        $('div#guestbook-add').show();
    } else {
        console.log('Feature Flag is off, hiding guest book');
        $('div#guestbook-add').hide();
    }
});

创建一个启动
现在,该功能已在服务器端定义,且客户端代码已经检测完成,我部署了代码并将其公开给我的客户。稍后,我可能会决定启动该功能。我导航回控制台,选择我的项目,然后选择 Create Launch(创建启动)。我为启动选择启动名称启动描述。然后,我选择想要启动的功能。

Evidently - 创建启动Launch Configuration(启动配置)部分中,我对发送给每个变体的流量量进行配置。我也可以通过多个步骤安排启动。这样我就可以根据时间表计划不同的路由步骤。例如,第一天,我可能会选择将 10% 的流量发送到新功能,在第二天将 20% 的流量发送到新功能,等等。在本示例中,我决定将流量分成 50/50。

Evidently - 启动配置最后,我最多可能会定义三个指标来衡量我的变体性能。指标通过将规则应用于数据事件来定义。

Evidently - 自定义指标同样地,我必须检测我的代码,以使用 Evidently 提供的 PutProjectEvents API 发送这些指标。创建我的启动后,EvaluateFeature API 将针对不同的 entityId 值返回不同值(在本次演示中为用户)。

任何时候,我都可以更改路由配置。此外,我还可以访问监控控制面板来观察我的变体分布以及每个变体的指标。

Evidently - 启动监控我相信您的真实启动图获得的数据比我获得的数据多,我只是为了写这篇博文才创建了它。

A/B 测试
进行 A/B 测试也是类似的。我创建一个要测试的功能,然后创建一个实验。我将实验配置为将部分流量路由到变体 1,然后将另一部分路由到变体 2。当我准备好启动实验时,我会明确选择 Start experiment(开启实验)。

Evidently - 开启实验

在本实验中,我对发送自定义指标很感兴趣。例如:

// pageLoadTime custom metric
const timeSpendOnHomePageData = `{
   "details": {
      "timeSpendOnHomePage": ${timeSpendOnHomePageValue}
   },
   "userDetails": { "userId": "${randomizedID}", "sessionId": "${randomizedID}" }
}`;

const putProjectEventsRequest: PutProjectEventsRequest = {
   project: 'AWSNewsBlog',
   events: [
    {
        timestamp: new Date(),
        type: 'aws.evidently.custom',
        data: JSON.parse(timeSpendOnHomePageData)
    },
   ],
};

this.evidently.putProjectEvents(putProjectEventsRequest).promise().then(res =>{})

却换到 Results(结果)页面,我看到 Event Count(事件计数)、Total Value(总值)、Average(平均值)、Improvement(改进)(具有 95% 置信区间)和 Statistical significance(统计显著性)的原始值和图表数据。统计显著性描述了与基线相比,我们有多确定变体对指标产生影响。

这些结果在整个实验过程中生成,并且可以保证在您想查看置信区间和统计显著性的任何时候,它们都有效。此外,在实验结束时,Evidently 还会生成实验的贝叶斯视角,提供有关变体之间存在差异的可能性的信息。

下面两个屏幕截图显示了两个指标随时间变化的平均值以及指标在 95% 置信区间内的改进的图表。

Evidently - 实验监控 - 平均值Evidently - 实验监控 - 改进

其他想法
总结之前,我想分享一些额外的注意事项。

首先,重要的是要了解我选择在前端应用程序开发的背景下进行 Evidently 演示。但是,您可以将 Evidently 用于任何类型的应用程序:前端 Web 或移动、后端 API 甚至是机器学习 (ML)。例如,您可以使用 Evidently 部署两个不同的 ML 模型并进行实验,就像我上面展示的那样。

其次,就像其他 AWS 服务一样,我们所有的 AWS 开发工具包中都提供了 Evidently API。这样您就可以使用 EvaluateFeature 和来自九种编程语言的其他 API:C++、Go、Java、JavaScript(和 Typescript)、.Net、NodeJS、PHP、Python 和 Ruby。适用于 Rust 和 Swift 的 AWS 开发工具包正在开发中中。

第三,对于我在这里演示的前端应用程序,重要的是要考虑如何验证对 Evidently API 的调用。硬编码访问密钥和秘密访问密钥并非选项。对于前端场景,我建议您使用 Amazon Cognito 身份池将用户身份令牌交换为临时访问密钥和秘密密钥。用户身份令牌可以从 Cognito 用户池Active DirectoryLogin with AmazonLogin with FacebookLogin with GoogleSignin with Apple 之类的第三方身份验证系统或者兼容 OpenID ConnectSAML 的任何系统中获取。Cognito 身份池还允许匿名访问。不需要身份令牌。Cognito 身份池发放与 IAM 角色关联的临时令牌。您必须在您的策略中允许evidently:EvaluateFeature API 的调用。

最后,在使用功能标记时,请在冲刺期间计划代码清理时间。启动功能后,您可以考虑删除对 EvaluateFeature API 的调用以及最初用于隐藏该功能的 if-then-else 逻辑。

定价和可用性
Amazon Cloudwatch Evidently 现已在 9 个 AWS 区域正式推出:美国东部(弗吉尼亚北部)、美国东部(俄亥俄)、美国西部(俄勒冈)、亚太地区(新加坡)、亚太地区(悉尼)、亚太地区(东京)、欧洲(爱尔兰)、欧洲(法兰克福)和欧洲(斯德哥尔摩)。像往常一样,我们将在未来几个月内逐步扩展到其他区域。

定价为随用随付型,没有最低费用或经常性费用。CloudWatch Evidently 会根据 Evidently 事件和 Evidently 分析单位向您的账户收费。Evidently 分析单位根据您在 Evidently 中创建的规则从 Evidently 事件中生成。例如,用户结账事件可能会产生两个 Evidently 分析单位:结账值和购物车中的商品数量。有关定价的更多信息,请参见 Amazon CloudWatch 定价

立即开始试用 CloudWatch Evidently

– seb