#include "server.h" int clientSubscriptionsCount(client *c); /*----------------------------------------------------------------------------- * Pubsub client replies API 发布订阅客户端回复API *----------------------------------------------------------------------------*/ /* Send a pubsub message of type "message" to the client. * Normally 'msg' is a Redis object containing the string to send as * message. However if the caller sets 'msg' as NULL, it will be able * to send a special message (for instance an Array type) by using the * addReply*() API family. */ 向客户端发送类型为“message”的订阅消息。通常,“msg”是一个Redis对象,包含要作为消息发送的字符串。 但是,如果调用方将“msg”设置为NULL,它将能够使用addReply系列的API发送特殊消息(例如数组类型) void addReplyPubsubMessage(client *c, robj *channel, robj *msg) { if (c->resp == 2) 协议2的版本 addReply(c,shared.mbulkhdr[3]); else addReplyPushLen(c,3); addReply(c,shared.messagebulk); addReplyBulk(c,channel); if (msg) addReplyBulk(c,msg); 消息非空,回复消息 } /* Send a pubsub message of type "pmessage" to the client. The difference * with the "message" type delivered by addReplyPubsubMessage() is that * this message format also includes the pattern that matched the message. */ 发送pmessage的订阅消息给客户端。 不同于通过函数addReplyPubsubMessage发送的message类型相比, 这种消息的格式还包括匹配消息的模式 void addReplyPubsubPatMessage(client *c, robj *pat, robj *channel, robj *msg) { if (c->resp == 2) addReply(c,shared.mbulkhdr[4]); else addReplyPushLen(c,4); addReply(c,shared.pmessagebulk); addReplyBulk(c,pat); addReplyBulk(c,channel); addReplyBulk(c,msg); } /* Send the pubsub subscription notification to the client. */ 将pubsub订阅通知发送到客户端 void addReplyPubsubSubscribed(client *c, robj *channel) { if (c->resp == 2) addReply(c,shared.mbulkhdr[3]); else addReplyPushLen(c,3); addReply(c,shared.subscribebulk); addReplyBulk(c,channel); addReplyLongLong(c,clientSubscriptionsCount(c)); } /* Send the pubsub unsubscription notification to the client. * Channel can be NULL: this is useful when the client sends a mass * unsubscribe command but there are no channels to unsubscribe from: we * still send a notification. */ 向客户端发送pubsub取消订阅通知。通道可以为空:当客户端发送批量取消订阅命令但没有可取消订阅的通道时, 此选项非常有用:我们仍然发送通知。 void addReplyPubsubUnsubscribed(client *c, robj *channel) { if (c->resp == 2) addReply(c,shared.mbulkhdr[3]); else addReplyPushLen(c,3); addReply(c,shared.unsubscribebulk); if (channel) addReplyBulk(c,channel); else addReplyNull(c); addReplyLongLong(c,clientSubscriptionsCount(c)); } /* Send the pubsub pattern subscription notification to the client. */ 向客户端发送pubsub模式订阅通知。 void addReplyPubsubPatSubscribed(client *c, robj *pattern) { if (c->resp == 2) addReply(c,shared.mbulkhdr[3]); else addReplyPushLen(c,3); addReply(c,shared.psubscribebulk); addReplyBulk(c,pattern); addReplyLongLong(c,clientSubscriptionsCount(c)); } /* Send the pubsub pattern unsubscription notification to the client. * Pattern can be NULL: this is useful when the client sends a mass * punsubscribe command but there are no pattern to unsubscribe from: we * still send a notification. */ 向客户端发送pubsub模式取消订阅通知。模式可以为NULL:当客户端发送mass punsubscribe命令但没有模式可取消订阅时, 这非常有用:我们仍然发送通知。 void addReplyPubsubPatUnsubscribed(client *c, robj *pattern) { if (c->resp == 2) addReply(c,shared.mbulkhdr[3]); else addReplyPushLen(c,3); addReply(c,shared.punsubscribebulk); if (pattern) addReplyBulk(c,pattern); else addReplyNull(c); addReplyLongLong(c,clientSubscriptionsCount(c)); } /*----------------------------------------------------------------------------- * Pubsub low level API 发布定于的底层API *----------------------------------------------------------------------------*/ 释放发布订阅模式 typedef struct pubsubPattern { client *client; 客户端 robj *pattern; 模式 } pubsubPattern; void freePubsubPattern(void *p) { pubsubPattern *pat = p; decrRefCount(pat->pattern); zfree(pat); } 列出匹配发布订阅的模式 int listMatchPubsubPattern(void *a, void *b) { pubsubPattern *pa = a, *pb = b; return (pa->client == pb->client) && (equalStringObjects(pa->pattern,pb->pattern)); } /* Return the number of channels + patterns a client is subscribed to. */ 返回客户端订阅的通道数+模式数。 int clientSubscriptionsCount(client *c) { return dictSize(c->pubsub_channels)+ listLength(c->pubsub_patterns); } /* Subscribe a client to a channel. Returns 1 if the operation succeeded, or * 0 if the client was already subscribed to that channel. */ 绑定客户端到订阅频道。如果操作成功,则返回1;如果客户端已订阅该通道,则返回0。 int pubsubSubscribeChannel(client *c, robj *channel) { dictEntry *de; list *clients = NULL; int retval = 0; /* Add the channel to the client -> channels hash table */ 添加频道到客户端机的频道哈希表 if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) { retval = 1; incrRefCount(channel);订阅通道+1 /* Add the client to the channel -> list of clients hash table */ 添加客户端到 通道的客户端列表 de = dictFind(server.pubsub_channels,channel); if (de == NULL) { clients = listCreate(); dictAdd(server.pubsub_channels,channel,clients); incrRefCount(channel); } else { clients = dictGetVal(de); } listAddNodeTail(clients,c); } /* Notify the client */通知客户端 addReplyPubsubSubscribed(c,channel); return retval; } /* Unsubscribe a client from a channel. Returns 1 if the operation succeeded, or * 0 if the client was not subscribed to the specified channel. */ 从频道取消订阅客户端。如果操作成功,则返回1;如果客户端未订阅指定通道,则返回0 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... */ 频道可能指向哈希表中相同对象的指针。保护它 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); listDelNode(clients,ln); 不为空就删除 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. */ 如果这是最后的客户端,释放列表和相关的哈希项,这样就有避免滥用Redis PUBSUB创建数百万个无用频道 dictDelete(server.pubsub_channels,channel); 删除频道 } } /* Notify the client */ if (notify) addReplyPubsubUnsubscribed(c,channel); 取消订阅 decrRefCount(channel); /* it is finally safe to release it */ 终于可以安全地释放它了 return retval; } /* Subscribe a client to a pattern. Returns 1 if the operation succeeded, or 0 if the client was already subscribed to that pattern. */ 向客户端订阅模式。如果操作成功,则返回1;如果客户端已订阅该模式,则返回0。 int pubsubSubscribePattern(client *c, robj *pattern) { dictEntry *de; list *clients; int retval = 0; if (listSearchKey(c->pubsub_patterns,pattern) == NULL) { retval = 1; pubsubPattern *pat; listAddNodeTail(c->pubsub_patterns,pattern); incrRefCount(pattern); pat = zmalloc(sizeof(*pat)); pat->pattern = getDecodedObject(pattern); pat->client = c; listAddNodeTail(server.pubsub_patterns,pat); /* Add the client to the pattern -> list of clients hash table */ 将客户端添加到 模式的哈希列表中 de = dictFind(server.pubsub_patterns_dict,pattern); if (de == NULL) { clients = listCreate(); dictAdd(server.pubsub_patterns_dict,pattern,clients); 创建新模式的哈希键和值 incrRefCount(pattern); } else { clients = dictGetVal(de); 存在就直接获取值 } listAddNodeTail(clients,c); 添加到列表的末尾 } /* Notify the client */ addReplyPubsubPatSubscribed(c,pattern); return retval; } /* Unsubscribe a client from a channel. Returns 1 if the operation succeeded, or * 0 if the client was not subscribed to the specified channel. */ 从频道取消订阅客户端。如果操作成功,则返回1;如果客户端未订阅指定通道,则返回0。 int pubsubUnsubscribePattern(client *c, robj *pattern, int notify) { dictEntry *de; list *clients; listNode *ln; pubsubPattern pat; int retval = 0; incrRefCount(pattern); /* Protect the object. May be the same we remove */ 保护对象,万一和我们要移除的是同一对象 if ((ln = listSearchKey(c->pubsub_patterns,pattern)) != NULL) { 是否在已有订阅模式中 retval = 1; listDelNode(c->pubsub_patterns,ln); 在的话删除 pat.client = c; pat.pattern = pattern; ln = listSearchKey(server.pubsub_patterns,&pat); listDelNode(server.pubsub_patterns,ln); /* Remove the client from the pattern -> clients list hash table */ 从模式的客户端哈希中的列表移除客户端 de = dictFind(server.pubsub_patterns_dict,pattern); serverAssertWithInfo(c,NULL,de != NULL); clients = dictGetVal(de); ln = listSearchKey(clients,c); serverAssertWithInfo(c,NULL,ln != NULL); listDelNode(clients,ln); if (listLength(clients) == 0) { /* Free the list and associated hash entry at all if this was * the latest client. */ 如果这是最后的客户端,释放相关联的哈希实体和列表 dictDelete(server.pubsub_patterns_dict,pattern); } } /* Notify the client */ if (notify) addReplyPubsubPatUnsubscribed(c,pattern); decrRefCount(pattern); return retval; } /* Unsubscribe from all the channels. Return the number of channels the * client was subscribed to. */ 取消所有频道的订阅。返回客户端订阅的频道数。 int pubsubUnsubscribeAllChannels(client *c, int notify) { dictIterator *di = dictGetSafeIterator(c->pubsub_channels); dictEntry *de; int count = 0; 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) addReplyPubsubUnsubscribed(c,NULL); dictReleaseIterator(di); return count; } /* Unsubscribe from all the patterns. Return the number of patterns the * client was subscribed from. */ 取消订阅所有模式。返回订阅客户端的模式数。 int pubsubUnsubscribeAllPatterns(client *c, int notify) { listNode *ln; listIter li; int count = 0; listRewind(c->pubsub_patterns,&li); while ((ln = listNext(&li)) != NULL) { robj *pattern = ln->value; count += pubsubUnsubscribePattern(c,pattern,notify); } if (notify && count == 0) addReplyPubsubPatUnsubscribed(c,NULL); return count; } /* Publish a message */ 发布消息 int pubsubPublishMessage(robj *channel, robj *message) { int receivers = 0; dictEntry *de; dictIterator *di; listNode *ln; listIter li; /* Send to clients listening for that channel */ 发送给监听该频道的客户端 de = dictFind(server.pubsub_channels,channel); 找出监听该频道的客户端 if (de) { 非空的情况下,遍历监听客户端列表 list *list = dictGetVal(de); listNode *ln; listIter li; listRewind(list,&li); while ((ln = listNext(&li)) != NULL) { 遍历订阅的客户端列表 client *c = ln->value; addReplyPubsubMessage(c,channel,message); 发出消息 receivers++; } } /* Send to clients listening to matching channels */ 发送到收听匹配频道的客户端 di = dictGetIterator(server.pubsub_patterns_dict); 查找模式匹配的客户端 if (di) { 非空 channel = getDecodedObject(channel); while((de = dictNext(di)) != NULL) { robj *pattern = dictGetKey(de); list *clients = dictGetVal(de); if (!stringmatchlen((char*)pattern->ptr, sdslen(pattern->ptr), (char*)channel->ptr, sdslen(channel->ptr),0)) continue; 是否匹配,匹配就往下走,不匹配就跳到下一个 listRewind(clients,&li); while ((ln = listNext(&li)) != NULL) { 遍历 client *c = listNodeValue(ln); addReplyPubsubPatMessage(c,pattern,channel,message); receivers++; } } decrRefCount(channel); dictReleaseIterator(di); } return receivers; } /*----------------------------------------------------------------------------- * Pubsub commands implementation 发布订阅命令的实现 *----------------------------------------------------------------------------*/ void subscribeCommand(client *c) { int j; for (j = 1; j < c->argc; j++) pubsubSubscribeChannel(c,c->argv[j]); 客户端订阅指定频道 c->flags |= CLIENT_PUBSUB; } void unsubscribeCommand(client *c) { 客户端取消订阅频道 if (c->argc == 1) { pubsubUnsubscribeAllChannels(c,1); 取消所有订阅频道 } else { int j; for (j = 1; j < c->argc; j++) pubsubUnsubscribeChannel(c,c->argv[j],1); 取消指定订阅频道 } if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB; } 根据客户端模式匹配订阅频道 void psubscribeCommand(client *c) { int j; for (j = 1; j < c->argc; j++) pubsubSubscribePattern(c,c->argv[j]); c->flags |= CLIENT_PUBSUB; } 根据客户端模式取消匹配订阅频道 void punsubscribeCommand(client *c) { if (c->argc == 1) { pubsubUnsubscribeAllPatterns(c,1); } else { int j; for (j = 1; j < c->argc; j++) pubsubUnsubscribePattern(c,c->argv[j],1); } if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB; } 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); } /* PUBSUB command for Pub/Sub introspection. */ 用于发布/订阅介绍如何使用PUBSUB命令 void pubsubCommand(client *c) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { const char *help[] = { "CHANNELS [<pattern>] -- Return the currently active channels matching a pattern (default: all).", "NUMPAT -- Return number of subscriptions to patterns.", "NUMSUB [channel-1 .. channel-N] -- Returns the number of subscribers for the specified channels (excluding patterns, default: none).", NULL }; addReplyHelp(c, help); } else if (!strcasecmp(c->argv[1]->ptr,"channels") && (c->argc == 2 || c->argc == 3)) { /* PUBSUB CHANNELS [<pattern>] */ 用于返回服务器当前被订阅的频道 sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr; dictIterator *di = dictGetIterator(server.pubsub_channels); dictEntry *de; long mblen = 0; void *replylen; replylen = addReplyDeferredLen(c); while((de = dictNext(di)) != NULL) { robj *cobj = dictGetKey(de); sds channel = cobj->ptr; if (!pat || stringmatchlen(pat, sdslen(pat), channel, sdslen(channel),0)) 模式非空 匹配中 { addReplyBulk(c,cobj); 返回匹配的频道 mblen++; } } dictReleaseIterator(di); setDeferredArrayLen(c,replylen,mblen); } else if (!strcasecmp(c->argv[1]->ptr,"numsub") && c->argc >= 2) { /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */ 返回对应频道订阅者(客户端)的数量 int j; addReplyArrayLen(c,(c->argc-2)*2); for (j = 2; j < c->argc; j++) { list *l = dictFetchValue(server.pubsub_channels,c->argv[j]); addReplyBulk(c,c->argv[j]); addReplyLongLong(c,l ? listLength(l) : 0); } } else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) { /* PUBSUB NUMPAT */ 统计所有模式被订阅的次数 addReplyLongLong(c,listLength(server.pubsub_patterns)); } else { addReplySubcommandSyntaxError(c); } }