The publishsubscribe function of apsaradb for redis is similar to the traditional message routing function. The publisher publishes messages, the subscriber receives messages, and the bridge between the subscriber and the subscriber is the channel or pattern subscribed. The publisher publishes messages to the specified publish or pattern, and the subscriber blocks messages in the subscribed channel or pattern. Yes
The publish/subscribe function of apsaradb for redis is similar to the traditional message routing function. The publisher publishes messages, the subscriber receives messages, and the bridge between the publisher and the subscriber is the channel or pattern of the subscription. The publisher publishes messages to the specified publish or pattern, and the subscriber blocks messages in the subscribed channel or pattern. Yes
The publish/subscribe function of apsaradb for redis is similar to the traditional message routing function. The publisher publishes messages, the subscriber receives messages, and the bridge between the publisher and the subscriber is the channel or pattern of the subscription. The publisher publishes messages to the specified publish or pattern, and the subscriber blocks messages in the subscribed channel or pattern. As you can see, the publisher does not specify a subscriber to receive messages, and the subscriber cannot only receive messages from a specific publisher. The relationship between the subscriber and the publisher is loosely coupled. The Subscriber does not know who published the message or who will receive the message.
The PUBLISH/SUBSCRIBE function of apsaradb for redis is mainly manifested by five commands: SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, and PUBLISH. Here, SUBSCRIBE and UNSUBSCRIBE are used to SUBSCRIBE to or UNSUBSCRIBE to channels, while PSUBSCRIBE and PUNSUBSCRIBE are used to SUBSCRIBE to or UNSUBSCRIBE to pattern, and publish messages through the publish command.
For the implementation of the publishing/subscription function, let's take a look at several structures related to this.
struct redisServer { --- /* Pubsub */ dict *pubsub_channels;/* Map channels to list of subscribed clients */ list *pubsub_patterns;/* A list of pubsub_patterns */ ---}typedef struct redisClient { --- dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */ list *pubsub_patterns; /* patterns a client is interested in (SUBSCRIBE) */} redisClient;
In redis's Global server variable (redisServer type), the relationship between the channel and the subscriber is saved using the dictionary pubsub_channels. A linked list composed of a specific channel and all subscribers constitutes one of the pubsub_channels dictionaries, that is, each item in the dictionary can be expressed as a (channel, subscriber linked list). The relationship between pattern and subscriber is saved by the chain table pubsub_patterns. Each item in the chain table can be expressed as (pattern, redisClient) a dictionary.
In the redisClient structure of a specific subscriber, pubsub_channels stores the dictionary of the channel it subscribes to, and the subscription mode is saved in the pubsub_patterns linked list.
From the above explanation, let's take a look at the worst time complexity of subscription/release commands (note that the complexity of adding, deleting, querying, and modifying a dictionary item is O (1 ), the query and deletion complexity of the linked list is O (N), and the complexity of adding one item from the end of the linked list is O (1 )).
SUBSCRIBE:
The SUBSCRIBE is used to SUBSCRIBE to a specific channel. This requires an additional entry (complexity: O (1) in the pubsub_channels structure of the subscriber's redisClient )), find the channel (complexity: O (1) in pubsub_channels of redisServer, and add an item (complexity: O (1) at the end of the subscriber linked list of the channel). Note, if this channel is not found in pubsub_channels, the insertion complexity is also O (1). Therefore, the worst time complexity of SUBSCRIBE to a specific channel is O (1 ).
UNSUBSCRIBE:
When a subscriber cancels a subscription, the subscriber must first delete one item (complexity: O (1) from the pubsub_channels In the subscriber's redisClient structure )), find the channel (complexity: O (1) in pubsub_channels of redisServer, and delete the subscriber (complexity: O (1) in the subscriber list of the channel )), therefore, the total complexity is O (N), and N is the number of subscribers of a specific channel.
PSUBSCRIBE:
When a subscriber uses PSUBSCRIBE to subscribe to pattern, the pubsub_patterns in the redisClient structure must first find whether the pattern already exists (complexity is O (N )), if the pubsub_patterns in the redisClient structure and the pubsub_patterns in the redisServer structure do not exist, an item is added at the end of the pubsub_patterns chain table (complexity is O (1). Therefore, the total complexity is O (N ), N indicates the subscription mode of the subscriber.
PUNSUBSCRIBE:
When a subscriber uses PUNSUBSCRIBE to cancel a pattern subscription, the subscriber must first Delete the pattern (complexity is O (N) from the pubsub_patterns linked list in the redisClient structure )), in the pubsub_patterns linked list in the redisServer structure, the ing between subscriber and pattern is deleted (complexity is O (M). Therefore, the total complexity is O (N + M ), N indicates the subscription mode of the subscriber, and M indicates the ing number composed of all subscribers and all pattern in the system.
PUBLISH:
When a message is published, it is only published to a specific channel, but the channel may match a pattern. Therefore, you must first find the subscriber linked list (O (1) of the channel in pubsub_channels in the redisServer structure, and then send it to all subscribers (complexity is O (N )), then, check all the items in the pubsub_patterns linked list in the redisServer structure to see if the channel matches the pattern in the item (complexity is O (M). (Note that this does not include the complexity of pattern matching ), therefore, the total complexity is O (N + M ),. N indicates the number of subscribers of the channel, and M indicates the ing number composed of all subscribers and all pattern in the system. In addition, it can be seen that a subscriber may receive the same message multiple times.
After explaining the publishing/subscription algorithm, the code is easy to understand. Here, only the publishCommand code of the PUBLISH command processing function is provided. For more code about the commands, see the source code of redis.
static void publishCommand(redisClient *c) { int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]); addReplyLongLong(c,receivers);}/* Publish a message */static int pubsubPublishMessage(robj *channel, robj *message) { int receivers = 0; struct dictEntry *de; listNode *ln; listIter li; /* Send to clients listening for that channel */ de = dictFind(server.pubsub_channels,channel); if (de) { list *list = dictGetEntryVal(de); listNode *ln; listIter li; listRewind(list,&li); while ((ln = listNext(&li)) != NULL) { redisClient *c = ln->value; addReply(c,shared.mbulk3); addReply(c,shared.messagebulk); addReplyBulk(c,channel); addReplyBulk(c,message); receivers++; } } /* Send to clients listening to matching channels */ if (listLength(server.pubsub_patterns)) { listRewind(server.pubsub_patterns,&li); channel = getDecodedObject(channel); 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.mbulk4); addReply(pat->client,shared.pmessagebulk); addReplyBulk(pat->client,pat->pattern); addReplyBulk(pat->client,channel); addReplyBulk(pat->client,message); receivers++; } } decrRefCount(channel); } return receivers;}
The client in the publish/subscribe mode cannot publish commands other than the preceding five commands (except quit), which are checked in the processCommand function, you can refer to the previous command processing chapter to explain this function.
Original article address: redis source code analysis 20-publish/subscribe, thanks to the original author for sharing.