小试身手—— Redis之发布/订阅机制

相关命令:

PUBLISH            发布

SUBSCRIBE          订阅

PSUBSCRIBE          一种订阅符合给定模式的所有频道的方法

UNSUBSCRIBE        退订

PUNSUBSCRIBE        退订一个订阅的模式

这些命令被广泛用于构建即时通信应用,比如网络聊天室(chatroom)和实时广播、实时提醒等。

Redis相关源码文件:pubsub.c

使用

PUBLISH 命令用于向给定的频道发送信息,返回值为接收到信息的订阅者数量

redis> PUBLISH treehole "top secret here ..."

(integer) 0

redis> PUBLISH chatroom "hi?"

(integer) 1

SUBSCRIBE 命令订阅给定的一个或多个频道:

  redis> SUBSCRIBE chatroom

  Reading messages... (press Ctrl-C to quit)

  1) "subscribe" # 订阅反馈

  2) "chatroom" # 订阅的频道

  3) (integer) 1 # 目前客户端已订阅频道/模式的数量

  1) "message" # 信息

  2) "chatroom" # 发送信息的频道

  3) "hi?" # 信息内容

SUBSCRIBE 的返回值当中, 1) ""subscribe""是订阅的反馈信息,1)"message "的则是订阅的频道所发送的信息。

SUBSCRIBE 还可以订阅多个频道,这样一来它接收到的信息就可能来自多个频道:

redis> SUBSCRIBE chatroom talk-to-jack

  Reading messages... (press Ctrl-C to quit)

  1) "subscribe" # 订阅 chatroom 的反馈

  2) "chatroom"

  3) (integer) 1

  1) "subscribe" # 订阅 talk-to-jack 的反馈

  2) "talk-to-jack"

  3) (integer) 2

  1) "message" # 来自 chatroom 的消息

  2) "chatroom"

  3) "yahoo"

  1) "message" # 来自 talk-to-peter 的消息

  2) "talk-to-jack"

  3) "Goodmorning, peter."

PSUBSCRIBE 提供了一种订阅符合给定模式的所有频道的方法,比如说,使用 it.* 为输入,就可以订阅所有以 it. 开头的频道,比如 it.news 、 it.blog 、 it.tweets ,诸如此类:

redis> PSUBSCRIBE it.*

  Reading messages... (press Ctrl-C to quit)

  1) "psubscribe"

  2) "it.*"

  3) (integer) 1

  1) "pmessage"

  2) "it.*" # 匹配的模式

  3) "it.news" # 消息的来源频道

  4) "Redis 2.6rc5 release" # 消息内容

  1) "pmessage"

  2) "it.*"

  3) "it.blog"

  4) "Why NoSQL matters"

  1) "pmessage"

  2) "it.*"

  3) "it.tweet"

  4) "@redis: when will the 2.6 stable release?"

当然, PSUBSCRIBE 也可以接受多个参数,从而匹配多种模式。

UNSUBSCRIBE 和 PUNSUBSCRIBE 负责退订给定的频道或模式。


内部实现

流程

当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher)。

而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE 命令接收信息的时候,我们称这个客户端为订阅者(subscriber)。

为了解耦发布者(publisher)和订阅者(subscriber)之间的关系,Redis 使用了 channel (频道)作为两者的中介 —— 发布者将信息直接发布给 channel ,而 channel 负责将信息发送给适当的订阅者,发布者和订阅者之间没有相互关系,也不知道对方的存在

具体实现

SUBSCRIBE 命令的实现

Redis 将所有接受和发送信息的任务交给 channel 来进行,而所有 channel 的信息就储存在 redisServer 这个结构中:

struct redisServer {

  // ......

  dict *pubsub_channels;    // Map channels to list of subscribed clients

  // ......

  };

pubsub_channels 是一个字典,字典的键就是一个个 channel ,而字典的值则是一个链表,链表中保存了所有订阅这个 channel 的客户端。(haspmap之类)

实现 SUBSCRIBE 命令的关键,就是将客户端添加到给定 channel 的订阅链表中。

函数 pubsubSubscribeChannel 是 SUBSCRIBE 命令的底层实现,它完成了将客户端添加到订阅链表中的工作:

    // 订阅指定频道

  // 订阅成功返回 1 ,如果已经订阅过,返回 0

  int pubsubSubscribeChannel(redisClient *c, robj *channel) {

  struct dictEntry *de;

  list *clients = NULL;

  int retval = 0;

  /* Add the channel to the client -> channels hash table */

  // dictAdd 在添加新元素成功时返回 DICT_OK

  // 因此这个判断句表示,如果新订阅 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 */

  // 将 client 添加到订阅给定 channel 的链表中

  // 这个链表是一个哈希表的值,哈希表的键是给定 channel

  // 这个哈希表保存在 server.pubsub_channels 里

  de = dictFind(server.pubsub_channels,channel);

  if (de == NULL) {

  // 如果 de 等于 NULL

  // 表示这个客户端是首个订阅这个 channel 的客户端

  // 那么创建一个新的列表, 并将它加入到哈希表中

  clients = listCreate();

  dictAdd(server.pubsub_channels,channel,clients);

  incrRefCount(channel);

  } else {

  // 如果 de 不为空,就取出这个 clients 链表

  clients = dictGetVal(de);

  }

  // 将客户端加入到链表中

  listAddNodeTail(clients,c);

  }

  /* Notify the client */

  addReply(c,shared.mbulkhdr[3]);

  addReply(c,shared.subscribebulk);

  // 返回订阅的频道

  addReplyBulk(c,channel);

  // 返回客户端当前已订阅的频道和模式数量的总和

  addReplyLongLong(c,dictSize(c->pubsub_channels)+listLength(c->pubsub_patterns));

  return retval;

  }

PSUBSCRIBE 命令的实现

和 redisServer.pubsub_channels 属性类似, redisServer.pubsub_patterns 属性用于保存所有被订阅的模式,和 pubsub_channels 不同的是, pubsub_patterns 是一个链表(而不是字典):

struct redisServer {

  // ......

  list *pubsub_patterns; // A list of pubsub_patterns

  // ......

  };

“我自己是一名Java架构师,辞职目前在做讲师,整理了一份学习Java干货,无论是刚需的高级面试专题还是常用的数据算法都有整理,送给每一位Java小伙伴。在日新月异的程序世界里,我们每一个人都是学生。"

加群:712477306 (招募中)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • redis-订阅与发布 Redis 通过 PUBLISH 、 SUBSCRIBE 等命令实现了订阅与发布模式, 这...
    全能程序猿阅读 5,832评论 0 4
  • 本文主要说明Redis中发布与订阅功能的设计与实现。 I、上帝视角看发布于订阅 Redis主要通过PUBLISH,...
    wenmingxing阅读 613评论 0 1
  • Redis的发布与订阅功能由PUBLISH、SUBSCRIBE、PSUBSCRIBE等命令组成。通过SUBSCRI...
    涵仔睡觉阅读 551评论 0 0
  • 今天看了秋叶大叔的书《和秋叶一起学PPT》,做了些笔记,现在整理一下分享给大家。 好的PPT体现了设计者的逻辑和美...
    Clover0124阅读 886评论 4 14
  • 《刺客聂隐娘》确实是一部不太容易看懂的电影,或者说,是一部不太那么大众的电影。 不太容易看懂,我以为有以下几个方面...
    菜園子阅读 2,359评论 9 31