チュートリアル: .NET アプリでプッシュ更新による動的な構成を使用する

App Configuration .NET クライアント ライブラリでは、アプリケーションを再起動させることなく必要に応じて構成を更新できます。 次の 2 つの方法のどちらかまたは両方を使用して、App Configuration 内の変更を検出するようにアプリケーションを構成することができます。

  1. ポーリング モデル: ポーリングを使用して構成の変更を検出する既定の動作です。 キャッシュされた設定値の有効期限が切れた後、次に TryRefreshAsync または RefreshAsync を呼び出すと、構成が変更されているかどうかをチェックするための要求がサーバーに送信され、必要に応じて、更新された構成がプルされます。

  2. プッシュ モデル: App Configuration のイベントを使用して構成の変更を検出します。 キー値の変更イベントを Azure Event Grid に送信するよう App Configuration を一度設定すれば、アプリケーションは、それらのイベントを使用できるので、構成を最新の状態に保つために必要な要求の総数は最小限で済みます。 アプリケーションは、Event Grid から直接イベントをサブスクライブするか、Webhook、Azure 関数、Service Bus トピックなど、サポートされているイベント ハンドラーのいずれかを使用してイベントをサブスクライブすることができます。

このチュートリアルでは、自分が作成するコードに、プッシュ更新を使用して構成の動的更新を実装する方法について説明します。 これは、チュートリアルで紹介されているアプリに基づいています。 先に進む前に、「チュートリアル: .NET アプリで動的な構成を使用する」を先に済ませておいてください。

このチュートリアルの手順は、任意のコード エディターを使用して実行できます。 推奨のエディターは Visual Studio Code です (Windows、macOS、および Linux プラットフォームで使用できます)。

このチュートリアルでは、以下の内容を学習します。

  • App Configuration から Service Bus トピックに構成の変更イベントを送信するようにサブスクリプションを設定する
  • App Configuration への変更に合わせて構成を更新するように .NET アプリを設定する。
  • 最新の構成をアプリケーションに取り込む。

前提条件

Azure Service Bus のトピックとサブスクリプションを設定する

このチュートリアルでは、App Configuration に絶えず変更をポーリングすることが望ましくないアプリケーションで構成変更の検出を単純化するために、Service Bus と Event Grid の統合を使用します。 Azure Service Bus SDK には、メッセージ ハンドラーを登録するための API が用意されています。この API を使用することで、App Configuration で変更が検出されたときに構成を更新することができます。 Azure portal を使用して Service Bus トピックとそのサブスクリプションを作成するクイック スタートの手順に従って、サービス バスの名前空間、トピック、サブスクリプションを作成します。

リソースの作成後、以下の環境変数を追加します。 アプリケーション コードで構成変更のイベント ハンドラーを登録する際に使用します。

Key
ServiceBusConnectionString サービス バスの名前空間の接続文字列
ServiceBusTopic Service Bus トピックの名前
ServiceBusSubscription Service Bus サブスクリプションの名前

イベント サブスクリプションの設定

  1. Azure portal で App Configuration リソースを開き、[Events] ペインの [+ Event Subscription] をクリックします。

    App Configuration イベント

  2. Event SubscriptionSystem Topic の名前を入力します。

    イベント サブスクリプションの作成

  3. [Endpoint Type] に [Service Bus Topic] を選択し、Service Bus トピックを選択して、[Confirm Selection] をクリックします。

    イベント サブスクリプションの Service Bus エンドポイント

  4. [Create] をクリックしてイベント サブスクリプションを作成します。

  5. [Events] ペインの [Event Subscriptions] をクリックして、サブスクリプションが正しく作成されたことを確認します。

    App Configuration イベント サブスクリプション

注意

構成の変更をサブスクライブするとき、1 つまたは複数のフィルターを使用することで、アプリケーションに送信されるイベントの数を減らすことができます。 これらは、Event Grid サブスクリプション フィルターまたは Service Bus サブスクリプション フィルターとして構成できます。 たとえばサブスクリプション フィルターを使用すると、特定の文字列で始まるキーの変更イベントだけをサブスクライブすることができます。

App Configuration からデータを再度読み込むためのイベント ハンドラーを登録する

Program.cs を開き、ファイルを次のコードで更新します。

using Azure.Messaging.EventGrid;
using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureAppConfiguration;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
using System;
using System.Threading.Tasks;

namespace TestConsole
{
    class Program
    {
        private const string AppConfigurationConnectionStringEnvVarName = "AppConfigurationConnectionString";
        // e.g. Endpoint=https://{store_name}.azconfig.io;Id={id};Secret={secret}
        
        private const string ServiceBusConnectionStringEnvVarName = "ServiceBusConnectionString";
        // e.g. Endpoint=sb://{service_bus_name}.servicebus.windows.net/;SharedAccessKeyName={key_name};SharedAccessKey={key}
        
        private const string ServiceBusTopicEnvVarName = "ServiceBusTopic";
        private const string ServiceBusSubscriptionEnvVarName = "ServiceBusSubscription";

        private static IConfigurationRefresher _refresher = null;

        static async Task Main(string[] args)
        {
            string appConfigurationConnectionString = Environment.GetEnvironmentVariable(AppConfigurationConnectionStringEnvVarName);

            IConfiguration configuration = new ConfigurationBuilder()
                .AddAzureAppConfiguration(options =>
                {
                    options.Connect(appConfigurationConnectionString);
                    options.ConfigureRefresh(refresh =>
                        refresh
                            .Register("TestApp:Settings:Message")
                            // Important: Reduce poll frequency
                            .SetCacheExpiration(TimeSpan.FromDays(1))  
                    );

                    _refresher = options.GetRefresher();
                }).Build();

            await RegisterRefreshEventHandler();
            var message = configuration["TestApp:Settings:Message"];
            Console.WriteLine($"Initial value: {configuration["TestApp:Settings:Message"]}");

            while (true)
            {
                await _refresher.TryRefreshAsync();

                if (configuration["TestApp:Settings:Message"] != message)
                {
                    Console.WriteLine($"New value: {configuration["TestApp:Settings:Message"]}");
                    message = configuration["TestApp:Settings:Message"];
                }

                await Task.Delay(TimeSpan.FromSeconds(1));
            }
        }

        private static async Task RegisterRefreshEventHandler()
        {
            string serviceBusConnectionString = Environment.GetEnvironmentVariable(ServiceBusConnectionStringEnvVarName);
            string serviceBusTopic = Environment.GetEnvironmentVariable(ServiceBusTopicEnvVarName);
            string serviceBusSubscription = Environment.GetEnvironmentVariable(ServiceBusSubscriptionEnvVarName); 
            ServiceBusClient serviceBusClient = new ServiceBusClient(serviceBusConnectionString);
            ServiceBusProcessor serviceBusProcessor = serviceBusClient.CreateProcessor(serviceBusTopic, serviceBusSubscription);

            serviceBusProcessor.ProcessMessageAsync += (processMessageEventArgs) =>
            {
                // Build EventGridEvent from notification message
                EventGridEvent eventGridEvent = EventGridEvent.Parse(BinaryData.FromBytes(processMessageEventArgs.Message.Body));

                // Create PushNotification from eventGridEvent
                eventGridEvent.TryCreatePushNotification(out PushNotification pushNotification);

                // Prompt Configuration Refresh based on the PushNotification
                _refresher.ProcessPushNotification(pushNotification);

                return Task.CompletedTask;
            };

            serviceBusProcessor.ProcessErrorAsync += (exceptionargs) =>
            {
                Console.WriteLine($"{exceptionargs.Exception}");
                return Task.CompletedTask;
            };

            await serviceBusProcessor.StartProcessingAsync();
        }
    }
}

このProcessPushNotificationメソッドでは、キャッシュの有効期限を短いランダム遅延にリセットします。 これにより、RefreshAsync または TryRefreshAsync 以降の呼び出しでは、キャッシュされた値が App Configuration に対して再検証され、必要に応じて更新されます。 この例では、TestApp:Settings:Message キーの変更を監視するために登録し、キャッシュの有効期限を 1 日に設定しています。 つまり、最後のチェックから1日が経過する前には、App Configuration への要求は行われません。 ProcessPushNotification アプリケーションを呼び出すと、次の数秒以内に App Configuration に要求が送信されます。 アプリケーションでは、App Configuration ストア内で 変更が発生した直後に新しい構成値が読み込まれます。更新を継続的にポーリングする必要はありません。 アプリケーションが何らかの理由で変更通知に失敗した場合でも、構成の変更を 1 日に 1 回確認します。

キャッシュの有効期限をランダムに短くすることは、アプリケーションまたはマイクロサービスのインスタンスの多くがプッシュ モデルで同じ App Configuration ストアに接続している場合に便利です。 この遅延がなければ、アプリケーションのすべてのインスタンスは、変更通知を受け取るとすぐに、App Configuration ストアに要求を同時に送信できます。 これにより、App Configuration サービスによってストアが制限される可能性があります。 キャッシュの有効期限は、既定では 0 から最大 30 秒までのランダムな数値に設定されますが、オプションのパラメーター maxDelay から ProcessPushNotification のメソッドで 最大値を変更することもできます。

ProcessPushNotification メソッドは、App Configuration のどの変更がプッシュ通知をトリガーしたかに関する情報を含む PushNotification オブジェクトを受け取ります。 これにより、トリガー イベントまでのすべての構成の変更が、次の構成の更新で読み込まれるようになります。 SetDirty メソッドでは、プッシュ通知をトリガーした変更が、直後の構成の更新で読み込まれることが保証されません。 プッシュモデルに対してSetDirtyメソッドを使用する場合は、代わりにProcessPushNotificationメソッドを使用することをお勧めします。

アプリをビルドしてローカルで実行する

  1. AppConfigurationConnectionString という名前の環境変数に、App Configuration ストアへのアクセス キーを設定します。

    Windows コマンド プロンプトを使用してアプリをローカルでビルドして実行するには、次のコマンドを実行し、変更が反映されるようにコマンド プロンプトを再起動します。

    setx AppConfigurationConnectionString "connection-string-of-your-app-configuration-store"
    
  2. 次のコマンドを実行して、コンソール アプリをビルドします。

    dotnet build
    
  3. ビルドが正常に完了したら、次のコマンドを実行して、アプリをローカルで実行します。

    dotnet run
    

    プッシュ更新の実行 (更新前)

  4. Azure portal にサインインします。 [すべてのリソース] を選択し、クイック スタートで作成した App Configuration ストア インスタンスを選択します。

  5. [Configuration Explorer]\(構成エクスプローラー) を選択して次のキーの値を更新します。

    Key
    TestApp:Settings:Message Azure App Configuration からのデータ - 更新済み
  6. イベントが処理されるまでしばらく待ちます。 更新後の構成が表示されます。

    プッシュ更新の実行 (更新後)

リソースをクリーンアップする

この記事で作成したリソースを継続して使用しない場合は、ここで作成したリソース グループを削除して課金されないようにしてください。

重要

リソース グループを削除すると、元に戻すことができません。 リソース グループとそのすべてのリソースは完全に削除されます。 間違ったリソース グループやリソースをうっかり削除しないようにしてください。 この記事のリソースを、保持したい他のリソースを含むリソース グループ内に作成した場合は、リソース グループを削除する代わりに、各リソースをそれぞれのペインから個別に削除します。

  1. Azure portal にサインインし、 [リソース グループ] を選択します。
  2. [名前でフィルター] ボックスにリソース グループの名前を入力します。
  3. 結果一覧でリソース グループ名を選択し、概要を表示します。
  4. [リソース グループの削除] を選択します。
  5. リソース グループの削除の確認を求めるメッセージが表示されます。 確認のためにリソース グループの名前を入力し、 [削除] を選択します。

しばらくすると、リソース グループとそのすべてのリソースが削除されます。

次のステップ

このチュートリアルでは、App Configuration から動的に構成設定を更新できるように .NET アプリを設定しました。 App Configuration へのアクセスを効率化する Azure マネージド ID を使用する方法については、次のチュートリアルに進んでください。