教程:使用 Evidently 示例应用程序进行 A/B 测试
重要
终止支持通知:2025 年 10 月 16 日,Amazon 将停止对 CloudWatch Evidently 的支持。2025 年 10 月 16 日之后,您将无法再访问 Evidently 控制台或 Evidently 资源。
本节介绍如何使用 Amazon CloudWatch Evidently 进行 A/B 测试的教程。本教程涉及 Evidently 示例应用程序,其是一个简单的响应应用程序。示例应用程序将被配置为显示/不显示 showDiscount 功能。当向用户显示该功能时,购物网站上以 20% 的折扣显示价格。
除了向一些用户而非向其他用户显示折扣之外,在本教程中,您还设置 Evidently 以从两个变体收集页面加载时间指标。
警告
此场景需要 IAM 用户具有编程访问权限和长期凭证,这会带来安全风险。为帮助减轻这种风险,我们建议仅向这些用户提供执行任务所需的权限,并在不再需要这些用户时将其移除。必要时可以更新访问密钥。有关更多信息,请参阅《IAM 用户指南》中的更新访问密钥。
步骤 1:下载示例应用程序
首先下载 Evidently 示例应用程序。
下载示例应用程序
从以下 Simple Storage Service (Amazon S3) 存储桶下载示例应用程序:
https://evidently-sample-application.s3.us-west-2.amazonaws.com/evidently-sample-shopping-app.zip解压缩程序包。
步骤 2:添加 Evidently 端点并设置凭证
接下来,将 Evidently 的区域和端点添加到示例应用程序包中 src 目录下的 config.js 文件,如以下示例所示:
evidently: { REGION: "us-west-2", ENDPOINT: "https://evidently.us-west-2.amazonaws.com (https://evidently.us-west-2.amazonaws.com/)", },
您还必须确保应用程序有调用 CloudWatch Evidently 的权限。
授予示例应用程序调用 Evidently 的权限
联合到您的 Amazon 账户。
创建 IAM 用户并将 AmazonCloudWatchEvidentlyFullAccess 策略附加到该用户。
记录 IAM 用户的访问密钥 ID 和秘密访问密钥,因为您在下一步中需要使用它们。
在您本节早些时候修改的同一
config.js文件中,输入访问密钥 ID 和秘密访问密钥的值,如以下示例所示:credential: { accessKeyId: "Access key ID", secretAccessKey: "Secret key" }重要
我们使用此步骤使示例应用程序尽可能简单,以便您尝试。我们不建议您将 IAM 用户凭证放到实际的生产应用程序中。我们建议您使用 Amazon Cognito 进行身份验证。有关更多信息,请参阅将 Amazon Cognito 与 Web 和移动应用程序集成。
步骤 3:为功能评估设置代码
当您使用 CloudWatch Evidently 评估功能时,必须使用 EvaluateFeature 操作为每个用户会话随机选择功能变体。此操作根据您在实验中指定的百分比,将用户会话分配给功能的每个变体。
为书店演示应用程序设置功能评估代码
将客户端生成器添加到
src/App.jsx文件,以便示例应用程序可以调用 Evidently。import Evidently from 'aws-sdk/clients/evidently'; import config from './config'; const defaultClientBuilder = ( endpoint, region, ) => { const credentials = { accessKeyId: config.credential.accessKeyId, secretAccessKey: config.credential.secretAccessKey } return new Evidently({ endpoint, region, credentials, }); };将以下内容添加到
const App代码部分以启动客户端。if (client == null) { client = defaultClientBuilder( config.evidently.ENDPOINT, config.evidently.REGION, );通过添加以下代码构建
evaluateFeatureRequest。此代码会预填充我们在本教程后面推荐的项目名称和功能名称。您可以替换自己的项目名称和功能名称,只要您也可以在 Evidently 控制台中指定这些项目名称和功能名称。const evaluateFeatureRequest = { entityId: id, // Input Your feature name feature: 'showDiscount', // Input Your project name' project: 'EvidentlySampleApp', };添加代码以调用 Evidently 进行功能评估。发送请求时,Evidently 会随机分配查看或不查看
showDiscount功能的用户会话。client.evaluateFeature(evaluateFeatureRequest).promise().then(res => { if(res.value?.boolValue !== undefined) { setShowDiscount(res.value.boolValue); } getPageLoadTime() })
步骤 4:为实验指标设置代码
对于自定义指标,请使用 Evidently 的 PutProjectEvents API 向 Evidently 发送指标结果。以下示例说明了如何设置自定义指标并向 Evidently 发送实验数据。
添加以下函数来计算页面加载时间,并使用 PutProjectEvents 向 Evidently 发送指标值。将以下函数添加到 Home.tsx 中并在 EvaluateFeature API 中调用此函数:
const getPageLoadTime = () => { const timeSpent = (new Date().getTime() - startTime.getTime()) * 1.000001; const pageLoadTimeData = `{ "details": { "pageLoadTime": ${timeSpent} }, "UserDetails": { "userId": "${id}", "sessionId": "${id}"} }`; const putProjectEventsRequest = { project: 'EvidentlySampleApp', events: [ { timestamp: new Date(), type: 'aws.evidently.custom', data: JSON.parse(pageLoadTimeData) }, ], }; client.putProjectEvents(putProjectEventsRequest).promise(); }
自您下载 App.js 文件并对其进行所有编辑之后,该文件应该如此处所示。
import React, { useEffect, useState } from "react"; import { BrowserRouter as Router, Switch } from "react-router-dom"; import AuthProvider from "contexts/auth"; import CommonProvider from "contexts/common"; import ProductsProvider from "contexts/products"; import CartProvider from "contexts/cart"; import CheckoutProvider from "contexts/checkout"; import RouteWrapper from "layouts/RouteWrapper"; import AuthLayout from "layouts/AuthLayout"; import CommonLayout from "layouts/CommonLayout"; import AuthPage from "pages/auth"; import HomePage from "pages/home"; import CheckoutPage from "pages/checkout"; import "assets/scss/style.scss"; import { Spinner } from 'react-bootstrap'; import Evidently from 'aws-sdk/clients/evidently'; import config from './config'; const defaultClientBuilder = ( endpoint, region, ) => { const credentials = { accessKeyId: config.credential.accessKeyId, secretAccessKey: config.credential.secretAccessKey } return new Evidently({ endpoint, region, credentials, }); }; const App = () => { const [isLoading, setIsLoading] = useState(true); const [startTime, setStartTime] = useState(new Date()); const [showDiscount, setShowDiscount] = useState(false); let client = null; let id = null; useEffect(() => { id = new Date().getTime().toString(); setStartTime(new Date()); if (client == null) { client = defaultClientBuilder( config.evidently.ENDPOINT, config.evidently.REGION, ); } const evaluateFeatureRequest = { entityId: id, // Input Your feature name feature: 'showDiscount', // Input Your project name' project: 'EvidentlySampleApp', }; // Launch client.evaluateFeature(evaluateFeatureRequest).promise().then(res => { if(res.value?.boolValue !== undefined) { setShowDiscount(res.value.boolValue); } }); // Experiment client.evaluateFeature(evaluateFeatureRequest).promise().then(res => { if(res.value?.boolValue !== undefined) { setShowDiscount(res.value.boolValue); } getPageLoadTime() }) setIsLoading(false); },[]); const getPageLoadTime = () => { const timeSpent = (new Date().getTime() - startTime.getTime()) * 1.000001; const pageLoadTimeData = `{ "details": { "pageLoadTime": ${timeSpent} }, "UserDetails": { "userId": "${id}", "sessionId": "${id}"} }`; const putProjectEventsRequest = { project: 'EvidentlySampleApp', events: [ { timestamp: new Date(), type: 'aws.evidently.custom', data: JSON.parse(pageLoadTimeData) }, ], }; client.putProjectEvents(putProjectEventsRequest).promise(); } return ( !isLoading? ( <AuthProvider> <CommonProvider> <ProductsProvider> <CartProvider> <CheckoutProvider> <Router> <Switch> <RouteWrapper path="/" exact component={() => <HomePage showDiscount={showDiscount}/>} layout={CommonLayout} /> <RouteWrapper path="/checkout" component={CheckoutPage} layout={CommonLayout} /> <RouteWrapper path="/auth" component={AuthPage} layout={AuthLayout} /> </Switch> </Router> </CheckoutProvider> </CartProvider> </ProductsProvider> </CommonProvider> </AuthProvider> ) : ( <Spinner animation="border" /> ) ); }; export default App;
每次用户访问示例应用程序时,都会向 Evidently 发送自定义指标进行分析。Evidently 会分析每个指标并在 Evidently 控制面板上实时显示结果。以下示例显示了指标有效负载:
[ {"timestamp": 1637368646.468, "type": "aws.evidently.custom", "data": "{\"details\":{\"pageLoadTime\":2058.002058},\"userDetails\":{\"userId\":\"1637368644430\",\"sessionId\":\"1637368644430\"}}" } ]
步骤 5:创建项目、功能和实验
然后,您可以在 CloudWatch Evidently 控制台中创建项目、功能和实验。
为本教程创建项目、功能和实验
通过 https://console.aws.amazon.com/cloudwatch/
打开 CloudWatch 控制台。 在导航窗格中,选择 Application Signals、Evidently。
请选择 Create project(创建项目)并填写字段。您必须使用
EvidentlySampleApp作为项目名,以便示例正常工作。对于 Evaluation event storage(评估事件存储),选择 Don't store Evaluation events(不要存储评估事件)。填写字段后,选择 Create Project(创建项目)。
有关更多详细信息,请参阅 创建新项目。
创建项目后,在该项目中创建功能。将功能命名为
showDiscount。在此功能中,创建Boolean类型的两个变体。使用值disable将第一个变体命名为False,然后使用值enable将第二个变体命名为True。有关创建功能的更多信息,请参阅 向项目添加功能。
完成功能创建后,请在项目中创建实验。将实验命名为
pageLoadTime。本实验将使用名为
pageLoadTime的自定义指标,用以衡量被测试页面的页面加载时间。用于实验的自定义指标使用 Amazon EventBridge 创建而成。有关 EventBridge 的更多信息,请参阅什么是 Amazon EventBridge?。要创建该自定义指标,请在创建实验时执行以下操作:
对于 Metric source(指标来源),请在 Metrics(指标)下选择 Custom metrics(自定义指标)。
对于 Metric name(指标名称),请输入
pageLoadTime。对于 Goal(目标),请选择 Decrease(减少)。这表示我们希望该指标的值较低,以指明该功能变体最佳。
对于 Metric rule(指标规则),请输入以下内容:
对于实体 ID,请输入
UserDetails.userId。对于 Value key(值键),请输入
details.pageLoadTime。对于 Units(单位)中,请输入
ms。
请选择 Add metric(添加指标)。
对于 Audiences(受众),请选择 100% 以便所有用户都能进入实验。将变体之间的流量拆分设置为每个变体 50%。
然后,请选择 Create experiment(创建实验)来创建实验。创建它后,只有您告知 Evidently 将其启动,它才会启动。
步骤 6:开始实验并测试 CloudWatch Evidently
最后的步骤是开始实验并开启示例应用程序。
开始教程实验
通过 https://console.aws.amazon.com/cloudwatch/
打开 CloudWatch 控制台。 在导航窗格中,选择 Application Signals、Evidently。
选择 EvidentlySampleApp 项目。
请选择 Experiments(实验)选项卡。
请选择 pageLoadTime 旁边的按钮,然后依次选择 Actions(操作)、Start experiment(开始实验)。
请选择实验结束的时间。
请选择 Start experiment(开始实验)。
实验会立即开始。
然后,使用以下命令开启 Evidently 示例应用程序:
npm install -f && npm start
开启应用程序后,您将被分配到正在测试的两个功能变体之一。一个变体会显示“20% 折扣”,另一个变体不会显示。继续刷新页面以查看不同的变体。
注意
Evidently 包含粘性评估。功能评估是确定性的,这意味着对于相同的 entityId 和功能,用户将始终收到相同的变体分配。变体分配仅在实体被添加到覆盖或实验流量增加时更改。
但是,为了让您轻松使用示例应用程序教程,Evidently 会在您每次刷新页面时重新分配示例应用程序功能评估,以便您在无需添加覆盖的情况下即可体验两种变体。
故障排除
我们建议您使用 npm 版本 6.14.14。如果您看到有关构建或开启示例应用程序的任何错误,并且您正在使用不同版本的 npm,请执行以下操作。
安装 npm 版本 6.14.14
下载 node-v14.17.5.pkg
并运行此 pkg 安装 npm。 如果您看到
webpack not found错误,导航至evidently-sample-shopping-app文件夹并尝试以下操作:删除
package-lock.json删除
yarn-lock.json删除
node_modules从
package.json中删除 webpack 依赖项运行以下命令:
npm install -f && npm