Redis と対話するためのクライアント API を調べる

完了

既に説明したように、Redis は複数のサーバー全体にレプリケートできるインメモリ NoSQL データベースです。 多くの場合、キャッシュとして使用されますが、正式なデータベースまたはメッセージ ブローカーとして使用することもできます。

Redis は、さまざまなデータ型と構造体を格納できます。 サポートされているさまざまなコマンドを発行して、キャッシュ データを取得したり、キャッシュ自体に関する情報を照会したりできます。 使用するデータは、常にキーと値のペアとして格納されます。

Redis Cache でコマンドを実行する

通常、クライアント アプリケーションは "クライアント ライブラリ" を使用して要求を作成し、Redis Cache でコマンドを実行します。 クライアント ライブラリの一覧は、Redis クライアント ページから直接取得できます。 .NET プラットフォーム用の一般的な高性能 Redis クライアントは StackExchange.Redis です。 このパッケージは NuGet から入手でき、コマンド ラインまたは IDE を使って .NET コードに追加できます。

StackExchange.Redis を使用して Redis Cache に接続する

Redis サーバーに接続するには、ホスト アドレス、ポート番号、アクセス キーを使用します。 Azure では、一部の Redis クライアント用に、このデータを 1 つの文字列にまとめた "接続文字列" も提供されます。

接続文字列とは

接続文字列とは、Azure の Redis Cache に接続して認証するために必要なすべての情報を含む 1 行のテキストです。 接続文字列は次のようになります (cache-name フィールドと password-here フィールドには実際の値を入力します)。

[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False

ヒント

接続文字列は、アプリケーション内で保護する必要があります。 アプリケーションが Azure でホストされている場合は、Azure Key Vault を使用して値を格納することを検討してください。

この文字列を StackExchange.Redis に渡して、サーバーへの接続を作成できます。

末尾にさらに 2 つのパラメーターがあることに注意してください。

  • ssl: 通信の暗号化を確保します
  • abortConnection: その時点でサーバーが使用できなくても接続を作成できます

クライアント ライブラリを構成するために文字列に追加できる他の省略可能なパラメーターがいくつかあります。

接続の作成

StackExchange.Redis の主な接続オブジェクトは StackExchange.Redis.ConnectionMultiplexer クラスです。 このオブジェクトは、Redis サーバー (またはサーバーのグループ) への接続プロセスを抽象化します。 接続を効率的に管理するように最適化されており、キャッシュにアクセスする必要がある間は保持されることを目的としています。

静的 ConnectionMultiplexer.Connect メソッドまたは ConnectionMultiplexer.ConnectAsync メソッドを使用して ConnectionMultiplexer インスタンスを作成し、接続文字列または ConfigurationOptions オブジェクトを渡します。

簡単な例を次に示します。

using StackExchange.Redis;
...
var connectionString = "[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False";
var redisConnection = ConnectionMultiplexer.Connect(connectionString);
    // ^^^ store and re-use this!!!

ConnectionMultiplexer を作成したら、主に次の 3 つのことを行うことができます。

  1. Redis データベースにアクセスします (このモジュールの焦点です)。
  2. Redis の発行元/サブスクリプション機能を使用します (このモジュールの範囲外)。
  3. メンテナンスまたは監視のために個々のサーバーにアクセスする。

Redis データベースにアクセスする

Redis データベースは IDatabase 型で表されます。 GetDatabase() メソッドを使用してこれを取得できます。

IDatabase db = redisConnection.GetDatabase();

ヒント

GetDatabase から返されるオブジェクトはライトウェイト オブジェクトであり、保存する必要はありません。 ConnectionMultiplexer だけを保持する必要があります。

IDatabase オブジェクトを取得したら、キャッシュを操作するメソッドを実行できます。 すべてのメソッドには同期バージョンと非同期バージョンがあります。非同期バージョンは、async および await キーワードに対応できるようにするための Task オブジェクトを返します。

キーと値をキャッシュに格納する例を次に示します。

bool wasSet = db.StringSet("favorite:flavor", "i-love-rocky-road");

StringSet メソッドは、値が設定されているか (true)、設定されていないか (false) を示す bool を返します。 値は StringGet メソッドを使用して取得できます。

string value = db.StringGet("favorite:flavor");
Console.WriteLine(value); // displays: ""i-love-rocky-road""

バイナリ値の取得と設定

Redis キーと値は "バイナリ セーフ" であることに注意してください。 これらの同じメソッドを使用して、バイナリ データを保存できます。 データを自然に操作できるように、byte[] 型を処理する暗黙的な変換演算子があります。

byte[] key = ...;
byte[] value = ...;

db.StringSet(key, value);
byte[] key = ...;
byte[] value = db.StringGet(key);

ヒント

StackExchange.Redis では、RedisKey 型を使用してキーを表します。 このクラスでは、stringbyte[] の両方との間で暗黙的な変換が行われるので、複雑さを伴わずにテキスト キーとバイナリ キーの両方を使用できます。 値は RedisValue 型で表されます。 RedisKey と同様に、string または byte[] を渡すことができるように、暗黙的な変換が適切に行われます。

その他の一般的な操作

IDatabase インターフェイスには、Redis Cache で動作する他の複数のメソッドがあります。 ハッシュ、リスト、セット、順序付けされたセットを操作するメソッドがあります。

ここでは、単一キーで動作するより一般的な操作を紹介します。完全なリストについては、このインターフェイスのソース コードを参照してください。

メソッド 説明
CreateBatch 単一ユニットとしてサーバーに送信されるが、必ずしもユニットとして処理されるわけではない "操作のグループ" を作成します。
CreateTransaction 単一ユニットとしてサーバーに送信され、なおかつサーバー上で単一ユニットとして処理される操作のグループを作成します。
KeyDelete キーと値を削除します。
KeyExists 特定のキーがキャッシュに存在するかどうかを返します。
KeyExpire キーの Time to Live (TTL) の有効期限を設定します。
KeyRename キーの名前を変更します。
KeyTimeToLive キーの TTL を返します。
KeyType キーに格納されている値の型の文字列表現を返します。 返される型は、文字列、list、set、zset、hash です。

その他のコマンドを実行する

IDatabase オブジェクトには Execute メソッドと ExecuteAsync メソッドがあり、テキスト コマンドを Redis サーバーに渡すときに使用できます。 次に例を示します。

var result = db.Execute("ping");
Console.WriteLine(result.ToString()); // displays: "PONG"

Execute メソッドと ExecuteAsync メソッドが返す RedisResult オブジェクトは、次の 2 つのプロパティを含むデータ ホルダーです。

  • Type: 結果の型 ("STRING"、"INTEGER" など) を示す string を返します。
  • IsNull: 結果が null かどうかを検出する true/false 値。

RedisResultToString() を使用して、実際の戻り値を取得できます。

Execute を使って、サポートされているすべてのコマンドを実行できます。 たとえば、次のように、キャッシュに接続されているすべてのクライアントを取得できます ("CLIENT LIST")。

var result = await db.ExecuteAsync("client", "list");
Console.WriteLine($"Type = {result.Type}\r\nResult = {result}");

上記のコマンドは、接続されているすべてのクライアントを出力します。

Type = BulkString
Result = id=9469 addr=16.183.122.154:54961 fd=18 name=DESKTOP-AAAAAA age=0 idle=0 flags=N db=0 sub=1 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=subscribe numops=5
id=9470 addr=16.183.122.155:54967 fd=13 name=DESKTOP-BBBBBB age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=client numops=17

より複雑な値を格納する

Redis はバイナリ セーフな文字列を中心としていますが、オブジェクト グラフをテキスト形式 (通常は XML または JSON) にシリアル化してキャッシュすることもできます。 たとえば、演習で使用する統計では、次のような GameStat オブジェクトがあります。

public class GameStat
{
    public string Id { get; set; }
    public string Sport { get; set; }
    public DateTimeOffset DatePlayed { get; set; }
    public string Game { get; set; }
    public IReadOnlyList<string> Teams { get; set; }
    public IReadOnlyList<(string team, int score)> Results { get; set; }

    public GameStat(string sport, DateTimeOffset datePlayed, string game, string[] teams, IEnumerable<(string team, int score)> results)
    {
        Id = Guid.NewGuid().ToString();
        Sport = sport;
        DatePlayed = datePlayed;
        Game = game;
        Teams = teams.ToList();
        Results = results.ToList();
    }

    public override string ToString()
    {
        return $"{Sport} {Game} played on {DatePlayed.Date.ToShortDateString()} - " +
               $"{String.Join(',', Teams)}\r\n\t" + 
               $"{String.Join('\t', Results.Select(r => $"{r.team } - {r.score}\r\n"))}";
    }
}

Newtonsoft.Json ライブラリを使用すると、このオブジェクトのインスタンスを文字列に変えることができます。

var stat = new GameStat("Soccer", new DateTime(1950, 7, 16), "FIFA World Cup", 
                new[] { "Uruguay", "Brazil" },
                new[] { ("Uruguay", 2), ("Brazil", 1) });

string serializedValue = Newtonsoft.Json.JsonConvert.SerializeObject(stat);
bool added = db.StringSet("event:1950-world-cup", serializedValue);

この文字列を取得し、逆のプロセスを使用してオブジェクトに戻すことができます。

var result = db.StringGet("event:1950-world-cup");
var stat = Newtonsoft.Json.JsonConvert.DeserializeObject<GameStat>(result.ToString());
Console.WriteLine(stat.Sport); // displays "Soccer"

接続をクリーンアップする

Redis 接続が完了したら、ConnectionMultiplexer破棄 (Dispose) できます。 このコマンドは、すべての接続を閉じて、サーバーとの通信をシャットダウンします。

redisConnection.Dispose();
redisConnection = null;

アプリケーションを作成し、Redis Cache を使った作業を実行してみましょう。

既に説明したように、Redis は複数のサーバー全体にレプリケートできるインメモリ NoSQL データベースです。 多くの場合、キャッシュとして使用されますが、正式なデータベースまたはメッセージ ブローカーとして使用することもできます。

Redis は、さまざまなデータ型と構造体を格納できます。 サポートされているさまざまなコマンドを発行して、キャッシュ データを取得したり、キャッシュ自体に関する情報を照会したりできます。 使用するデータは、常にキーと値のペアとして格納されます。

Redis Cache でコマンドを実行する

通常、クライアント アプリケーションは "クライアント ライブラリ" を使用して要求を作成し、Redis Cache でコマンドを実行します。 クライアント ライブラリの一覧は、Redis クライアント ページから直接取得できます。 JavaScript 用の一般的な Redis クライアントは redis パッケージであり、これは npm install redis を使ってプロジェクトに追加できます。

redis パッケージを使用した Redis Cache への接続

Redis Cache との対話は、RedisClient クラスを使って行われます。 Azure の Redis Cache に接続する RedisClient を作成するには、ほとんどの場合、次の定型コードを使用することをお勧めします。

const client = redis.createClient(
  port,  // the port number, 6380 by default
  hostname,  // <resourceName>.redis.cache.windows.net
  {
    password: accessKey,  // the primary or secondary access key
    tls: { servername: hostname }
  }
);

なるべく、複数の RedisClient は作成しないようにしてください。 RedisClient の 1 つのインスタンスを順番に使用して、Redis が必要なコード内のすべての場所で使用することができます。

Redis データベースの操作

Redis コマンドは RedisClient のメソッドとして、そのコマンド自体の名前で表現されます。 新しい値をキャッシュに格納する例を次に示します。

client.set("myKey", "myValue"); // executes "set myKey myValue" on the cache

RedisClient のコマンド メソッドはすべて非同期であり、結果を提供するオプションのコールバック引数をサポートしています。 redis パッケージは Promise (すなわち、async/await.then() によるチェーン) を最初からサポートしていません。 RedisClientasync/await または .then() を使う最も簡単な方法は、bluebird パッケージの promisifyAll 関数を使って Promise のサポートをクライアント全体に一度に追加することです。

var redis = require("redis");
var Promise = require("bluebird");

Promise.promsifyAll(redis);

promisifyAll 関数は、すべてのコマンド メソッドの XXXAsync バージョンを RedisClient インスタンスに追加し、次の例のように非同期メソッドを使用できるようにします。

var result = await client.setAsync("myKey", "myValue");

コマンドを動的に実行する

sendCommand() (bluebird では sendCommandAsync()) を使ってコマンドを動的に送信し、任意の文字列をコマンドとしてキャッシュに送信できます。 たとえば、アプリでコマンドをキャッシュに直接送信するためのプロンプトを表示したり、redis パッケージでサポートされていない新しいコマンドを Redis で導入したりできます。 コマンド引数は配列として送信する必要があります。

// Add a key/value pair
var result = await client.sendCommandAsync("set", ["myKey", "myValue"]);

接続をクリーンアップする

Redis の接続が完了したら、quit() で終了する必要があります (bluebird を使用する場合は quitAsync())。

await client.quitAsync();

アプリケーションを作成し、Redis Cache を使った作業を実行してみましょう。