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

来源:互联网 发布:什么是网络超市? 编辑:程序博客网 时间:2024/06/06 01:10

发布与订阅

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

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

使用示例

  客户端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 设计与实现(第二版)

0 0
原创粉丝点击