Share via


チュートリアル: Azure Functions と Azure Cache for Redis を使用してライトビハインド キャッシュを作成する

このチュートリアルの目的は、Azure Cache for Redis インスタンスをライトビハインド キャッシュとして使用することです。 このチュートリアルのライトビハインド パターンでは、キャッシュへの書き込みによって SQL Database (Azure SQL Database サービスのインターフェイス) に対応する書き込みがトリガーされるしくみを示します。

この機能を実装するために、Azure Functions 用の Redis トリガーを使用します。 このシナリオでは、Azure Cache for Redis を使用してインベントリと価格情報を格納する一方で、その情報を SQL Database にバックアップする方法を示します。

新しいアイテムまたは新しい価格がキャッシュに書き込まれるたびに、データベース内の SQL テーブルに反映されます。

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

  • データベース、トリガー、接続文字列を構成します。
  • トリガーが機能していることを検証します。
  • 関数アプリにコードをデプロイします。

前提条件

  • Azure サブスクリプション。 Azure サブスクリプションをお持ちでない場合は、無料アカウントを作成してください。
  • 前のチュートリアル「Azure Cache for Redis での Azure Functions トリガーの概要」を完了し、これらのリソースがプロビジョニングされている。
    • Azure Cache for Redis インスタンス
    • Azure Functions インスタンス
    • Azure SQL に関する実用的な知識
    • NuGet パッケージがインストールされた Visual Studio Code (VS Code) 環境のセットアップ

新しい SQL データベースを作成して構成する

SQL データベースは、この例のバッキング データベースです。 SQL データベースは、Azure portal または任意の自動化方法を使用して作成できます。

SQL データベースの作成の詳細については、「クイックスタート: 単一データベースを作成する - Azure SQL Database」を参照してください。

以下の例ではポータルを使用しています。

  1. データベース名を入力し、[新規作成] を選択して、データベースを保持する新しいサーバーを作成します。

    Azure SQL リソースの作成を示すスクリーンショット。

  2. [SQL 認証を使用する] を選択し、管理者のサインインとパスワードを入力します。 これらの資格情報を覚えておくか、書き留めておいてください。 運用環境でサーバーをデプロイするときは、代わりに Microsoft Entra 認証を使用します。

    Azure SQL リソースの認証情報のスクリーンショット。

  3. [ネットワーク] タブに移動し、接続方法として [パブリック エンドポイント] を選択します。 表示される両方のファイアウォール規則について [はい] を選択します。 このエンドポイントでは、Azure Functions アプリからのアクセスを許可します。

    Azure SQL リソースのネットワーク設定のスクリーンショット。

  4. 検証が終了したら、[確認と作成] を選択し、[作成] を選択します。 SQL データベースのデプロイが開始します。

  5. デプロイが終了したら、Azure portal でリソースに移動し、[クエリ エディター] タブを選択します。書き込むデータを保持する inventory という名前の新しいテーブルを作成します。 次の SQL コマンドを使用して、2 つのフィールドを含む新しいテーブルを作成します。

    • ItemName は各アイテムの名前を一覧表示します。
    • Price はアイテムの価格を格納します。
    CREATE TABLE inventory (
        ItemName varchar(255),
        Price decimal(18,2)
        );
    

    Azure SQL リソースのクエリ エディターでのテーブルの作成を示すスクリーンショット。

  6. コマンドの実行を修了したら、Tables フォルダーを展開し、新しいテーブルが作成されたことを確認します。

Redis トリガーを構成する

まず、前のチュートリアルで使用したのと同じ VS Code プロジェクトのコピーを作成します。 前のチュートリアルのフォルダーを RedisWriteBehindTrigger などの新しい名前でコピーし、VS Code で開きます。

次に、RedisBindings.cs ファイルと RedisTriggers.cs ファイルを削除します。

この例では、pub/sub トリガーを使用して keyevent 通知でトリガーします。 この例の目標は次のとおりです。

  • SET イベントが発生するたびにトリガーします。 SET イベントは、新しいキーがキャッシュ インスタンスに書き込まれているとき、またはキーの値が変更されているときに発生します。
  • SET イベントがトリガーされたら、キャッシュ インスタンスにアクセスして、新しいキーの値を見つけます。
  • SQL データベース内の inventory テーブルにキーが既に存在しているかどうかを確認します。
    • 存在している場合は、そのキーの値を更新します。
    • そうでない場合は、キーとその値を含む新しい行を書き込みます。

トリガーを構成するには、以下を行います。

  1. System.Data.SqlClient NuGet パッケージをインポートして、SQL データベースとの通信を有効にします。 VS Code のターミナルに移動し、次のコマンドを使用します。

      dotnet add package System.Data.SqlClient
    
  2. RedisFunction.cs という名前の新しいファイルを作成します。 RedisBindings.cs ファイルと RedisTriggers.cs ファイルを削除していることを確認します。

  3. RedisFunction.cs に次のコードをコピーして貼り付け、既存のコードを置き換えます。

using Microsoft.Extensions.Logging;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Extensions.Redis;
using System.Data.SqlClient;

public class WriteBehindDemo
{
    private readonly ILogger<WriteBehindDemo> logger;

    public WriteBehindDemo(ILogger<WriteBehindDemo> logger)
    {
        this.logger = logger;
    }
    
    public string SQLAddress = System.Environment.GetEnvironmentVariable("SQLConnectionString");

    //This example uses the PubSub trigger to listen to key events on the 'set' operation. A Redis Input binding is used to get the value of the key being set.
    [Function("WriteBehind")]
    public void WriteBehind(
        [RedisPubSubTrigger(Common.connectionString, "__keyevent@0__:set")] Common.ChannelMessage channelMessage,
        [RedisInput(Common.connectionString, "GET {Message}")] string setValue)
    {
        var key = channelMessage.Message; //The name of the key that was set
        var value = 0.0;

        //Check if the value is a number. If not, log an error and return.
        if (double.TryParse(setValue, out double result))
        {
            value = result; //The value that was set. (i.e. the price.)
            logger.LogInformation($"Key '{channelMessage.Message}' was set to value '{value}'");
        }
        else
        {
            logger.LogInformation($"Invalid input for key '{key}'. A number is expected.");
            return;
        }        

        // Define the name of the table you created and the column names.
        String tableName = "dbo.inventory";
        String column1Value = "ItemName";
        String column2Value = "Price";        
        
        logger.LogInformation($" '{SQLAddress}'");
        using (SqlConnection connection = new SqlConnection(SQLAddress))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand())
                {
                    command.Connection = connection;

                    //Form the SQL query to update the database. In practice, you would want to use a parameterized query to prevent SQL injection attacks.
                    //An example query would be something like "UPDATE dbo.inventory SET Price = 1.75 WHERE ItemName = 'Apple'".
                    command.CommandText = "UPDATE " + tableName + " SET " + column2Value + " = " + value + " WHERE " + column1Value + " = '" + key + "'";
                    int rowsAffected = command.ExecuteNonQuery(); //The query execution returns the number of rows affected by the query. If the key doesn't exist, it will return 0.

                    if (rowsAffected == 0) //If key doesn't exist, add it to the database
                 {
                         //Form the SQL query to update the database. In practice, you would want to use a parameterized query to prevent SQL injection attacks.
                         //An example query would be something like "INSERT INTO dbo.inventory (ItemName, Price) VALUES ('Bread', '2.55')".
                        command.CommandText = "INSERT INTO " + tableName + " (" + column1Value + ", " + column2Value + ") VALUES ('" + key + "', '" + value + "')";
                        command.ExecuteNonQuery();

                        logger.LogInformation($"Item " + key + " has been added to the database with price " + value + "");
                    }

                    else {
                        logger.LogInformation($"Item " + key + " has been updated to price " + value + "");
                    }
                }
                connection.Close();
            }

            //Log the time that the function was executed.
            logger.LogInformation($"C# Redis trigger function executed at: {DateTime.Now}");
    }
}

重要

この例は、チュートリアル用に単純化しています。 運用環境では、SQL インジェクション攻撃を防ぐために、パラメーター化された SQL クエリを使用することをお勧めします。

接続文字列の構成

SQL データベースの接続文字列を含めるには、local.settings.json ファイルを更新する必要があります。 SQLConnectionStringValues セクションにエントリを追加します。 ファイルは、次の例のようになります。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "redisConnectionString": "<redis-connection-string>",
    "SQLConnectionString": "<sql-connection-string>"
  }
}

Redis 接続文字列を見つけるには、Azure Cache for Redis リソースのリソース メニューに移動します。 文字列は、[設定][アクセス キー] 領域にあります。

SQL データベースの接続文字列を見つけるには、SQL データベース リソースのリソース メニューに移動します。 [文字列] で、[接続文字列][ADO.NET] タブの順に選びます。文字列は、[ADO.NET (SQL 認証)] 領域にあります。

パスワードは自動的に貼り付けられないため、SQL データベース接続文字列のパスワードは手動で入力する必要があります。

重要

この例は、チュートリアル用に単純化しています。 運用環境では、Azure Key Vault を使用して接続文字列情報を格納するか、SQL 認証に Azure EntraID を使用することをお勧めします。

プロジェクトをビルドして実行する

  1. VS Code で、[実行とデバッグ] タブに移動し、プロジェクトを実行します。

  2. Azure portal で Azure Cache for Redis インスタンスに戻り、[コンソール] ボタンを選択して Redis コンソールを実行します。 いくつかの SET コマンドを使用してみます。

    • SET apple 5.25
    • SET bread 2.25
    • SET apple 4.50
  3. VS Code に戻ると、トリガーが登録されています。 トリガーが機能していることを検証します。

    1. Azure portal の SQL データベースに移動します。

    2. [リソース] メニューで [クエリ エディター] を選択します。

    3. 新しいクエリの場合は、次の SQL コマンドを使用して、inventory テーブルに上位 100 アイテムを表示するクエリを作成します。

      SELECT TOP (100) * FROM [dbo].[inventory]
      

      Azure Cache for Redis インスタンスに書き込まれた項目がここに表示されることを確認します。

    情報が SQL からキャッシュ インスタンスにコピーされたことを示すスクリーンショット。

コードを関数アプリにデプロイする

このチュートリアルは、前のチュートリアルに基づいています。 詳細については、「Azure 関数にコードをデプロイする」を参照してください。

  1. VS Code で、[Azure] タブに移動します。

  2. サブスクリプションを検索して展開します。 次に、[関数アプリ] セクションを見つけて展開します。

  3. 関数アプリを長押し (または右クリック) し、[関数アプリにデプロイ] を選択します。

接続文字列情報を追加する

このチュートリアルは、前のチュートリアルに基づいています。 redisConnectionString の詳細については、「接続文字列情報を追加する」を参照してください。

  1. Azure portal で関数アプリにアクセスします。 リソース メニューで [環境変数] を選択します。

  2. [アプリ設定] ペインで、新しいフィールドとして「SQLConnectionString」を入力します。 [値] の場合、接続文字列を入力します。

  3. 適用を選択します。

  4. [概要] ブレードに移動し、[再起動] を選択して、新しい接続文字列情報を使用してアプリを再起動します。

デプロイの確認

デプロイが完了したら、Azure Cache for Redis インスタンスに戻り、SET コマンドを使用してさらに値を書き込みます。 それらが SQL データベースにも表示されることを確認します。

関数アプリの正常動作を確認する場合は、ポータルでアプリに移動し、[リソース] メニューから [ログ ストリーム] を選択します。 トリガーが実行されており、それに対応して SQL データベースが更新されていることを確認します。

SQL データベース テーブルを削除せずにクリアする場合は、次の SQL クエリを使用できます。

TRUNCATE TABLE [dbo].[inventory]

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

この記事で作成したリソースを引き続き使用する場合は、リソース グループを保持します。

それ以外の場合、リソースを使い終わったら、課金されないように、作成した Azure リソース グループを削除できます。

重要

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

リソース グループを削除するには

  1. Azure portal にサインインし、 [リソース グループ] を選択します。

  2. 削除するリソース グループを選択します。

    多数のリソース グループがある場合は、[任意のフィールドのフィルター...] ボックスを使用し、この記事用に作成したリソース グループの名前を入力します。 結果リストでリソース グループを選びます。

    作業ペインの削除するリソース グループの一覧を示すスクリーンショット。

  3. [リソース グループの削除] を選択します。

  4. リソース グループの削除の確認を求めるメッセージが表示されます。 確認のためにリソース グループの名前を入力し、[削除] を選択します。

    削除を確認するためにリソース名を必要とするフォームを示すスクリーンショット。

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

まとめ

このチュートリアルと Azure Cache for Redis での Azure Functions トリガーの概要は、Azure Cache for Redis を使用して Azure Functions アプリをトリガーする方法を示します。 また、Azure SQL Database で Azure Cache for Redis をライトビハインド キャッシュとして使用する方法についても説明します。 Azure Cache for Redis と Azure Functions の併用は、統合とパフォーマンスに関する多くの問題を解決できる強力な組み合わせです。