Pub/Sub

SUBSCRIBE, UNSUBSCRIBE, および PUBLISH は、 Publish/Subscribe messaging paradigm を実装しています。送信者(パブリッシャー)はメッセージを特定の受信者(サブスクライバ)へ向けて送信するようにはプログラムされていません。代わりに、特定のチャネルにメッセージを配信します。(存在するとしたら)どのようなサブスクライバが存在するのかという知識はもっていません。サブスクライバは、関心のある一つもしくは複数のチャネルを購読し、そのチャネルからのみメッセージを受信します。(存在するとしたら)どのようなパブリッシャーが存在するのかという知識はもっていません。このような、パブリッシャーとサブスクライバの疎な関係は、スケーラビリティとダイナミックなネットワークトポロジーを可能にします。

たとえば、’foo’, ‘bar’ という 2 つのチャネル購読するには、クライアントはチャネル名を指定して SUBSCRIBE コマンドを発行します:

SUBSCRIBE foo bar

これらのチャネルに他のクライアントが送信したメッセージは、Redis により、購読しているすべてのクライアントへプッシュ配信されます。

一つ、もしくは複数のチャネルを購読しているサブスクライバは、コマンドを発行するべきではありません(とはいえ、他のチャネルを購読したり購読停止したりすることはできます)。 SUBSCRIBE, UNSUBSCRIBE 操作の戻り値は、メッセージ形式で返却され、クライアントは一貫したメッセージのストリームを読み取ることができるようになります。なお最初の要素はメッセージのタイプを示します。

配信メッセージのフォーマット

ひとつのメッセージは、3 つの要素からなる Array reply です。

最初の要素は、メッセージの種類です:

  • ‘subscribe’ : チャネルの購読に成功したことを意味します。チャネル名は応答の 2 つめの要素で与えられます。3 つめの要素は現在購読中のチャネル数を表します。

  • ‘unsubscribe’: チャネルの購読停止に成功したことを意味します。チャネル名は応答の 2 つめの要素で与えられます。3 つめの要素は現在購読中のチャネル数を表します。最後の要素が 0 なら、もういずれのチャネルも購読しておらず、Pub/Sub 状態を抜けているため、クライアントは任意の Redis コマンドを発行できます。

  • ‘message’: 他のクライアントが発行した PUBLISH コマンドにより生成されたメッセージを受信したことを意味します。2 つめの要素は受信元のチャネル名、3 つめの要素は実際のメッセージペイロードです。

データベースとスコープ

Pub/Sub はいずれのキースペースとも関係していません。データベース番号を含むどのレベルとも干渉しません。

DB 10 で発行されたメッセージは、DB 1 のサブスクライバが購読できます。

もし何らかのスコープが必要なら、チャネル名に環境に応じた prefix (test, staging, production, ...) を付与してください。

ワイヤープロトコル例

SUBSCRIBE first second
*3
$9
subscribe
$5
first
:1
*3
$9
subscribe
$6
second
:2

ここで、他のクライアントから PUBLISH 操作を ‘second’ チャネルに対して発行します:

> PUBLISH second Hello

すると最初のクライアントは、以下のデータを受信します:

*3
$7
message
$6
second
$5
Hello

ここでクライアントが、 UNSCRIBE コマンドを引数なしで発行し、すべてのチャネルの購読を停止します:

UNSUBSCRIBE
*3
$11
unsubscribe
$6
second
:1
*3
$11
unsubscribe
$5
first
:0

パターンマッチによる購読

Redis Pub/Sub の実装はパターンマッチをサポートします。クライアントは、glob スタイル のパターンを購読することで、指定したパターンにマッチするすべてのチャネルに送信されたメッセージをを受信できます。

たとえば:

PSUBSCRIBE news.*

この場合、’news.arg.figurative’, ‘news.music.jazz’, といったチャネルに送信されたすべてのメッセージを受信します。どのような glob スタイルのパターンも許可されるため、複数のワイルドカードもサポートされます。

PUNSUBSCRIBE news.*

この場合、クライアントはこのパターンにマッチするチャネルの購読を停止します。その他の購読には影響を与えません。

パターンマッチにより受信されるメッセージは、異なるフォーマットをもちます:

  • メッセージタイプ ‘pmessage’: 他のクライアントが発行した PUBLISH コマンドにより生成されたメッセージを受信したことを意味します。2 つめの要素はマッチしたパターン、最後の要素は実際のメッセージペイロードです。

SUBSCRIBE, UNSUBSCRIBE と同様に、 PSUBSCRIBE, PUNSUBSCRIBE コマンドがシステムにより確認されると、’subscribe’, ‘unsubscribe’ と同じフォーマット’psubscribe’ および ‘punsubscribe’ メッセージタイプが送信されます。

パターンとチャネルの両方にマッチするメッセージについて

クライアントが、複数のパターンを購読していたり、パターンとチャネルの両方を購読している場合、ひとつのメッセージを複数回受信する可能性があります。たとえば以下の例のように:

SUBSCRIBE foo
PSUBSCRIBE f*

上記の例では、’foo’ チャネルにメッセージが送信されると、クライアントは 2 つのメッセージを受信します: ひとつは ‘message’ タイプ、もうひとつは ‘pmessage’ タイプです。

パターンマッチにおける購読数の意味

‘subscribe’, ‘unsubscribe’, ‘psubscribe’, および ‘psubscribe’ メッセージタイプにおいて、最後の要素はアクティブな購読数を表します。この数は、実際には、クライアントがまだ受信を続けているチャネルとパターンの総数です。クライアントは、すべてのチャネルとパターンの購読が停止され、この数が 0 になった時にかぎり、Pub/Sub 状態から抜けます。

プログラミング例

Pieter Noordhuis は EventMachine と Redis を使って、素晴らしい 高性能なマルチユーザーWebチャット の例を公開しています。

クライアントライブラリ実装のヒント

受信されるすべてのメッセージは、メッセージデリバリーの元になった購読情報(‘message’タイプの場合はチャネル名、’pmessage’タイプの場合はパターン)を含んでいるため、クライアントライブラリは、ハッシュテーブルを使って元の購読をコールバック(無名関数、ブロック、関数ポインタ、など)にバインドすることができます。

あるメッセージが受信されたとき、そのメッセージをあらかじめ登録されたコールバックに届けるために、O(1) の計算量でルックアップができるでしょう。