redis学习笔记(20)---发布与订阅

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012658346/article/details/51417721

发布与订阅

  redis的发布与订阅功能由以下几个命令实现:

序号 命令及描述
1 SUBSCRIBE
订阅给定的一个或多个频道的信息
2 PUBLISH
将信息 message 发送到指定的频道 channel
3 PUBSUB
查看订阅与发布系统状态
4 PSUBSCRIBE
订阅一个或多个符合给定模式的频道

使用示例

  客户端A通过subscribe命令订阅频道“news”,客户端B通过命令publish发布“news”频道的信息,可以发现客户端A会收到这条信息
  这里写图片描述
  这里写图片描述

实现

  redis将所有的订阅关系都保存在字典pubsub_channels中,这个字典的键key为被订阅的频道,对应的value是一个链表,指向所有订阅了这个channel的客户端

struct redisServer { 
    ......
    dict *pubsub_channels; 
    ......
};

  每个client也维护了一个这样的字典pubsub_channels,用来记录每个client所订阅的频道。这个字典的key为订阅的频道,value为NULL

typedef struct redisClient {
    ......
   dict *pubsub_channels;  
   ......
} redisClient;

  假设有三个客户端:client A、client B、client C
  其中A订阅了频道news和it,B订阅了频道news,C订阅了频道it
  则一个基本的示意图如下:
  这里写图片描述
  server的字典中保存了两对key-value对,key为news时,对应的链表中有A和B两个client
  key为it时,对应的链表中有A和C两个client。
  client A的字典中保存着两个key-value对,key为news和it两个频道,value为NULL

订阅subscribe  

void subscribeCommand(redisClient *c) {
    for (j = 1; j < c->argc; j++) //依次处理每个订阅的频道
        pubsubSubscribeChannel(c,c->argv[j]);
    c->flags |= REDIS_PUBSUB;
}

int pubsubSubscribeChannel(redisClient *c, robj *channel) { 
    //将订阅的channel加入到对应client的字典中
    if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) { 
        //在server的字典中查找channel
        de = dictFind(server.pubsub_channels,channel); 

        //若server中不存在这个channel
        if (de == NULL) { 
            //则创建一个链表,将channel-client加入到server中
            clients = listCreate();
            dictAdd(server.pubsub_channels,channel,clients);
        } else { 
            //获取channel对应的client链表
            clients = dictGetVal(de);
        } 
        //将这个client加入到链表尾部
        listAddNodeTail(clients,c);
    }

    //将信息返回给客户端
    addReply(c,shared.mbulkhdr[3]);
    addReply(c,shared.subscribebulk);
    addReplyBulk(c,channel);
    addReplyLongLong(c,clientSubscriptionsCount(c));
    return retval;
}

发布publish

void publishCommand(redisClient *c) {
    int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]); 
    addReplyLongLong(c,receivers);
}

int pubsubPublishMessage(robj *channel, robj *message) {
    //在server的字典中,找到channel
    de = dictFind(server.pubsub_channels,channel);
    if (de) { 
        //获取channel对应的client列表,遍历列表
        list *list = dictGetVal(de);
        while ((ln = listNext(&li)) != NULL) { 
            //依次向订阅了这个频道的所有client发送信息
            redisClient *c = ln->value;

            addReply(c,shared.mbulkhdr[3]);
            addReply(c,shared.messagebulk);
            addReplyBulk(c,channel);
            addReplyBulk(c,message);
            receivers++;
        }
    }
    //向所有匹配模式的用户发送信息
    if (listLength(server.pubsub_patterns)) {
        listRewind(server.pubsub_patterns,&li);
        channel = getDecodedObject(channel);
        while ((ln = listNext(&li)) != NULL) {
            pubsubPattern *pat = ln->value;
           ......
        }
    }
    return receivers;
}



本文所引用的源码全部来自Redis3.0.7版本

redis学习参考资料:
https://github.com/huangz1990/redis-3.0-annotated
Redis 设计与实现(第二版)

猜你喜欢

转载自blog.csdn.net/u012658346/article/details/51417721