Redisの発行/購読メカニズム
4905 ワード
関連コマンド:
これらのコマンドは、ネットワークチャットルームやリアルタイムブロードキャスト、リアルタイムアラートなどのインスタント通信アプリケーションの構築に広く使用されている.
Redis関連ソースファイル:pubsub.c
使用
PUBLISH
SUBSCRIBE
PSUBSCRIBE
UNSUBSCRIBE
PUNSUBSCRIBE
これらのコマンドは、ネットワークチャットルームやリアルタイムブロードキャスト、リアルタイムアラートなどのインスタント通信アプリケーションの構築に広く使用されている.
Redis関連ソースファイル:pubsub.c
使用
PUBLISHコマンドは、所定のチャネルに情報を送信するために使用され、情報を受信したサブスクライバの数を返す。
redis> PUBLISH treehole "top secret here ..."
(integer) 0
redis> PUBLISH chatroom "hi?"
(integer) 1
SUBSCRIBEコマンドは、指定された1つ以上のチャンネルを購読します。
redis> SUBSCRIBE chatroom
Reading messages... (press Ctrl-C to quit)
1) "subscribe" #
2) "chatroom" #
3) (integer) 1 # /
1) "message" #
2) "chatroom" #
3) "hi?" #
SUBSCRIBEの戻り値のうち,1)subscribe"は購読のフィードバック情報,1)messageは購読のチャンネルから送信される情報である.
SUBSCRIBEは、複数のチャンネルを購読することもでき、これにより、受信した情報が複数のチャンネルから受信される可能性があります.redis> SUBSCRIBE chatroom talk-to-jack
Reading messages... (press Ctrl-C to quit)
1) "subscribe" # chatroom
2) "chatroom"
3) (integer) 1
1) "subscribe" # talk-to-jack
2) "talk-to-jack"
3) (integer) 2
1) "message" # chatroom
2) "chatroom"
3) "yahoo"
1) "message" # talk-to-peter
2) "talk-to-jack"
3) "Goodmorning, peter."
PSUBScriBEは、指定されたモードに適合するすべてのチャネルを購読する方法を提供します。例えば、it.*を使用します。入力のためにitですべて購読できます。冒頭のチャンネル、例えばit.news 、 it.blog 、 it.tweetsなど:
redis> PSUBSCRIBE it.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "it.*"
3) (integer) 1
1) "pmessage"
2) "it.*" #
3) "it.news" #
4) "Redis 2.6rc5 release" #
1) "pmessage"
2) "it.*"
3) "it.blog"
4) "Why NoSQL matters"
1) "pmessage"
2) "it.*"
3) "it.tweet"
4) "@redis: when will the 2.6 stable release?"
もちろん、PSUBScriBEは、複数のパラメータを受け入れ、複数のモードに一致させることもできる.
UNSUBSCRIBEおよびPUNSUBSCRRIBEは、所定のチャネルまたはモードのキャンセルを担当します。
内部実装
プロセス
あるクライアントがPUBLISHコマンドを介して購読者に情報を送信する場合、このクライアントをパブリッシャと呼ぶ.
一方、あるクライアントがSUBSCRIBEまたはPSUBSCRRIBEコマンドを使用して情報を受信する場合、このクライアントをサブスクライバ(subscriber)と呼ぶ.
公開者(publisher)とサブスクライバ(subscriber)の関係を解くために、Redisはchannel(チャンネル)を両者の仲介として使用した.公開者は直接channelに情報を公開し、channelは適切なサブスクライバに情報を送信し、公開者とサブスクライバの間には相互関係がなく、相手の存在も知らない.
具体的な実装
SUBSCRIBEコマンドの実装
Redisは、情報を受信および送信するすべてのタスクをchannelに渡し、すべてのchannelの情報はredisServerという構造に格納されます.struct redisServer {
// ......
dict *pubsub_channels; // Map channels to list of subscribed clients
// ......
};
pubsub_channelsは辞書で、辞書のキーはchannelであり、辞書の値はチェーンテーブルであり、チェーンテーブルにはこのchannelを購読するクライアントがすべて保存されています.(haspmapとか)
SUBSCRIBEコマンドを実装する鍵は、クライアントを特定のchannelのサブスクリプションスプレッドシートに追加することです.
関数pubsubSubscribeChannelは、サブスクリプションスプレッドにクライアントを追加する作業を完了するSUBSCRIBEコマンドの最下位の実装です. //
// 1 , , 0
int pubsubSubscribeChannel(redisClient *c, robj *channel) {
struct dictEntry *de;
list *clients = NULL;
int retval = 0;
/* Add the channel to the client -> channels hash table */
// dictAdd DICT_OK
// , channel , 。。。
if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) {
retval = 1;
incrRefCount(channel);
/* Add the client to the channel -> list of clients hash table */
// client channel
// , channel
// server.pubsub_channels
de = dictFind(server.pubsub_channels,channel);
if (de == NULL) {
// de NULL
// channel
// ,
clients = listCreate();
dictAdd(server.pubsub_channels,channel,clients);
incrRefCount(channel);
} else {
// de , clients
clients = dictGetVal(de);
}
//
listAddNodeTail(clients,c);
}
/* Notify the client */
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.subscribebulk);
//
addReplyBulk(c,channel);
//
addReplyLongLong(c,dictSize(c->pubsub_channels)+listLength(c->pubsub_patterns));
return retval;
}
PSUBScriBEコマンドの実装
およびredisServer.pubsub_チャンネルのプロパティは似ています.redisServer.pubsub_patternsプロパティは、サブスクリプションされたすべてのモードとpubsub_を保存するために使用されます.チャンネルが違うのはpubsub_patternsは辞書ではなくチェーンテーブルです.struct redisServer {
// ......
list *pubsub_patterns; // A list of pubsub_patterns
// ......
};
redis> PUBLISH treehole "top secret here ..."
(integer) 0
redis> PUBLISH chatroom "hi?"
(integer) 1
redis> SUBSCRIBE chatroom
Reading messages... (press Ctrl-C to quit)
1) "subscribe" #
2) "chatroom" #
3) (integer) 1 # /
1) "message" #
2) "chatroom" #
3) "hi?" #
redis> SUBSCRIBE chatroom talk-to-jack
Reading messages... (press Ctrl-C to quit)
1) "subscribe" # chatroom
2) "chatroom"
3) (integer) 1
1) "subscribe" # talk-to-jack
2) "talk-to-jack"
3) (integer) 2
1) "message" # chatroom
2) "chatroom"
3) "yahoo"
1) "message" # talk-to-peter
2) "talk-to-jack"
3) "Goodmorning, peter."
redis> PSUBSCRIBE it.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "it.*"
3) (integer) 1
1) "pmessage"
2) "it.*" #
3) "it.news" #
4) "Redis 2.6rc5 release" #
1) "pmessage"
2) "it.*"
3) "it.blog"
4) "Why NoSQL matters"
1) "pmessage"
2) "it.*"
3) "it.tweet"
4) "@redis: when will the 2.6 stable release?"
プロセス
あるクライアントがPUBLISHコマンドを介して購読者に情報を送信する場合、このクライアントをパブリッシャと呼ぶ.
一方、あるクライアントがSUBSCRIBEまたはPSUBSCRRIBEコマンドを使用して情報を受信する場合、このクライアントをサブスクライバ(subscriber)と呼ぶ.
公開者(publisher)とサブスクライバ(subscriber)の関係を解くために、Redisはchannel(チャンネル)を両者の仲介として使用した.公開者は直接channelに情報を公開し、channelは適切なサブスクライバに情報を送信し、公開者とサブスクライバの間には相互関係がなく、相手の存在も知らない.
具体的な実装
SUBSCRIBEコマンドの実装
Redisは、情報を受信および送信するすべてのタスクをchannelに渡し、すべてのchannelの情報はredisServerという構造に格納されます.
struct redisServer {
// ......
dict *pubsub_channels; // Map channels to list of subscribed clients
// ......
};
pubsub_channelsは辞書で、辞書のキーはchannelであり、辞書の値はチェーンテーブルであり、チェーンテーブルにはこのchannelを購読するクライアントがすべて保存されています.(haspmapとか)
SUBSCRIBEコマンドを実装する鍵は、クライアントを特定のchannelのサブスクリプションスプレッドシートに追加することです.
関数pubsubSubscribeChannelは、サブスクリプションスプレッドにクライアントを追加する作業を完了するSUBSCRIBEコマンドの最下位の実装です.
//
// 1 , , 0
int pubsubSubscribeChannel(redisClient *c, robj *channel) {
struct dictEntry *de;
list *clients = NULL;
int retval = 0;
/* Add the channel to the client -> channels hash table */
// dictAdd DICT_OK
// , channel , 。。。
if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) {
retval = 1;
incrRefCount(channel);
/* Add the client to the channel -> list of clients hash table */
// client channel
// , channel
// server.pubsub_channels
de = dictFind(server.pubsub_channels,channel);
if (de == NULL) {
// de NULL
// channel
// ,
clients = listCreate();
dictAdd(server.pubsub_channels,channel,clients);
incrRefCount(channel);
} else {
// de , clients
clients = dictGetVal(de);
}
//
listAddNodeTail(clients,c);
}
/* Notify the client */
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.subscribebulk);
//
addReplyBulk(c,channel);
//
addReplyLongLong(c,dictSize(c->pubsub_channels)+listLength(c->pubsub_patterns));
return retval;
}
PSUBScriBEコマンドの実装
およびredisServer.pubsub_チャンネルのプロパティは似ています.redisServer.pubsub_patternsプロパティは、サブスクリプションされたすべてのモードとpubsub_を保存するために使用されます.チャンネルが違うのはpubsub_patternsは辞書ではなくチェーンテーブルです.
struct redisServer {
// ......
list *pubsub_patterns; // A list of pubsub_patterns
// ......
};