演習 - アプリケーションの回復性を実装する

完了

eShop プロジェクトには、HTTP 要求を使って相互に通信する 2 つのサービスがあります。 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 で Dev Container を実行するためのシステム要件をインストールします。
  2. Docker が動作していることを確認します。
  3. 新しい Visual Studio Code ウィンドウで、クローンされたリポジトリのフォルダーを開きます
  4. Ctrl+Shift+P キーを押して、コマンド パレットを開きます。
  5. 検索: >Dev Containers:コンテナーでリビルドして再度開く
  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 コンテナーの CONTAINER ID を探します。 上の例では、ID は 6ba80f3c7ab0 です。

  4. 次の docker コマンドを使って製品サービスを停止します。

    docker stop <CONTAINER ID>
    

    ここで、<CONTAINER ID> は前述の手順で見つけた ID です。 次に例を示します。

    docker stop 6ba80f3c7ab0
    
  5. アプリを実行しているブラウザーのタブに戻り、ページを更新します。 エラー メッセージが表示されているはずです。

    製品の読み込みで問題が発生しました。 後で再度お試しください。

  6. codespace に戻り、[ターミナル]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 の [ターミナル] タブで、Store プロジェクト フォルダーに移動します。

    cd Store
    
  2. 次のコマンドを実行して、回復性 NuGet パッケージを追加します。

    dotnet add package Microsoft.Extensions.Http.Resilience
    

    アプリ プロジェクト フォルダーのターミナルからこのコマンドを実行すると、パッケージ参照が Store.csproj プロジェクト ファイルに追加されます。

  3. [エクスプローラー] サイドバーで、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 に戻り、[ターミナル] タブで 2 番目の bash ターミナルを選びます。 productservice コンテナーの CONTAINER 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'
    

    このようなメッセージが多数表示されます。それぞれが再試行です。 上のメッセージは、2 回目の試行と実行にかかった時間を示しています。

回復性戦略を構成する

アプリに回復性を追加すると、ユーザーに迅速に応答する必要性と、バックエンド サービスに過負荷をかけないようにする必要性のバランスが取られます。 既定のオプションがビジネス ニーズを満たすかどうかを判断できるのはあなただけです。

この例では、ストア サービスが回復する機会を与えるために、ストア サービスを少し長く待機させます。

  1. Program.cs のコード ウィンドウで、13 行目のコードを次のように変更します。

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

    上記のコードでは、再試行戦略の既定の最大リタイア数が 7 に変更されています。 この戦略はエクスポネンシャル バックオフであるため、合計時間は約 5 分であることに注意してください。

  2. Ctrl+C キーを押して docker を停止します。 次に以下のコマンドを実行して、eShop アプリをリビルドします。

    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. ビルドが完了したら、次のコマンドを実行してアプリを起動します。

    docker compose up
    

    bash ターミナルでバックエンド サービス コンテナーを停止し、eShop を更新します。 エラー メッセージが表示されるまでに時間がかかることに注意してください。 ただし、ログを確認すると、再試行戦略が 5 回しか再試行されていないことがわかります。 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 アイコンを選びます。

    products サービスを停止する方法を示す Docker 拡張機能のスクリーンショット。

  2. [DOCKER] パネルの [コンテナー] で、products コンテナーを右クリックし、[停止] を選びます。

  3. アプリを実行しているブラウザーのタブに戻り、製品ページを更新します。 "読み込んでいます..." メッセージが表示されます。

  4. codespace に戻り、[ターミナル] タブで [docker] ターミナルを選んでください。 回復性戦略が機能します。

  5. [DOCKER] パネルの [コンテナー] で、products コンテナーを右クリックし、[開始] を選びます。

  6. アプリを実行しているブラウザーのタブに戻ります。 待機すると、アプリは製品の一覧を表示して回復します。

  7. ターミナルで、Ctrl+C を押して Docker を停止します。