pubsub.c
Redis的pubsub.c
是对pubsub
功能的实现。
Redis的pub/sub
是一个稳定快速的消息系统。
基于 ziplist 和 dict 实现。
主要包含了消息的发布、订阅、取消订阅功能。
在server.h
有如下属性:
/* Pubsub */
dict *pubsub_channels; /* Map channels to list of subscribed clients */
list *pubsub_patterns; /* A list of pubsub_patterns */
基于这两个属性,
client
用pubsub_channels
来保存client
和channel列表
之间的关系。
server
用pubsub_channels
来保存channel
和client列表
之间的关系。
本质上,pub/sub
就是在操作这几个属性的值。
1. 消息发布
//publish命令 publish channel message
void publishCommand(client *c) {
int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]);
if (server.cluster_enabled)
clusterPropagatePublish(c->argv[1],c->argv[2]);
else
forceCommandPropagation(c,PROPAGATE_REPL);
addReplyLongLong(c,receivers);
}
/* Publish a message */
//发布消息到指定channel
int pubsubPublishMessage(robj *channel, robj *message) {
int receivers = 0;
dictEntry *de;
listNode *ln;
listIter li;
/* Send to clients listening for that channel */
//查找订阅了channel的clients
de = dictFind(server.pubsub_channels,channel);
if (de) {
list *list = dictGetVal(de);
listNode *ln;
listIter li;
listRewind(list,&li);
//遍历所有client
while ((ln = listNext(&li)) != NULL) {
client *c = ln->value;
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.messagebulk);
addReplyBulk(c,channel);
addReplyBulk(c,message);
receivers++;
}
}
/* Send to clients listening to matching channels */
//查找匹配pattern的channel对应的clients
if (listLength(server.pubsub_patterns)) {
listRewind(server.pubsub_patterns,&li);
channel = getDecodedObject(channel);
//遍历所有client
while ((ln = listNext(&li)) != NULL) {
pubsubPattern *pat = ln->value;
if (stringmatchlen((char*)pat->pattern->ptr,
sdslen(pat->pattern->ptr),
(char*)channel->ptr,
sdslen(channel->ptr),0)) {
addReply(pat->client,shared.mbulkhdr[4]);
addReply(pat->client,shared.pmessagebulk);
addReplyBulk(pat->client,pat->pattern);
addReplyBulk(pat->client,channel);
addReplyBulk(pat->client,message);
receivers++;
}
}
decrRefCount(channel);
}
return receivers;
}
2. 消息订阅
//subscribe命令 subscribe channel [channel...]
void subscribeCommand(client *c) {
int j;
//获取所有的channel,然后执行订阅操作
for (j = 1; j < c->argc; j++)
pubsubSubscribeChannel(c,c->argv[j]);
c->flags |= CLIENT_PUBSUB;
}
/* Subscribe a client to a channel. Returns 1 if the operation succeeded, or
* 0 if the client was already subscribed to that channel. */
//client订阅channel
int pubsubSubscribeChannel(client *c, robj *channel) {
dictEntry *de;
//一个channel可以被多个client订阅
//一个client可以订阅多个channel
list *clients = NULL;
int retval = 0;
/* Add the channel to the client -> channels hash table */
//c->pubsub_channels,保存的是一个client订阅的channel列表
//将该channel加入到client的pubsub_channels中
if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) {
retval = 1;
incrRefCount(channel);
//通过server.pubsub_channels保存一个channel被订阅的client列表
//查找该chanel的client列表
de = dictFind(server.pubsub_channels,channel);
if (de == NULL) {
clients = listCreate();
dictAdd(server.pubsub_channels,channel,clients);
incrRefCount(channel);
} else {
clients = dictGetVal(de);
}
//将client加入到channel的pubsub_channels中
listAddNodeTail(clients,c);
}
/* Notify the client */
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.subscribebulk);
addReplyBulk(c,channel);
addReplyLongLong(c,clientSubscriptionsCount(c));
return retval;
}
3. 取消订阅
//ubsubscribe命令 unsubscribe channel [channel...]
void unsubscribeCommand(client *c) {
//取消订阅所有channel
if (c->argc == 1) {
pubsubUnsubscribeAllChannels(c,1);
} else {
int j;
//取消订阅指定channel
for (j = 1; j < c->argc; j++)
pubsubUnsubscribeChannel(c,c->argv[j],1);
}
if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;
}
/* Unsubscribe a client from a channel. Returns 1 if the operation succeeded, or
* 0 if the client was not subscribed to the specified channel. */
//取消订阅channel
int pubsubUnsubscribeChannel(client *c, robj *channel, int notify) {
dictEntry *de;
list *clients;
listNode *ln;
int retval = 0;
/* Remove the channel from the client -> channels hash table */
incrRefCount(channel); /* channel may be just a pointer to the same object
we have in the hash tables. Protect it... */
//从client的pubsub_channels中删除channel
if (dictDelete(c->pubsub_channels,channel) == DICT_OK) {
retval = 1;
/* Remove the client from the channel -> clients list hash table */
de = dictFind(server.pubsub_channels,channel);
serverAssertWithInfo(c,NULL,de != NULL);
clients = dictGetVal(de);
ln = listSearchKey(clients,c);
serverAssertWithInfo(c,NULL,ln != NULL);
//从channel的pubsub_channels中删除client
listDelNode(clients,ln);
//如果channel的订阅者为空,则移除对象
if (listLength(clients) == 0) {
/* Free the list and associated hash entry at all if this was
* the latest client, so that it will be possible to abuse
* Redis PUBSUB creating millions of channels. */
dictDelete(server.pubsub_channels,channel);
}
}
/* Notify the client */
if (notify) {
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.unsubscribebulk);
addReplyBulk(c,channel);
addReplyLongLong(c,dictSize(c->pubsub_channels)+
listLength(c->pubsub_patterns));
}
decrRefCount(channel); /* it is finally safe to release it */
return retval;
}
/* Unsubscribe from all the channels. Return the number of channels the
* client was subscribed to. */
//取消订阅所有的channel
int pubsubUnsubscribeAllChannels(client *c, int notify) {
dictIterator *di = dictGetSafeIterator(c->pubsub_channels);
dictEntry *de;
int count = 0;
//遍历client对应的pubsub_channels dict
while((de = dictNext(di)) != NULL) {
robj *channel = dictGetKey(de);
//取消订阅
count += pubsubUnsubscribeChannel(c,channel,notify);
}
/* We were subscribed to nothing? Still reply to the client. */
if (notify && count == 0) {
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.unsubscribebulk);
addReply(c,shared.nullbulk);
addReplyLongLong(c,dictSize(c->pubsub_channels)+
listLength(c->pubsub_patterns));
}
dictReleaseIterator(di);
return count;
}
4. 基于正则匹配订阅
//psubscribe命令 psubscribe pattern [pattern...]
void psubscribeCommand(client *c) {
int j;
//正则匹配订阅channel
for (j = 1; j < c->argc; j++)
pubsubSubscribePattern(c,c->argv[j]);
c->flags |= CLIENT_PUBSUB;
}
/* Subscribe a client to a pattern. Returns 1 if the operation succeeded, or 0 if the client was already subscribed to that pattern. */
//正则匹配订阅channel
int pubsubSubscribePattern(client *c, robj *pattern) {
int retval = 0;
//正则匹配 list match
//client的pubsub_patterns存储正则匹配channel的规则pattern
if (listSearchKey(c->pubsub_patterns,pattern) == NULL) {
retval = 1;
//pubsubPattern存储client及其对应的pattern
pubsubPattern *pat;
listAddNodeTail(c->pubsub_patterns,pattern);
incrRefCount(pattern);
pat = zmalloc(sizeof(*pat));
pat->pattern = getDecodedObject(pattern);
pat->client = c;
//将pubsubPattern存入server的pubsub_patterns
listAddNodeTail(server.pubsub_patterns,pat);
}
/* Notify the client */
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.psubscribebulk);
addReplyBulk(c,pattern);
addReplyLongLong(c,clientSubscriptionsCount(c));
return retval;
}
5. 基于正则匹配取消订阅
//punsubscribe命令 punsubscribe pattern [pattern...]
void punsubscribeCommand(client *c) {
//取消订阅所有
if (c->argc == 1) {
pubsubUnsubscribeAllPatterns(c,1);
} else {
int j;
//取消订阅符合指定正则的channel
for (j = 1; j < c->argc; j++)
pubsubUnsubscribePattern(c,c->argv[j],1);
}
if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;
}
/* Unsubscribe a client from a channel. Returns 1 if the operation succeeded, or
* 0 if the client was not subscribed to the specified channel. */
//正则匹配取消订阅pattern
int pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {
listNode *ln;
//存储client及对应的pattern
pubsubPattern pat;
int retval = 0;
incrRefCount(pattern); /* Protect the object. May be the same we remove */
//查找客户端对应的pattern是否存在
if ((ln = listSearchKey(c->pubsub_patterns,pattern)) != NULL) {
retval = 1;
//存在则删除
listDelNode(c->pubsub_patterns,ln);
pat.client = c;
pat.pattern = pattern;
//查找服务端pattern中对应的client是否存在
ln = listSearchKey(server.pubsub_patterns,&pat);
//存在则删除
listDelNode(server.pubsub_patterns,ln);
}
/* Notify the client */
if (notify) {
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.punsubscribebulk);
addReplyBulk(c,pattern);
addReplyLongLong(c,dictSize(c->pubsub_channels)+
listLength(c->pubsub_patterns));
}
decrRefCount(pattern);
return retval;
}
/* Unsubscribe from all the patterns. Return the number of patterns the
* client was subscribed from. */
//取消订阅所有匹配正则的channel
int pubsubUnsubscribeAllPatterns(client *c, int notify) {
listNode *ln;
listIter li;
int count = 0;
listRewind(c->pubsub_patterns,&li);
//遍历client对应的pubsub_channels dict
while ((ln = listNext(&li)) != NULL) {
robj *pattern = ln->value;
//取消正则订阅
count += pubsubUnsubscribePattern(c,pattern,notify);
}
if (notify && count == 0) {
/* We were subscribed to nothing? Still reply to the client. */
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.punsubscribebulk);
addReply(c,shared.nullbulk);
addReplyLongLong(c,dictSize(c->pubsub_channels)+
listLength(c->pubsub_patterns));
}
return count;
}