共用方式為


教學課程:使用變體功能旗標 (預覽) 來執行實驗

在應用程式上執行實驗可協助您做出明智的決策,以改善應用程式的效能和使用者體驗。 在本指南中,您將了解如何在應用程式組態存放區內設定和執行實驗。 您將了解如何使用應用程式組態、Application Insights (預覽) 和分割實驗工作區 (預覽) 的功能來收集和測量資料。

如此一來,您就可以根據資料來做出決策,以改善應用程式。

注意

開始實驗旅程的快速方法是執行 Quote of the Day AZD 範例。此存放庫提供完整的範例,其中包含 Azure 資源佈建和第一個實驗,以了解如何整合 Azure 應用程式組態與 .NET 應用程式以執行實驗。

在本教學課程中,您已:

  • 建立變體功能旗標
  • 將 Application Insights 資源新增至存放區
  • 將分割實驗工作區新增至存放區
  • 設定應用程式以執行實驗
  • 在變體功能旗標中啟用遙測並建立實驗
  • 建立實驗的計量
  • 取得實驗結果

必要條件

建立變體功能旗標 (預覽)

建立具有 OffOn 這兩個變體、且名為 Greeting 的變體功能旗標,如功能旗標快速入門所述。

將 Application Insights (預覽) 資源連線到組態存放區

若要執行實驗,您必須先將工作區型 Application Insights 資源連線到應用程式組態存放區。 將此資源連線到應用程式組態存放區會使用實驗的遙測來源來設定組態存放區。

  1. 在應用程式組態存放區中,選取 [遙測] > [Application Insights (預覽)]

    螢幕擷取畫面:將 Application Insights 新增至存放區的 Azure 入口網站。

  2. 選取所需的 Application Insights 資源來作為變體功能旗標和應用程式的遙測提供者,然後選取 [儲存]。 如果您沒有 Application Insights 資源,請選取 [新建] 來建立資源。 如需如何繼續的詳細資訊,請移至建立工作區型資源。 然後,回到 [Application Insights (預覽)],重新載入可用的 Application Insights 資源清單,然後選取新的 Application Insights 資源。

  3. 會有通知指出,應用程式組態存放區已成功更新 Application Insights 資源。

將分割實驗工作區 (預覽) 連線到存放區

若要在 Azure 應用程式組態中執行實驗,您會使用分割實驗工作區。 請遵循下列步驟,將分割實驗工作區連線到存放區。

  1. 在應用程式組態存放區中,從左側功能表中選取 [實驗]>[分割實驗工作區 (預覽)]

    螢幕擷取畫面:將分割實驗工作區新增至應用程式組態存放區的 Azure 入口網站。

  2. 選取 [分割實驗工作區],然後選取 [儲存]。 如果您沒有分割實驗工作區,請遵循分割實驗工作區快速入門以建立一個。

    注意

    在分割實驗工作區中選取的資料來源必須與上一個步驟中選取的 Application Insights 資源相同。

  3. 會有通知指出作業成功。

設定應用程式以執行實驗

您現已將 Application Insights (預覽) 資源連線到應用程式組態存放區,接下來請設定應用程式以執行實驗 (預覽)。

在此範例中,您會建立名為 Quote of the Day 的 ASP.NET Web 應用程式。 應用程式載入時會顯示引用。 使用者可以按心型按鈕來按讚。 為了改善使用者參與度,您想要探索個人化問候訊息是否會增加對引用按讚的使用者數目。 您在 Azure 應用程式組態中建立具有 OffOn 這兩個變體的 Greeting 功能旗標。 收到 Off 變體的使用者會看到標準標題。 收到 On 變體的使用者會收到問候訊息。 您在 Application Insights 中收集並儲存使用者互動的遙測資料。 透過分割實驗工作區,您可以分析實驗的有效性。

建立應用程式並新增使用者秘密

  1. 開啟命令提示字元,然後執行下列程式碼。 這會使用個別帳戶驗證在 ASP.NET Core 中建立新的 Razor Pages 應用程式,並將其放在名為 QuoteOfTheDay 的輸出資料夾中。

    dotnet new razor --auth Individual -o QuoteOfTheDay
    
  2. 在命令提示字元中,瀏覽至 QuoteOfTheDay 資料夾,然後執行下列命令以建立應用程式的使用者祕密。 此秘密會保存應用程式組態的連接字串。

    dotnet user-secrets set ConnectionStrings:AppConfiguration "<App Configuration Connection string>"
    
  3. 建立另一個會保存 Application Insights 連接字串的使用者祕密。

    dotnet user-secrets set ConnectionStrings:AppInsights "<Application Insights Connection string>"
    

更新應用程式碼

  1. QuoteOfTheDay.csproj 中,將功能管理和應用程式組態 SDK 的最新預覽版本新增為必要套件。

    <PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="8.0.0-preview.2" />
    <PackageReference Include="Microsoft.FeatureManagement.Telemetry.ApplicationInsights" Version="4.0.0-preview3" />
    <PackageReference Include="Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore" Version="4.0.0-preview3" />
    <PackageReference Include="Microsoft.FeatureManagement.AspNetCore" Version="4.0.0-preview3" />
    
  2. Program.csvar builder = WebApplication.CreateBuilder(args); 這行底下,新增應用程式組態提供者,這會在應用程式啟動時從 Azure 提取組態。 根據預設,UseFeatureFlags 方法會包含不含標籤的所有功能旗標,並設定 30 秒的快取到期時間。

    builder.Configuration
        .AddAzureAppConfiguration(o =>
        {
            o.Connect(builder.Configuration.GetConnectionString("AppConfiguration"));
    
            o.UseFeatureFlags();
        });
    
  3. Program.cs 中,新增下列 using 陳述式:

    using Microsoft.ApplicationInsights.AspNetCore.Extensions;
    using Microsoft.ApplicationInsights.Extensibility;
    using Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore;
    
  4. builder.Configuration.AddAzureAppConfiguration 的呼叫位置底下,新增:

    // Add Application Insights telemetry.
    builder.Services.AddApplicationInsightsTelemetry(
        new ApplicationInsightsServiceOptions
        {
            ConnectionString = builder.Configuration.GetConnectionString("AppInsights"),
            EnableAdaptiveSampling = false
        })
        .AddSingleton<ITelemetryInitializer, TargetingTelemetryInitializer>();
    

    此程式碼片段會執行下列動作。

    • 將 Application Insights 遙測用戶端新增至應用程式。
    • 新增會將目標資訊附加至傳出遙測的遙測初始設定式。
    • 停用調適型取樣。 如需如何停用調適型取樣的詳細資訊,請前往疑難排解
  5. 在根資料夾 QuoteOfTheDay 中,建立名為 ExampleTargetingContextAccessor.cs 的新檔案。 這會建立名為 ExampleTargetingContextAccessor 的新類別。 將下列內容貼到檔案中。

    using Microsoft.FeatureManagement.FeatureFilters;
    
    namespace QuoteOfTheDay
    {
        public class ExampleTargetingContextAccessor : ITargetingContextAccessor
        {
            private const string TargetingContextLookup = "ExampleTargetingContextAccessor.TargetingContext";
            private readonly IHttpContextAccessor _httpContextAccessor;
    
            public ExampleTargetingContextAccessor(IHttpContextAccessor httpContextAccessor)
            {
                _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
            }
    
            public ValueTask<TargetingContext> GetContextAsync()
            {
                HttpContext httpContext = _httpContextAccessor.HttpContext;
                if (httpContext.Items.TryGetValue(TargetingContextLookup, out object value))
                {
                    return new ValueTask<TargetingContext>((TargetingContext)value);
                }
                List<string> groups = new List<string>();
                if (httpContext.User.Identity.Name != null)
                {
                    groups.Add(httpContext.User.Identity.Name.Split("@", StringSplitOptions.None)[1]);
                }
                TargetingContext targetingContext = new TargetingContext
                {
                    UserId = httpContext.User.Identity.Name ?? "guest",
                    Groups = groups
                };
                httpContext.Items[TargetingContextLookup] = targetingContext;
                return new ValueTask<TargetingContext>(targetingContext);
            }
        }
    }
    

    這個類別會宣告 FeatureManagement 的目標如何取得使用者的內容。 在這個案例中,其會讀取 UserIdhttpContext.User.Identity.Name,並將電子郵件地址的網域視為 Group

  6. 瀏覽回 Program.cs,並新增下列 using 陳述式。

    using Microsoft.FeatureManagement.Telemetry;
    using Microsoft.FeatureManagement;
    using QuoteOfTheDay;
    
  7. AddApplicationInsightsTelemetry 的呼叫位置底下,新增服務來處理應用程式組態重新整理、設定功能管理、設定功能管理目標,以及啟用功能管理來發佈遙測事件。

    builder.Services.AddHttpContextAccessor();
    
    // Add Azure App Configuration and feature management services to the container.
    builder.Services.AddAzureAppConfiguration()
        .AddFeatureManagement()
        .WithTargeting<ExampleTargetingContextAccessor>()
        .AddTelemetryPublisher<ApplicationInsightsTelemetryPublisher>();
    
  8. var app = builder.Build(); 這行底下,新增會在適當時觸發應用程式組態重新整理的中介軟體。

    // Use Azure App Configuration middleware for dynamic configuration refresh.
    app.UseAzureAppConfiguration();
    
  9. 在該項目底下新增下列程式碼,透過將目標資訊儲存在 HttpContext 上,讓 TargetingTelemetryInitializer 可以存取目標資訊。

    // Add TargetingId to HttpContext for telemetry
    app.UseMiddleware<TargetingHttpContextMiddleware>();
    
  10. QuoteOfTheDay>Pages>Shared>_Layout.cshtml 中,於 QuoteOfTheDay.styles.css 的新增位置底下,新增下面這行以便為 5.15.3 版的 font-awesome 新增 css。

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
    
  11. 開啟 QuoteOfTheDay>Pages>Index.cshtml.cs,並將內容覆寫到引用應用程式。

    using Microsoft.ApplicationInsights;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    using Microsoft.FeatureManagement;
    
    namespace QuoteOfTheDay.Pages;
    
    public class Quote
    {
        public string Message { get; set; }
    
        public string Author { get; set; }
    }
    
    public class IndexModel(IVariantFeatureManagerSnapshot featureManager, TelemetryClient telemetryClient) : PageModel
    {
        private readonly IVariantFeatureManagerSnapshot _featureManager = featureManager;
        private readonly TelemetryClient _telemetryClient = telemetryClient;
    
        private Quote[] _quotes = [
            new Quote()
            {
                Message = "You cannot change what you are, only what you do.",
                Author = "Philip Pullman"
            }];
    
        public Quote? Quote { get; set; }
    
        public bool ShowGreeting { get; set; }
    
        public async void OnGet()
        {
            Quote = _quotes[new Random().Next(_quotes.Length)];
    
            Variant variant = await _featureManager.GetVariantAsync("Greeting", HttpContext.RequestAborted);
    
            ShowGreeting = variant.Configuration.Get<bool>();
        }
    
        public IActionResult OnPostHeartQuoteAsync()
        {
            string? userId = User.Identity?.Name;
    
            if (!string.IsNullOrEmpty(userId))
            {
                // Send telemetry to Application Insights
                _telemetryClient.TrackEvent("Like");
    
                return new JsonResult(new { success = true });
            }
            else
            {
                return new JsonResult(new { success = false, error = "User not authenticated" });
            }
        }
    }
    

    這個 PageModel 會挑選隨機引用,使用 GetVariantAsync 來取得目前使用者的變體,並將名為 “ShowGreeting” 的變數設定為變體的值。 PageModel 也會處理 Post 要求,呼叫 _telemetryClient.TrackEvent("Like"); 以將事件傳送至名為 Like 的 Application Insights。 此事件會自動繫結至使用者和變體,並可透過計量來追蹤。

  12. 開啟 index.cshtml,並覆寫引用應用程式的內容。

    @page
    @model IndexModel
    @{
        ViewData["Title"] = "Home page";
        ViewData["Username"] = User.Identity.Name;
    }
    
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            color: #333;
        }
    
        .quote-container {
            background-color: #fff;
            margin: 2em auto;
            padding: 2em;
            border-radius: 8px;
            max-width: 750px;
            box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
            display: flex;
            justify-content: space-between;
            align-items: start;
            position: relative;
        }
    
        .vote-container {
            position: absolute;
            top: 10px;
            right: 10px;
            display: flex;
            gap: 0em;
        }
    
        .vote-container .btn {
            background-color: #ffffff; /* White background */
            border-color: #ffffff; /* Light blue border */
            color: #333
        }
    
        .vote-container .btn:focus {
            outline: none;
            box-shadow: none;
        }
    
        .vote-container .btn:hover {
            background-color: #F0F0F0; /* Light gray background */
        }
    
        .greeting-content {
            font-family: 'Georgia', serif; /* More artistic font */
        }
    
        .quote-content p.quote {
            font-size: 2em; /* Bigger font size */
            font-family: 'Georgia', serif; /* More artistic font */
            font-style: italic; /* Italic font */
            color: #4EC2F7; /* Medium-light blue color */
        }
    </style>
    
    <div class="quote-container">
        <div class="quote-content">
            @if (Model.ShowGreeting)
            {
                <h3 class="greeting-content">Hi <b>@User.Identity.Name</b>, hope this makes your day!</h3>
            }
            else
            {
                <h3 class="greeting-content">Quote of the day</h3>
            }
            <br />
            <p class="quote">“@Model.Quote.Message”</p>
            <p>- <b>@Model.Quote.Author</b></p>
        </div>
    
        <div class="vote-container">
            <button class="btn btn-primary" onclick="heartClicked(this)">
                <i class="far fa-heart"></i> <!-- Heart icon -->
            </button>
        </div>
    
        <form action="/" method="post">
            @Html.AntiForgeryToken()
        </form>
    </div>
    
    <script>
        function heartClicked(button) {
            var icon = button.querySelector('i');
            icon.classList.toggle('far');
            icon.classList.toggle('fas');
    
            // If the quote is hearted
            if (icon.classList.contains('fas')) {
                // Send a request to the server to save the vote
                fetch('/Index?handler=HeartQuote', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
                    }
                });
            }
        }
    </script>
    

    此程式碼會對應至 UI 以顯示QuoteOfTheDay,並使用引用上的心型動作來進行處理。 其會使用先前提及的 Model.ShowGreeting 值,根據不同使用者的變體向其顯示不同的內容。

建置並執行應用程式

  1. 在命令提示字元中,於 QuoteOfTheDay 資料夾內執行:dotnet build

  2. 執行:dotnet run --launch-profile https

  3. 在應用程式的輸出中尋找格式為 Now listening on: https://localhost:{port} 的訊息。 瀏覽至瀏覽器中包含的連結。

  4. 檢視執行中的應用程式之後,選取右上方的 [註冊] 以註冊新的使用者。

    螢幕擷取畫面:顯示 [註冊] 的 Quote of the day 應用程式。

  5. 註冊名為 user@contoso.com 的新使用者。 密碼必須至少有六個字元,且包含數字和特殊字元。

  6. 在輸入使用者資訊後,選取 [按這裡以驗證電子郵件] 連結。

  7. 註冊名為 userb@contoso.com 的第二個使用者,輸入另一個密碼,並驗證這第二封電子郵件。

    注意

    請務必讓本教學課程正確使用這些名稱。 只要功能如預期般設定,這兩個使用者應該會看到不同的變體。

  8. 選取右上方的 [登入] 以登入為 userb (userb@contoso.com)。

    螢幕擷取畫面:顯示 [登入] 的 Quote of the day 應用程式。

  9. 登入之後,您應該會在檢視應用程式時看到 userb@contoso.com 中有特殊訊息。

    螢幕擷取畫面:顯示使用者特殊訊息的 Quote of the day 應用程式。

    userb@contoso.com 是唯一會看到特殊訊息的使用者。

在變體功能旗標中啟用遙測並建立實驗

遵循下列步驟,在變體功能旗標中啟用遙測並建立實驗:

  1. 在應用程式組態存放區中,移至 [作業]>[功能管理員]

  2. 在 [...] 捷徑功能表中一路選取到變體功能旗標「Greeting」的右側,然後選取 [編輯]

    螢幕擷取畫面:編輯變體功能旗標的 Azure 入口網站。

  3. 移至 [遙測] 索引標籤,然後核取 [啟用遙測] 方塊。

  4. 移至 [實驗] 索引標籤,核取 [建立實驗] 方塊,然後為您的實驗命名。

  5. 選取 [檢閱 + 更新],然後選取 [更新]

  6. 會有通知指出作業成功。 在 [功能管理員] 中,變體功能旗標應該會在 [實驗] 下具有 [作用中] 的字眼。

建立實驗的計量

分割實驗工作區中的計量是傳送至 Application Insights 的事件量化量值。 此計量有助於評估功能旗標對使用者行為和結果的影響。

稍早更新應用程式時,您已將 _telemetryClient.TrackEvent("Like") 新增至應用程式的程式碼。 Like 是代表使用者動作的遙測事件,在此案例中為選取 [心型] 按鈕。 此事件會傳送至 Application Insights 資源,您會將此資源連線到即將建立的計量。 我們建立的應用程式只會指定一個事件,但您可以有多個事件,後續也可以有多個計量。 多個計量也能以單一 Application Insight 事件為基礎。

  1. 瀏覽至分割實驗工作區資源。 在 [設定]>[實驗計量] 底下,選取 [建立]

  2. 在 [建立實驗計量] 底下選取或輸入下列資訊,並使用 [建立] 來儲存。

    螢幕擷取畫面:建立新實驗計量的 Azure 入口網站。

    設定 範例值 描述
    名稱 心型投票 實驗計量的名稱。
    說明 計算人們在看到特殊訊息與沒看到特殊訊息時,選取心型按鈕的人數。 計量的選擇性說明。
    Application Insights 事件名稱 Like Application Insights 事件的名稱。 此名稱區分大小寫,而且是程式碼中使用 _telemetryClient.TrackEvent("<Event-Name>") 所指定的名稱。
    測量形式 Count 有下列選項可供使用:
    • 計數:計算使用者觸發事件的次數。
    • 平均:將使用者的事件值平均。
    • 總和:將使用者的事件值加總。 顯示平均總和值。
    • 百分比:計算觸發事件的使用者百分比。
    所需的影響 增加 此設定代表測量所建立計量的最終目標或目的。

    在本教學課程中,我們的假設是,當 Quote of the Day 旁有特殊訊息時,按心型讚按鈕的使用者會越多。 應用程式的程式碼會以名為按讚的事件來追蹤此點選。 應用程式會將按讚事件作為遙測資料傳送至 Application Insights,此實驗的所需的影響是看到心型投票 的使用者點選次數增加 (以計數的形式來測量),以便能夠驗證指定的假設。 如果已向所配置的對象顯示特殊訊息,但點選該按鈕的次數還是減少,則代表此實驗的假設無效。

  3. 建立後便會在入口網站中顯示新的計量。 您可以選取畫面右側的 (...) 捷徑功能表來加以編輯或刪除。

    螢幕擷取畫面:顯示實驗計量的 Azure 入口網站。

取得實驗結果

若要讓新設定的實驗進行測試,並產生結果以供分析,請模擬一些傳送至應用程式的流量,並等候 10 到 15 分鐘。

若要檢視實驗的結果,請瀏覽至 [功能管理員],然後在變體功能旗標清單上,按一下 [...]>[實驗]或選取 [實驗] 資料行中的 [作用中] 連結。 預設不會顯示此資料行。 若要顯示它,請在 [功能管理員]中,選取 [管理檢視]>[編輯資料行]>[新增資料行]>[實驗],然後 [套用]

在結果頁面上,預設會選取實驗的 [版本]、用來比較結果的 [基準],以及 [比較] 變體。 如有需要,請根據您的喜好變更預設值,然後選取 [套用] 以檢視實驗結果。

螢幕擷取畫面:顯示實驗結果的 Azure 入口網站。

上述螢幕擷取畫面顯示實驗具有所需的結果,心型投票On 變體所產生的心型投票,比 Off 變體多 560.62%。

任何對變體功能旗標的編輯都會產生新版本的實驗,加以選取即可檢視其結果。

注意

若要取得實驗結果,每個變體至少需要 30 個事件,但建議您使用的數量要超過取樣大小下限,以確保實驗能產生可靠的結果。

注意

預設會啟用 Application Insights 取樣,此取樣可能會影響實驗結果。 在進行本教學課程時,建議您關閉 Application Insights 中的取樣。 深入了解如何在 Application Insights 中取樣

後續步驟

若要深入了解實驗概念,請參閱下列文件。

如需 .NET 功能管理程式庫的完整功能摘要,請繼續參閱下列文件。