Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

Redis Pub/Sub ハウツーガイド:チャンネルからパターンマッチングまで徹底解説

RedisのPub/Subに関する概要記事では、Pub/Subの目的と、Redis Pub/Sub特有の設計上の選択について解説しました。本記事では、Node.jsクライアント「node-redis」を使用しながら、Redis Pub/Subの主要な概念——チャンネルパブリッシュサブスクライブパターンマッチング——を一つずつ実際に試すことで、Redis Pub/Subの具体的な使い方をステップごとに学んでいきます。

チャンネルの仕組みを理解する

チャンネル(channel)とは、Pub/Subシステム上で公開されるメッセージを分類するための「名前」です。チャンネル名には system-health:i-36a44b83trade-prices:RAXtemp-reading:living-room のようなシステム固有の命名もあれば、events のような汎用的な名前もあります。あるチャンネルに流れるデータに関心を持つサブスクライバーは誰でもそのチャンネルを購読でき、システムが拡張されても新しいパブリッシャーやサブスクライバーを容易に追加できます。

Redisサーバー上で現在アクティブなチャンネルを確認するには、PUBSUB CHANNELS コマンドを使用します。Pub/Subがまだ利用されていない状態では、何も返されません。

$ redis-cli pubsub channels
(empty list or set)

重要なポイントとして、チャンネルはサブスクライバーがそのメッセージを購読している間しか存在しません。そのため、チャンネルを明示的に「作成」したり「削除」したりする必要は一切ありません。この挙動を確認するため、片方のコンソールウィンドウで redis-cli をサブスクライバーとして起動し、もう片方のウィンドウで再び PUBSUB CHANNELS を実行してみましょう。

コンソール 1:

$ redis-cli subscribe events
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "events"
3) (integer) 1

コンソール 2:

$ redis-cli pubsub channels
1) "events"

コンソール1の接続が切断されると同時に、システム上には再びチャンネルが存在しなくなります。

メッセージのパブリッシュ(送信)

コマンド構文

メッセージの公開には PUBLISH コマンドを使用します。

PUBLISH channel message

このコマンドを実行すると、そのチャンネルを購読中のすべてのサブスクライバーへメッセージが配信されます。逆に言えば、受信者が一人もいなければ、そのメッセージは単純に破棄されます。

Node.jsでの実装例

通常のRedis接続を作成すれば、PUBLISH は他のコマンドと同じように使用できます。

var client = require("redis").createClient();
client.publish("temp-reading:living-room", "37.0");

他のRedisコマンドとの組み合わせ

チャンネルへのデータ送信は非常に高速な処理のため、他の操作と組み合わせて使われることが一般的です。このように複数の操作を連携させることで、Redis本来の力が発揮されます。

var client = require("redis").createClient();

client.multi()
  .publish("temp-reading:living-room", "37.0")
  .lpush("recent-temperatures", "37.0")
  .ltrim("recent-temperatures", 0, 99)
  .exec();

この例は、先ほどのPub/Subへの送信と同じ操作ですが、同一の MULTI/EXEC トランザクション内で、直近100件の温度を保持するリストにも値を追加しています。

この短いサンプルは、メッセージ履歴を含むより大規模なシステム設計にRedis Pub/Subを統合する一つの手法を示しています。このような操作により、他のクライアントは新着メッセージの購読だけでなく、過去の値への問い合わせも可能になります。

メッセージのサブスクライブ(受信)

コマンド構文

チャンネルの購読には SUBSCRIBE コマンドを使用します。このコマンドを実行すると、クライアントは特別な「購読中(subscribed)」状態に入り、追加の SUBSCRIBEUNSUBSCRIBE 以外のコマンドを送信できなくなります。

SUBSCRIBE channel [channel ...]

Node.jsでの実装例

以下のサブスクライバーは、先ほどのパブリッシュの例と組み合わせて動作します。

var subscriber = require("redis").createClient();

subscriber.on("message", function(channel, message) {
  console.log("A temperature of " + message + " was read.");
});

subscriber.subscribe("temp-reading:living-room");

ここではシンプルなイベントエミッタを使い、メッセージが到着するたびに関数が呼び出されるようにしています。ハンドラ関数は現時点では温度をコンソールに出力しているだけですが、ここで任意の処理を行うことが可能です。

購読中のクライアントで他のコマンドを使うには

前述のとおり、購読中のクライアントは特殊なモードになっており、他のコマンドには使用できません。別のコマンドを使いたい場合は、Redisへの接続をもう一つ作成しましょう。

var redis = require("redis")
  , subscriber = redis.createClient()
  , client = redis.createClient();

subscriber.on("message", function(channel, message) {
  console.log("A temperature of " + message + " was read.");
  client.incr("temp-count");
});

subscriber.subscribe("temp-reading:living-room");

このハンドラはメッセージをコンソールに出力するとともに、Redis上のカウンターをインクリメントします。場合によっては、計算結果を別のチャンネルにパブリッシュして他のサブスクライバーに通知することもあるでしょう。ただし、自分が購読している同じチャンネルへパブリッシュし返すと無限ループが発生するので注意してください :)

パターンマッチング

コマンド構文

チャンネル名をパターンでマッチさせるには、PSUBSCRIBE コマンドを使用します。

PSUBSCRIBE pattern [pattern ...]

このコマンドは通常の SUBSCRIBE とほぼ同じですが、パターンに一致する名前のチャンネルをまとめて購読できる点が異なります。これにより、パブリッシャー側は非常に細かい粒度で情報を発信でき、サブスクライバー側は正確なチャンネル名を知らなくても多数のチャンネルを購読できます。

サポートされているパターンはシンプルです。* は任意の文字列に、? は任意の1文字に一致し、角括弧(例:[acd])は括弧内のいずれか1文字に一致します。

複数の部屋からの温度データを取得する例:

PSUBSCRIBE temp-reading:*

site-link:logo:a:clickratesite-link:logo:b:clickrate のようなA/Bテストのイベントを取得する例:

PSUBSCRIBE site-link:logo:?:clickrate

system-health:us-east-1a:i-36a44b83system-health:us-east-1c:i-73657420636f636f6 のようなAWSインスタンス群にまたがるイベントを取得する例:

PSUBSCRIBE system-health:us-east-1[acd]:i-*

Node.jsでの実装例

次の例も、先ほどのパブリッシュの例と組み合わせられます。温度とともに、その値が測定された部屋名を出力します。

var subscriber = require("redis").createClient();

subscriber.on("pmessage", function(pattern, channel, message) {
  var room = channel.split(":")[1];
  console.log("A temperature of " + message + " was read in " + room);
});

subscriber.psubscribe("temp-reading:*");

パターンベースの購読では、コールバックに渡される情報が少し増えます。チャンネル名とメッセージに加え、マッチしたパターンそのものも受け取ります。これは、サブスクライバーが複数のチャンネルやパターンを同時に購読できるためです。

補足:PUBLISH のパフォーマンス特性

Redisのすべてのコマンドには計算量(タイムコンプレキシティ)が公式ドキュメントに記載されており、多くの場合それは直感的に理解できるものです。PUBLISH 操作は非常にシンプル(ほとんど SET のような感覚)なので、計算量もO(1)だろうと想像しがちです。しかし実際には、PUBLISH の計算量はサブスクライバーの動向に応じて線形に増加します。

PUBLISH コマンドが実行されると、対象チャンネルにマッチしうるすべての購読パターンと、メッセージを受け取るべきすべてのサブスクライバーを順にたどる必要があります。その結果、時間計算量はO(N+M)となります。

ほとんどの運用環境では、この計算量によってパフォーマンス問題が発生することはありません。それでも、このコマンドのパフォーマンスを継続的に監視しておくのは賢明です。特に、コードが自動的に大量の購読パターンを生成するようなケースには注意が必要です。

あなたのコードも紹介しませんか?

Redis Pub/Subを使って構築された優れたアプリケーションの事例もぜひ紹介したいと考えています。良い事例をご存知の方は、support@redisgreen.net までお知らせください。いただいたコードの中から、積極的にご紹介していきます。

  1. JedisライブラリでRedisのPub/Subシステムを実装する方法【Javaサンプルコード付き】

    このチュートリアルでは、Java向けクライアントライブラリ「Jedis」を使用して、RedisのPub/Sub(パブリッシュ/サブスクライブ)システムを実装する方法を解説します。 Jedisライブラリとは Jedisは、Redisデータストア用のJavaクライアントライブラリです。軽量で非常に扱いやすく、Redis 2.8.x、3.x.x以降のバージョンと完全な互換性があります。シンプルなAPI设计で、Redisの各種コマンドを直感的に操作できるのが特徴です。 RedisのPub/Subシステムとは Redisは、Publish/Subscribe(パブリッシュ/サブスクライブ)型のメッセージ

  2. 【2022年版】Rokuでチャンネルのブロックを解除する方法を徹底解説

    Rokuは、テレビでストレスなくコンテンツを楽しめる人気のストリーミングデバイスです。従来のテレビをスマートTVへと変身させる高性能ストリーミングスティックであり、仕様の異なるさまざまなモデルが展開されており、多数のチャンネルを追加してオンデマンドコンテンツを視聴できます。しかし、その多くは地理的制限(ジオブロック)がかかっており、サブスクリプションに加入していても、国外からはアクセスできない場合があります。 あわせて読みたい記事: 2022年版:Rokuデバイスに最適なブラウザ6選 iPhoneをRoku TVに接続する方法 幸い、Rokuのチャンネル規制を解除する手間のかからない方法が