练习 - 实现应用程序复原能力

已完成

eShop 项目有两个使用 HTTP 请求相互通信的服务。 Store 服务调用 Product 服务以获取所有当前可供购买的产品列表。

当前版本的应用没有实现复原处理。 如果 Product 服务不可用,Store 服务会向客户返回一个错误,并要求他们稍后重试。 此行为不是良好的用户体验。

你的经理要求你向应用增添复原能力,以便 Store 服务在遇到失败时重试后端服务调用。

在本练习中,你将向现有的云原生应用增添复原能力,并测试你的修补。

开放开发环境

可以选择使用托管练习的 GitHub codespace,或者在 Visual Studio Code 中本地完成练习。

若要使用 codespace,请使用此 Codespace 创建链接创建预配置的 GitHub Codespace

GitHub 需要几分钟时间来创建和配置 codespace。 完成此过程后,你会看到练习的代码文件。 用于本模块其余部分的代码位于 /dotnet-resiliency 目录中。

要使用 Visual Studio Code,请将 https://github.com/MicrosoftDocs/mslearn-dotnet-cloudnative 存储库克隆到本地计算机。 然后:

  1. 安装任何系统要求以在 Visual Studio Code 中运行开发容器。
  2. 确保 Docker 正在运行。
  3. 在新的 Visual Studio Code 窗口中,打开克隆存储库的文件夹
  4. Ctrl+Shift+P 打开命令面板。
  5. 搜索:>开发容器:在容器中重新生成和重新打开
  6. 从下拉列表中选择“eShopLite - dotnet-resiliency”。 Visual Studio Code 会在本地创建你的开发容器。

生成并运行应用

  1. 在底部面板中,选择“终端”选项卡并运行以下命令以转到代码根目录:

    cd dotnet-resiliency
    
  2. 运行以下命令以生成 eShop 应用映像:

    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. 生成完成后,运行以下命令以启动应用:

    docker compose up
    
  4. 在底部面板中,选择“端口”选项卡,然后在表的转发地址列中,选择“前端(32000)”端口的“在浏览器中打开”图标。

    如果在本地运行应用,请打开浏览器窗口以查看 http://localhost:32000/products

  5. eShop 应用应正在运行。 选择“产品”菜单项,你应会看到产品列表。

    显示在浏览器中运行的 eShop 应用的屏幕截图。

测试当前复原能力

停止产品服务,看看应用会发生什么情况。

  1. 返回到 codespace,然后在“终端”选项卡中选择 + 以打开新的 bash 终端。

  2. 运行以下 docker 命令以列出正在运行的容器:

    docker ps
    

    应会看到当前正在运行的容器列表,例如:

    CONTAINER ID   IMAGE                                                                            COMMAND                  CREATED          STATUS          PORTS                                                        NAMES
    c08285e8aaa4   storeimage                                                                       "dotnet Store.dll"       8 minutes ago    Up 8 minutes    80/tcp, 443/tcp, 0.0.0.0:5902->8080/tcp, :::5902->8080/tcp   eshoplite-frontend-1
    6ba80f3c7ab0   productservice                                                                   "dotnet Products.dll"    8 minutes ago    Up 8 minutes    80/tcp, 443/tcp, 0.0.0.0:5200->8080/tcp, :::5200->8080/tcp   eshoplite-backend-1
    cd0c822a5222   vsc-eshoplite-958868d22c9851dd911b2423199bfc782861d1a8f7afac48e5096a1b7516082f   "/bin/sh -c 'echo Co…"   27 minutes ago   Up 27 minutes     
    
  3. 查找 productservice 容器的容器 ID。 在上面的示例中,ID 为“6ba80f3c7ab0”。

  4. 使用此 docker 命令来停止产品服务:

    docker stop <CONTAINER ID>
    

    其中 <CONTAINER ID> 是在上一步中找到的 ID。 例如:

    docker stop 6ba80f3c7ab0
    
  5. 返回到运行应用的浏览器选项卡并刷新页面。 应会看到一条错误消息:

    加载产品时出现问题。 请稍后再试。

  6. 返回到你的 codespace,在“TERMINAL”中 ,选择“docker”终端,然后按 Ctrl+C 停止应用。 应会看到:

    Gracefully stopping... (press Ctrl+C again to force)
    Aborting on container exit...
    [+] Stopping 2/1
     ✔ Container eshoplite-frontend-1  Stopped                                                                      0.3s 
     ✔ Container eshoplite-backend-1   Stopped                                                                      0.0s 
    canceled
    

将复原能力添加到应用

使应用更具复原能力的第一步是将 Microsoft.Extensions.Http.Resilience NuGet 包添加到项目。 然后,可以在 Program.cs 中使用它。

添加 Microsoft.Extensions.Http.Resilience 包

  1. 在 codespace 中,在“终端”选项卡上,导航到“存储”项目文件夹:

    cd Store
    
  2. 运行以下命令以添加复原能力 NuGet 包:

    dotnet add package Microsoft.Extensions.Http.Resilience
    

    从终端在应用项目文件夹中运行此命令会将包引用添加到“Store.csproj”项目文件。

  3. 在“EXPLORER”边栏中,选择“Program.cs”。

  4. 在该文件的顶部,添加以下 using 语句:

    using Microsoft.Extensions.Http.Resilience;
    

添加一个标准复原策略

  1. 在第 13 行的 ; 之前,添加此代码:

    .AddStandardResilienceHandler()
    

    代码应如下所示:

    builder.Services.AddHttpClient<ProductService>(c =>
    {
        var url = builder.Configuration["ProductEndpoint"] ?? throw new InvalidOperationException("ProductEndpoint is not set");
    
        c.BaseAddress = new(url);
    }).AddStandardResilienceHandler();
    

    上述代码将标准复原处理程序添加到 HTTPClient。 此处理程序将使用标准复原策略的所有默认设置。

    应用不需要其他代码更改。 让我们运行应用并测试复原能力。

  2. 运行以下命令以重新生成 eShop 应用:

    cd ..
    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. 生成完成后,运行以下命令以启动应用:

    docker compose up
    
  4. 返回到运行应用的浏览器选项卡并刷新产品页面。 应会看到产品列表。

  5. 返回到 codespace,然后在“终端”选项卡中选择第二个 bash 终端。 复制 productservice 容器的容器 ID。

  6. 重新运行 docker stop 命令:

    docker stop <CONTAINER ID>
    
  7. 返回到运行应用的浏览器选项卡并刷新产品页面。 这一次,应花费更长的时间直至看到应用错误消息:

    加载产品时遇到问题。 请稍后再试。

    让我们检查日志,看看复原策略是否正常工作。

  8. 返回到 codespace,然后在“终端”选项卡中选择“docker”终端。

  9. 在终端中,按 Ctrl+C 停止应用运行。

  10. 在日志消息中,向上滚动,直到找到对“Polly”的引用。

    eshoplite-frontend-1  | warn: Polly[3]
    eshoplite-frontend-1  |       Execution attempt. Source: 'ProductService-standard//Standard-Retry', Operation Key: '', Result: 'Name or service not known (backend:8080)', Handled: 'True', Attempt: '2', Execution Time: '27.2703'
    

    你应会看到许多类似的消息,每个消息都是一次重试尝试。 上述消息显示第二次尝试,以及执行该尝试所花费的时间。

配置复原策略

向应用添加复原能力时,需要在快速响应用户和不重载任何后端服务之间平衡需求。 只有你可以决定默认选项是否满足业务需求。

在此示例中,你希望存储服务等待更长的时间,以便为存储服务提供恢复的机会。

  1. 在 Program.cs 的代码窗口中,将第 13 行的代码更改为:

    .AddStandardResilienceHandler(options =>
    {
        options.Retry.MaxRetryAttempts = 7;
    });
    

    上述代码将重试策略默认值更改为最大停用次数为 7。 请记住,此策略是指数退避,因此总时间约为 5 分钟。

  2. 使用 Ctrl+C 以停止 docker up 命令。 然后运行以下命令以重新生成 eShop 应用:

    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. 生成完成后,运行以下命令以启动应用:

    docker compose up
    

    停止 bash 终端中的后端服务容器并刷新 eShop。 请注意,看到错误消息需要更长的时间。 但是如果你检查日志,你会看到重试策略只重试了五次。 来自 Polly 的最后一条消息是:

    Polly.Timeout.TimeoutRejectedException: The operation didn't complete within the allowed timeout of '00:00:30'.
    

    上述消息告诉你,总请求超时阻止达到最大重试次数。 可以通过增加总请求超时来解决此问题。

  4. 在终端中,按 Ctrl+C 以停止应用。

  5. 在 Program.cs 的代码窗口中,将第 13 行的代码更改为:

    .AddStandardResilienceHandler(options =>
    {
        options.Retry.RetryCount = 7;
        options.TotalRequestTimeout = new HttpTimeoutStrategyOptions
        {
            Timeout = TimeSpan.FromMinutes(5)
        };
    });
    

    上述代码将总请求超时更改为 260 秒,从而比重试策略更长。

    通过这些更改,你应该有足够的时间来运行应用、停止产品服务、检查终端日志以重试尝试、刷新 eShop 以查看加载消息,最后重启产品服务以成功查看产品列表。

  6. 运行以下命令以重新生成 eShop 应用:

    dotnet publish /p:PublishProfile=DefaultContainer
    
  7. 生成完成后,运行以下命令以启动应用:

    docker compose up
    

测试新的复原能力选项

为了帮助测试容器中的应用,请使用 Docker 扩展。 该扩展提供了一个 GUI 来查看和控制容器的状态。

  1. 从左侧菜单中选择“Docker”图标。

    Docker 扩展的屏幕截图,其中显示了如何停止产品服务。

  2. 在“DOCKER”面板中的“容器”下,右键单击“products”容器,然后选择“停止”。

  3. 返回到运行应用的浏览器选项卡并刷新产品页面。 你应会看到“正在加载...”消息。

  4. 返回到你的 codespace,然后在“TERMINAL”选项卡中选择“docker”终端。 复原策略正在工作。

  5. 在“DOCKER”面板中的“容器”下,右键单击“products”容器,然后选择“开始”。

  6. 返回到运行应用的浏览器选项卡。 等待,应用应该会恢复并显示产品列表。

  7. 在终端中,使用 Ctrl+C 停止 docker。