Redis Source Analysis (30)---pubsub Publish subscription mode

Source: Internet
Author: User

Today, I learned about the tall noun in Redis, "Publish subscription Mode", the word Publish subscription mode was heard in the JMS (Java message Service) Java Messaging Service when I first heard about it. This ranking in a popular point of saying, is that I subscribe to this type of message, when only this kind of message broadcast sent, I will, other messages directly filtered to ensure an efficient transmission efficiency. Let's cut to the chase and learn how Redis implements this publish subscription model. First look at the inside of the simple API construction;

/*-----------------------------------------------------------------------------* Pubsub Low level API *------------
----------------------------------------------------------------/void Freepubsubpattern (void *p)/* Releases the mode of the Publish subscription * * int Listmatchpubsubpattern (void *a, void *b)/* Publish subscription mode matches/int clientsubscriptionscount (redisclient *c)/* Returns the number of subscriptions to the client , including channels + patterns pipe and Mode/int Pubsubsubscribechannel (redisclient *c, RobJ *channel)/* Client subscribe to a channel pipe/INT Pub Subunsubscribechannel (redisclient *c, robj *channel, int notify)/* unsubscribe from the channel/int pubsubsubscribepattern in the client ( Redisclient *c, RobJ *pattern)/* Client clients subscribe to a mode/int Pubsubunsubscribepattern (redisclient *c, robj *pattern, int notif  Y)/* Client client cancels subscription to pattern mode/int pubsubunsubscribeallchannels (redisclient *c, int notify) * All channel/int for clients to unsubscribe themselves Pubsubunsubscribeallpatterns (redisclient *c, int notify)/* Client unsubscribe from all pattern modes/int Pubsubpublishmessage (RobJ * Channel, RobJ *message)/* For all subscribed channel Client Send message/*------------pub/sub API----------------/* Subscribecommand (redisclient *c)/* Subscribe to channel command/V OID Unsubscribecommand (redisclient *c)/* Unsubscribe channel command/void Psubscribecommand (Redisclient *c)/* Subscription mode command/void Punsu Bscribecommand (redisclient *c)/* unsubscribe mode command/void Publishcommand (Redisclient *c)/* Publish message command/void Pubsubcommand (REDISCLI
 ENT *c) * * Publish subscription command/*
In the presence of high-frequency Word pattern (mode) and channel (channel, called Pipe more awkward), that is to say, all the follow-up on the release of the subscription is based on these 2 people started. Here's a general explanation of how this pattern is implemented in Redis:

1. A pubsub_channels channel list has been maintained within the redisclient, recording the channels subscribed by this client

2. At the server service side, a similar variable is maintained, called Pubsub_channels, which is a dict dictionary variable, each channel corresponding to a group of client subscribed to this channel, that is, channel-->list of Clients

3. When a client publish a message, will first go to the service side of the Pubsub_channels find the corresponding channel, traversing the inside of the client, and then send a notice, that is, completed the entire release subscription model.

We can simply look at the Redis subscribe to a channel method implementation;

/* Subscribe a client to a channel. Returns 1 If the operation succeeded, or * 0 if the client is already subscribed to that channel.
    * * * Client subscribe to a channel pipe/int Pubsubsubscribechannel (redisclient *c, robj *channel) {struct dictentry;
    List *clients = NULL;

    int retval = 0; /* Add the channel to the client-> channels hash Table///In the client's dictionary pubsub_channels adds 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 *//Add CLIETN to Pubsub_channels in server, corresponding list
        de = Dictfind (Server.pubsub_channels,channel);
            if (de = = NULL) {//If the client list for this channel is empty, create a new list and add clients = Listcreate ();
            Dictadd (server.pubsub_channels,channel,clients);
        Incrrefcount (channel);
      else {//otherwise, get the client list for this channel, add a new client to the tail clients = Dictgetval (DE);  } listaddnodetail (Clients,c);
    }/* Notify the client *///Add to reply to clients addreply (C,shared.mbulkhdr[3]);
    Addreply (C,shared.subscribebulk);
    Addreplybulk (C,channel);
    Addreplylonglong (C,clientsubscriptionscount (c));
return retval;
 }
Add operations are mainly divided into 2 parts, the client itself internal maintenance of the addition of Pubsub_channels, is a dict Dictionary object, and then the server-side maintenance of pubsub_channels in the addition of the client list. This is also the 2 steps that are performed when the channel channel is removed:

/* Unsubscribe a client from a channel. Returns 1 If the operation succeeded, or * 0 if the client is not subscribed to the specified channel. * * * Unsubscribe from the client channel/int Pubsubunsubscribechannel (redisclient *c, robj *channel, int notify) {struct DICTENTR
    Y *de;
    List *clients;
    ListNode *ln;

    int retval = 0; * Remove the channel from the client-> channels hash Table */Incrrefcount (channel); /* Channel May is just a pointer to the same object we have in the hash tables. 
        Protect it ...//dictionary delete the Channel if (dictdelete (c->pubsub_channels,channel) = DICT_OK) in Pubsub_channels in the client {
        retval = 1; * Remove the client from the channel-> clients list hash table///Remove channel corresponding client list de = Dictfi
        nd (Server.pubsub_channels,channel);
        Redisassertwithinfo (C,null,de!= NULL);
        Clients = Dictgetval (DE);
        ln = Listsearchkey (clients,c); Redisassertwithinfo (C,nuLl,ln!= NULL);
        Listdelnode (CLIENTS,LN); if (listlength (clients) = = 0) {/* Free the list and associated hash entry in all if this is * t He latest the client, so that it would 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;
 }
There is also the corresponding mode of subscription and unsubscribe operation, the principle and channel exactly the same, the difference is that pattern is used to match the channel, what does this mean? There will be an answer in the back, and then look. Finally look at one of the most core methods, the client sends the message method:

/* Publish a message *////////////////////int pubsubpublishmessage (RobJ *channel, RobJ *message) for all subscribers subscribing to channel
    int receivers = 0;
    struct Dictentry *de;
    ListNode *ln;

    Listiter Li; /* Send to clients listening for that channel///find channel corresponding dictentry de = Dictfind (server.pubsub_channels,cha
    Nnel);
        if (DE) {//get this channel corresponding customer list *list = Dictgetval (DE);
        ListNode *ln;

        Listiter Li;
        Listrewind (List,&li);

            while (ln = listnext (&li))!= NULL) {//sequentially remove the customer order from the list, add message reply redisclient *c = ln->value;
            Addreply (C,shared.mbulkhdr[3]);
            Addreply (C,shared.messagebulk);
            Addreplybulk (C,channel);
            Add message reply Addreplybulk (c,message);
        receivers++; }/* Send to clients listening to matching channels/* is sent to a client message that attempts to match the channel/if (Listlength (server . Pubsub_patterns)) {Listrewind (SerVer.pubsub_patterns,&li);
        Channel = Getdecodedobject (channel);
			
			while (ln = listnext (&li))!= NULL) {Pubsubpattern *pat = ln->value;
                                If the client's schema matches channel, the message will also be sent 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;
 }
The role of pattern is reflected in that, if certain patterns match the channel channel, then the client of the mode will also receive the message. In Server->pubsub_patterns, Pubsub_patterns is a list of lists in which each pattern corresponds to only one client, the pat->client above, There is an essential difference between this and channel. After finishing the basic operation of the publish subscription model, incidentally, the Notify notification class related to this is also slightly talked about, and the notification has only 3 methods,
/*-----------------API-------------------*
/int keyspaceeventsstringtoflags (char *classes)/* The type of the key-value character is converted to the corresponding class type
/SDS keyspaceeventsflagstostring (int flags)/* By entering the flag value class, converting to the character type/
void notifykeyspaceevent (int type, char *event, robj *key, int dbid)/* Publish notification method, divided into 2 categories, keyspace notification, keyevent notice * *
The conversion involving the string to flag and flag to string is not known where it will be used;

/* Turn A string representing notification classes into an integer * representing notification classes flags xored. * The function Returns-1 if the input contains characters not mapping to * any class.

    int C, flags = 0;
        while ((c = *p++)!= ' i ') {switch (c) {case ' A ': Flags |= redis_notify_all; Case ' G ': Flags |= redis_notify_generic;
        Break Case ' $ ': Flags |= redis_notify_string;
        Break Case ' l ': Flags |= redis_notify_list;
        Break Case ' s ': Flags |= redis_notify_set;
        Break Case ' h ': Flags |= redis_notify_hash;
        Break Case ' Z ': Flags |= redis_notify_zset;
        Break Case ' x ': Flags |= redis_notify_expired;
        Break Case ' E ': Flags |= redis_notify_evicted;
        Break Case ' K ': Flags |= redis_notify_keyspace;
        Break Case ' E ': Flags |= redis_notify_keyevent;
        Break Default return-1;
} return flags;
 }
Should be a conversion between the type that responds to keyboard input and the Redis type. The Notify method also has a notification method for event events:

/* The API provided to the rest of the Redis core are a simple function: * * notifykeyspaceevent (char *event, RobJ *key,
 int dbid);
 * ' event ' is a C string representing the event name.
 * ' key ' is a Redis object representing the key name.  * ' dbid ' is the database ID where the key lives. * * * Notification method, divided into 2 categories, keyspace notification, keyevent notice/void notifykeyspaceevent (int type, char *event, robj *key, int dbid) {s
    DS Chan;
    RobJ *chanobj, *eventobj;
    int len =-1;

    Char buf[24]; /* If notifications for this class of the events are off and return ASAP. */if (!) (

    Server.notify_keyspace_events & type)) return;
    
    eventobj = Createstringobject (Event,strlen (event)); 2 types of notification form, slightly different/* __keyspace@<db>__:<key> <event> notifications.
        */if (server.notify_keyspace_events & redis_notify_keyspace) {chan = Sdsnewlen ("__keyspace@", 11);
        Len = ll2string (buf,sizeof (BUF), dbid);
        Chan = Sdscatlen (chan, buf, Len); Chan = Sdscatlen (Chan, "__:", 3);
        Chan = Sdscatsds (chan, key->ptr);
        Chanobj = CreateObject (Redis_string, Chan);
        The above steps, the component format string, the final release message, the following keyevent notice of Empathy pubsubpublishmessage (Chanobj, eventobj);
    Decrrefcount (Chanobj); }/* __keyevente@<db>__:<event> <key> notifications.
        */if (server.notify_keyspace_events & redis_notify_keyevent) {chan = Sdsnewlen ("__keyevent@", 11);
        if (len = = 1) len = ll2string (buf,sizeof (BUF), dbid);
        Chan = Sdscatlen (chan, buf, Len);
        Chan = Sdscatlen (chan, "__:", 3);
        Chan = Sdscatsds (chan, eventobj->ptr);
        Chanobj = CreateObject (Redis_string, Chan);
        Pubsubpublishmessage (Chanobj, key);
    Decrrefcount (Chanobj);
} decrrefcount (eventobj);
 }
There are 2 kinds of event notifications for Keyspace and KeyEvent. How to use it, wait for the back when you see.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.