演習 - キャッシュにアプリを接続する

完了

Azure で Redis Cache が作成されたので、それを使用するアプリケーションを作成しましょう。 Azure portal から取得した接続文字列情報があることを確認します。

Note

統合された Cloud Shell は右側で利用できます。 そのコマンド プロンプトを使用して、ここでビルドするコード例を作成して実行できます。また、.NET Core 開発環境を設定している場合は、次の手順をローカルで実行できます。

コンソール アプリケーションの作成

Redis 実装に集中できるように、コンソール アプリケーションを使用します。

  1. Cloud Shell で、新しい .NET Core コンソール アプリケーションを作成し、SportsStatsTracker という名前を付けます。

    dotnet new console --name SportsStatsTracker
    
  2. 現在のディレクトリを新しいプロジェクト用のフォルダーに変更します。

    cd SportsStatsTracker
    

接続文字列を追加する

Azure portal から取得した接続文字列をコードに追加しましょう。 このような資格証明は、ソース コードに保存しないでください。 このサンプルをシンプルにするために、構成ファイルを使用します。 Azure のサーバー側アプリケーションに適した方法は、Azure Key Vault と認定資格証を使用することです。

  1. 新しい appsettings.json ファイルを作成してプロジェクトに追加します。

    touch appsettings.json
    
  2. プロジェクト フォルダーで「code .」と入力して、コード エディターを開きます。 ローカルで作業している場合は、Visual Studio Code を使用することをお勧めします。 ここでの手順のほとんどが、その使用手順に対応しています。

  3. エディターで appsettings.json ファイルを選択し、次のテキストを追加します。 使用する接続文字列を設定のに貼り付けます。

    {
      "CacheConnection": "[value-goes-here]"
    }
    
  4. 変更を保存します。

    重要

    エディターでファイルにコードを貼り付けたり、コードを変更したりした場合は、その後に必ず [...] メニューまたはアクセス キー (Windows および Linux の場合は Ctrl + S キー、macOS の場合は Cmd + S キー) を使用して保存してください。

  5. エディターで SportsStatsTracker.csproj ファイルを選択して開きます。

  6. 次の <ItemGroup> 構成ブロックを、<PropertyGroup> 要素の下のルート <Project> 要素に追加します。 この構成では、プロジェクトに新しいファイルを含め、それを出力フォルダーにコピーします。 このブロックの手順により、アプリがコンパイルまたはビルドされるときに、アプリ構成ファイルが出力ディレクトリに確実に配置されるようになります。

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
          ...
      </PropertyGroup>
    
      <ItemGroup>
         <None Update="appsettings.json">
           <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
         </None>
      </ItemGroup>
    
    </Project>
    
  7. ファイルを保存します。

    重要

    ファイルを保存しないと、後でパッケージを追加するときに変更内容が失われます。

JSON 構成ファイルを読み込むためのサポートを追加する

.NET Core アプリケーションでは、JSON 構成ファイルを読み取るための他の NuGet パッケージが必要です。

ウィンドウのコマンド プロンプト セクションで、Microsoft.Extensions.Configuration.Json NuGet パッケージへの参照を追加します。

dotnet add package Microsoft.Extensions.Configuration.Json

構成ファイルを読み取るためのコードを追加する

読み取り構成を有効にするために必要なライブラリが追加されたので、コンソール アプリケーション内でその機能を有効にする必要があります。

  1. エディターで Program.cs を選択します。 ファイルの内容を次のコードに置き換えます。

    using Microsoft.Extensions.Configuration;
    
    namespace SportsStatsTracker
    {
        class Program
        {
            static void Main(string[] args)
            {
                var config = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json")
                    .Build();
            }
        }
    }
    

    using ステートメントを使用すると、ライブラリにアクセスして構成を読み取ることができ、Main メソッドのコードで appsettings.json ファイルから読み取る構成システムが初期化されます。

構成から接続文字列を取得する

Program.csMain メソッドの末尾で、新しい config 変数を使用して接続文字列を取得し、connectionString という名前の新しい変数に格納します。

config 変数には、appSettings.json ファイルから取得する文字列を渡すことができるインデクサーが含まれています。

string connectionString = config["CacheConnection"];

Redis Cache .NET クライアントのサポートを追加する

次に、.NET 用の StackExchange.Redis クライアントを使用するようにコンソール アプリケーションを構成しましょう。

  1. Cloud Shell エディターの下部にあるコマンド プロンプトを使用して、StackExchange.Redis NuGet パッケージをプロジェクトに追加します。

    dotnet add package StackExchange.Redis
    
  2. エディターで Program.cs を選択し、StackExchange.Redis 名前空間の using を追加します。

    using StackExchange.Redis;
    

インストールが完了すると、Redis Cache クライアントをプロジェクトで使用できるようになります。

キャッシュに接続する

キャッシュに接続するコードを追加しましょう。

  1. エディターで Program.cs を選択します。

  2. ConnectionMultiplexer.Connect を使用し、これに接続文字列を渡して ConnectionMultiplexer を作成します。 戻り値に cache という名前を付けます。

  3. 作成された接続は "破棄可能" であるため、using ブロックにラップします。 コードは次のようになります。

    string connectionString = config["CacheConnection"];
    
    using (var cache = ConnectionMultiplexer.Connect(connectionString))
    {
    
    }
    

Note

Azure Cache for Redis への接続には、ConnectionMultiplexer クラスを使用します。 このクラスは、クライアント アプリケーション全体で共有して再利用する必要があります。 操作ごとに新しい接続を作成するのは望ましくありません。 代わりに、接続をクラスのフィールドとして保存し、各操作で再利用します。 ここでは、Main メソッドでのみ使用しますが、運用アプリケーションでは、クラス フィールドまたはシングルトンに格納する必要があります。

キャッシュに値を追加する

接続が作成されたので、キャッシュに値を追加しましょう。

  1. 接続が作成された後の using ブロック内で、GetDatabase メソッドを使用して IDatabase インスタンスを取得します。

     IDatabase db = cache.GetDatabase();
    
  2. IDatabase オブジェクトに対して StringSet を呼び出して、キー "test:key" を値 "some value" に設定します。

StringSet からの戻り値は、bool であり、キーが追加されたかどうかを示します。

  1. StringSet からの戻り値をコンソールに表示します。

     bool setValue = db.StringSet("test:key", "some value");
     Console.WriteLine($"SET: {setValue}");
    

キャッシュから値を取得する

  1. 次に、StringGet を使用して値を取得します。 このメソッドではキーを取得して値を返します。

  2. 戻り値を出力します。

     string? getValue = db.StringGet("test:key");
     Console.WriteLine($"GET: {getValue}");
    
  3. コードは次のようになります。

     using System;
     using Microsoft.Extensions.Configuration;
     using System.IO;
     using StackExchange.Redis;
    
     namespace SportsStatsTracker
     {
         class Program
         {
             static void Main(string[] args)
             {
                 var config = new ConfigurationBuilder()
                     .SetBasePath(Directory.GetCurrentDirectory())
                     .AddJsonFile("appsettings.json")
                     .Build();
    
                 string connectionString = config["CacheConnection"];
    
                 using (var cache = ConnectionMultiplexer.Connect(connectionString))
                 {
                     IDatabase db = cache.GetDatabase();
    
                     bool setValue = db.StringSet("test:key", "some value");
                     Console.WriteLine($"SET: {setValue}");
    
                     string? getValue = db.StringGet("test:key");
                     Console.WriteLine($"GET: {getValue}");
                 }
             }
         }
     }
    
  4. アプリケーションを実行して結果を確認します。 エディターの下のターミナル ウィンドウに「dotnet run」と入力します。 必ずプロジェクト フォルダーに移動してください。そうしないと、ビルドして実行するコードが見つかりません。

     dotnet run
    

ヒント

コンパイルしてもプログラムが想定した内容を実行しない場合は、原因として、エディターで変更を保存しなかったことが考えられます。 ターミナル ウィンドウとエディター ウィンドウを切り替えるときは、必ず変更内容を保存してください。

非同期バージョンのメソッドを使用する

キャッシュから値を取得および設定できましたが、これらのメソッドの古い同期バージョンを使用しています。 サーバー側アプリケーションの場合、これらのメソッドではスレッドを効率的に使用できません。 代わりに、''非同期'' バージョンを使用します。 非同期バージョンはすべて末尾が Async であるため、簡単に見つけることができます。

これらのメソッドを操作しやすくするために、C# の async および await キーワードを使用できます。 統合 Cloud Shell ではなく独自の .NET Core 開発環境を使用している場合、これらのキーワードを Main メソッドに適用できるようにするには、''少なくとも'' C# 7.1 を使用する必要があります。

async キーワードを適用する

async キーワードを Main メソッドに適用します。 2 つのことを行う必要があります。

  1. async キーワードを Main メソッド シグネチャに追加します。

  2. 戻り値の型を void から Task に変更します。

    using Microsoft.Extensions.Configuration;
    using StackExchange.Redis;
    
    namespace SportsStatsTracker
    {
       class Program
       {
          static async Task Main(string[] args)
          {
             ...
    

値を非同期に取得および設定する

同期メソッドは、そのままにしておくことができます。 StringSetAsync メソッドと StringGetAsync メソッドの呼び出しを追加して、別の値をキャッシュに追加しましょう。 counter の値を 100 に設定します。

  1. StringSetAsync および StringGetAsync メソッドを使用して、counter という名前のキーを設定および取得します。 値を 100 に設定します。

  2. await キーワードを適用して、各メソッドから結果を取得します。

  3. 同期バージョンで行ったように、コンソール ウィンドウに結果を出力します。

    // Simple get and put of integral data types into the cache
    setValue = await db.StringSetAsync("counter", "100");
    Console.WriteLine($"SET: {setValue}");
    
    getValue = await db.StringGetAsync("counter");
    Console.WriteLine($"GET: {getValue}");
    
  4. アプリケーションをもう一度実行する 引き続き動作して、2 つの値が存在するようになります。

値を増分する

  1. StringIncrementAsync メソッドを使用して counter 値を増分します。 カウンターに追加する数値 50 を渡します。

    • このメソッドは、キーlong または double を取得することに注意してください。

    • 渡されたパラメーターに応じて、long または double が返されます。

  2. メソッドの結果をコンソールに出力します。

    long newValue = await db.StringIncrementAsync("counter", 50);
    Console.WriteLine($"INCR new value = {newValue}");
    

その他の操作

最後に、ExecuteAsync をサポートするメソッドをさらにいくつか実行してみましょう。

  1. PING を実行して、サーバー接続をテストします。 これは PONG で応答します。

  2. FLUSHDB を実行してデータベース値をクリアします。 これは OK で応答します。

    var result = await db.ExecuteAsync("ping");
    Console.WriteLine($"PING = {result.Type} : {result}");
    
    result = await db.ExecuteAsync("flushdb");
    Console.WriteLine($"FLUSHDB = {result.Type} : {result}");
    

最終的なコードは次のようになります。

using Microsoft.Extensions.Configuration;
using StackExchange.Redis;

namespace SportsStatsTracker
{
   class Program
   {
      static async Task Main(string[] args)
      {
         var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();

         string connectionString = config["CacheConnection"];

         using (var cache = ConnectionMultiplexer.Connect(connectionString))
         {
            IDatabase db = cache.GetDatabase();

            bool setValue = db.StringSet("test:key", "some value");
            Console.WriteLine($"SET: {setValue}");

            string getValue = db.StringGet("test:key");
            Console.WriteLine($"GET: {getValue}");

            setValue = await db.StringSetAsync("counter", "100");
            Console.WriteLine($"SET: {setValue}");

            getValue = await db.StringGetAsync("counter");
            Console.WriteLine($"GET: {getValue}");

            long newValue = await db.StringIncrementAsync("counter", 50);
            Console.WriteLine($"INCR new value = {newValue}");  

            var result = await db.ExecuteAsync("ping");
            Console.WriteLine($"PING = {result.Type} : {result}");

            result = await db.ExecuteAsync("flushdb");
            Console.WriteLine($"FLUSHDB = {result.Type} : {result}");
         }
      }
   }
}

もう一度アプリケーションを実行すると、次のような出力が表示されるはずです。

SET: True
GET: some value
SET: True
GET: 100
INCR new value = 150
PING = SimpleString : PONG
FLUSHDB = SimpleString : OK

チャレンジ

チャレンジとして、オブジェクト型をシリアル化してキャッシュに格納してみてください。 基本的な手順は次のとおりです。

  1. いくつかのパブリック プロパティを持つ新しい class を作成します。 独自のクラス ("Person" や "Car" が一般的) を作成することも、前のユニットで提供された "GameStats" の例を使用することもできます。

  2. dotnet add package を使用して、Newtonsoft.Json NuGet パッケージのサポートを追加します。

  3. Newtonsoft.Json 名前空間の using を追加します。

  4. オブジェクトのいずれかを作成します。

  5. JsonConvert.SerializeObject を使用してオブジェクトをシリアル化し、StringSetAsync を使用してキャッシュにプッシュします。

  6. StringGetAsync を使用してこれをキャッシュから取得し、JsonConvert.DeserializeObject<T> を使用して逆シリアル化します。

Azure で Redis Cache が作成されたので、それを使用するアプリケーションを作成しましょう。 Azure portal から接続情報が取得されていることを確認します。

Note

統合された Cloud Shell は右側で利用できます。 そのコマンド プロンプトを使用して、ここでビルドするサンプル コードを作成し、実行できます。また、Node.js 開発環境を設定している場合は、以下の手順をローカルで実行できます。

コンソール アプリケーションを作成する

Redis 実装に集中できるように、コンソール アプリケーションを使用します。

  1. Cloud Shell で、redisapp という名前の新しいディレクトリを作成し、そこで新しい Node.js アプリを初期化します。

     mkdir redisapp
     cd redisapp
     npm init -y
     touch app.js
    
  2. このアプリでは、次の npm パッケージを使用します。

    • redis: Redis に接続するために最もよく使用される JavaScript パッケージです。
    • bluebird: redis パッケージ内のコールバックスタイルのメソッドを待機可能な Promise に変換するために使用します。
    • dotenv: .env ファイルから環境変数を読み込みます。ここには、Redis の接続情報を格納します。

    これらを今すぐインストールしてみましょう。 次のコマンドを実行して、アプリに追加します。

    npm install redis bluebird dotenv
    

構成を追加する

Azure portal から受け取った接続情報を .env 構成ファイルに追加してみましょう。

  1. プロジェクトに新しい .env ファイルを作成します。

    touch .env
    
  2. プロジェクト フォルダーで「code .」と入力して、コード エディターを開きます。 ローカルで作業している場合は、Visual Studio Code を使用することをお勧めします。 ここでの手順のほとんどが、その使用手順に対応しています。

  3. エディターで .env ファイルを選択し、次のテキストに貼り付けます。

    REDISHOSTNAME=
    REDISKEY=
    REDISPORT=
    
  4. それぞれの行の等号の後に、ホスト名、主キー、ポートを貼り付けます。 完了すると、ファイルは次の例のようになります。

    REDISHOSTNAME=myredishost.redis.cache.windows.net
    REDISKEY=K21mLSMN++z8d1FvIeMGy3VOAgoOmqaNYCqeE44eMDc=
    REDISPORT=6380
    
  5. Windows と Linux では Ctrl + S キー、macOS では Cmd + S キーを使用してファイルを保存します。

実装を設定する

では、アプリケーションのコードを記述しましょう。

  1. エディターで app.js を選択します。

  2. まず、require ステートメントを追加します。 ファイルの先頭に次のコードを貼り付けます。

    var Promise = require("bluebird");
    var redis = require("redis");
    
  3. 次に、.env 構成を読み込み、bluebird の promisifyAll 関数を使用して redis パッケージの関数とメソッドを待機可能な Promise に変換します。 次のコードを貼り付けます。

    require("dotenv").config();
    Promise.promisifyAll(redis);
    
  4. 次に、Redis クライアントを初期化します。 前のユニットの定型コードを貼り付け (ホスト名、ポート、キーにアクセスするには process.env を使用します)、クライアントを作成します。

    const client = redis.createClient(
       process.env.REDISPORT,
       process.env.REDISHOSTNAME,
       {
          password: process.env.REDISKEY,
          tls: { servername: process.env.REDISHOSTNAME }
       }
    );
    

クライアントを使用してキャッシュを操作する

Redis Cache と対話するコードを記述する準備ができました。

  1. 最初に、メイン コードを格納するため、ファイルの末尾に async 関数ラッパーを追加します。 このラッパーは、使用する非同期関数呼び出しの await を可能にするために必要です。 このユニットで追加するコードの残りはすべて、このラッパーに含まれます。

    (async () => {
    
       // The rest of the code you'll paste in goes here.
    
    })();
    
  2. setAsync メソッドを使用して値をキャッシュに追加し、getAsync で読み取ります。

    console.log("Adding value to the cache");
    await client.setAsync("myKey", "myValue");
    
    console.log("Reading value back:");
    console.log(await client.getAsync("myKey"));
    
  3. pingAsync で、キャッシュに ping を送信します。

    console.log("Pinging the cache");
    console.log(await client.pingAsync());
    
  4. flushdbAsync で、キャッシュのすべてのキーを削除します。

    await client.flushdbAsync();
    
  5. 最後に、quitAsync で接続を閉じます。

    await client.quitAsync();
    
  6. ファイルを保存します。 完成したアプリケーションは次のようになります。

    var Promise = require("bluebird");
    var redis = require("redis");
    
    require("dotenv").config();
    
    Promise.promisifyAll(redis);
    
    const client = redis.createClient(
    process.env.REDISPORT,
    process.env.REDISHOSTNAME,
    {
       password: process.env.REDISKEY,
       tls: { servername: process.env.REDISHOSTNAME }
    }
    );
    
    (async () => {
      console.log("Adding value to the cache");
      await client.setAsync("myKey", "myValue");
      console.log("Reading value back:");
      console.log(await client.getAsync("myKey"));
      console.log("Pinging the cache");
      console.log(await client.pingAsync());
      await client.flushdbAsync();
      await client.quitAsync();
    })();
    
  7. アプリケーションを実行します。 Cloud Shell で、次のコマンドを実行します。

    node app.js
    

    次の結果が表示されます。

    Adding value to the cache
    Reading value back:
    myValue
    Pinging the cache
    PONG