redis-訂閱與發(fā)布

redis-訂閱與發(fā)布

Redis 通過 PUBLISH 、 SUBSCRIBE 等命令實(shí)現(xiàn)了訂閱與發(fā)布模式健芭, 這個(gè)功能提供兩種信息機(jī)制县钥, 分別是訂閱/發(fā)布到頻道和訂閱/發(fā)布到模式, 下文先討論訂閱/發(fā)布到頻道的實(shí)現(xiàn)慈迈, 再討論訂閱/發(fā)布到模式的實(shí)現(xiàn)若贮。

頻道的訂閱與信息發(fā)送

Redis 的 SUBSCRIBE 命令可以讓客戶端訂閱任意數(shù)量的頻道省有, 每當(dāng)有新信息發(fā)送到被訂閱的頻道時(shí), 信息就會(huì)被發(fā)送給所有訂閱指定頻道的客戶端谴麦。

作為例子蠢沿, 下圖展示了頻道 channel1 , 以及訂閱這個(gè)頻道的三個(gè)客戶端 —— client2 匾效、 client5 和 client1 之間的關(guān)系:

當(dāng)有新消息通過 PUBLISH 命令發(fā)送給頻道 channel1 時(shí)舷蟀,

這個(gè)消息就會(huì)被發(fā)送給訂閱它的三個(gè)客戶端:

在后面的內(nèi)容中, 我們將探討 SUBSCRIBE 和 PUBLISH 命令的實(shí)現(xiàn)面哼, 以及這套訂閱與發(fā)布機(jī)制的運(yùn)作原理野宜。

訂閱頻道

每個(gè) Redis 服務(wù)器進(jìn)程都維持著一個(gè)表示服務(wù)器狀態(tài)的 redis.h/redisServer 結(jié)構(gòu), 結(jié)構(gòu)的 pubsub_channels 屬性是一個(gè)字典魔策, 這個(gè)字典就用于保存訂閱頻道的信息:

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

其中匈子,字典的鍵為正在被訂閱的頻道, 而字典的值則是一個(gè)鏈表闯袒, 鏈表中保存了所有訂閱這個(gè)頻道的客戶端旬牲。

比如說,在下圖展示的這個(gè) pubsub_channels 示例中搁吓, client2 、 client5 和 client1 就訂閱了 channel1 吭历, 而其他頻道也分別被別的客戶端所訂閱:

當(dāng)客戶端調(diào)用 SUBSCRIBE 命令時(shí)堕仔, 程序就將客戶端和要訂閱的頻道在 pubsub_channels 字典中關(guān)聯(lián)起來。

舉個(gè)例子晌区,如果客戶端 client10086 執(zhí)行命令 SUBSCRIBE channel1 channel2 channel3 摩骨,那么前面展示的 pubsub_channels 將變成下面這個(gè)樣子:

SUBSCRIBE 命令的行為可以用偽代碼表示如下:

def SUBSCRIBE(client, channels):

    # 遍歷所有輸入頻道
    for channel in channels:

        # 將客戶端添加到鏈表的末尾
        redisServer.pubsub_channels[channel].append(client)

通過 pubsub_channels 字典, 程序只要檢查某個(gè)頻道是否為字典的鍵朗若, 就可以知道該頻道是否正在被客戶端訂閱恼五; 只要取出某個(gè)鍵的值, 就可以得到所有訂閱該頻道的客戶端的信息哭懈。

發(fā)送信息到頻道

了解了 pubsub_channels 字典的結(jié)構(gòu)之后灾馒, 解釋 PUBLISH 命令的實(shí)現(xiàn)就非常簡單了: 當(dāng)調(diào)用 PUBLISH channel message 命令, 程序首先根據(jù) channel 定位到字典的鍵遣总, 然后將信息發(fā)送給字典值鏈表中的所有客戶端睬罗。

比如說,對于以下這個(gè) pubsub_channels 實(shí)例旭斥, 如果某個(gè)客戶端執(zhí)行命令 PUBLISH channel1 "hello moto" 容达,那么 client2 、 client5 和 client1 三個(gè)客戶端都將接收到 "hello moto" 信息:

PUBLISH 命令的實(shí)現(xiàn)可以用以下偽代碼來描述:

def PUBLISH(channel, message):

    # 遍歷所有訂閱頻道 channel 的客戶端
    for client in server.pubsub_channels[channel]:

        # 將信息發(fā)送給它們
        send_message(client, message)

退訂頻道

使用 UNSUBSCRIBE 命令可以退訂指定的頻道垂券, 這個(gè)命令執(zhí)行的是訂閱的反操作: 它從 pubsub_channels 字典的給定頻道(鍵)中花盐, 刪除關(guān)于當(dāng)前客戶端的信息, 這樣被退訂頻道的信息就不會(huì)再發(fā)送給這個(gè)客戶端。

模式的訂閱與信息發(fā)送

當(dāng)使用 PUBLISH 命令發(fā)送信息到某個(gè)頻道時(shí)算芯, 不僅所有訂閱該頻道的客戶端會(huì)收到信息柒昏, 如果有某個(gè)/某些模式和這個(gè)頻道匹配的話, 那么所有訂閱這個(gè)/這些頻道的客戶端也同樣會(huì)收到信息也祠。

下圖展示了一個(gè)帶有頻道和模式的例子昙楚, 其中 tweet.shop.* 模式匹配了 tweet.shop.kindle 頻道和 tweet.shop.ipad 頻道, 并且有不同的客戶端分別訂閱它們?nèi)齻€(gè):

當(dāng)有信息發(fā)送到 tweet.shop.kindle 頻道時(shí)诈嘿, 信息除了發(fā)送給 clientX 和 clientY 之外堪旧, 還會(huì)發(fā)送給訂閱 tweet.shop.* 模式的 client123 和 client256 :

另一方面, 如果接收到信息的是頻道 tweet.shop.ipad 奖亚, 那么 client123 和 client256 同樣會(huì)收到信息:

訂閱模式

redisServer.pubsub_patterns 屬性是一個(gè)鏈表淳梦,鏈表中保存著所有和模式相關(guān)的信息:

struct redisServer {
    // ...
    list *pubsub_patterns;
    // ...
};

鏈表中的每個(gè)節(jié)點(diǎn)都包含一個(gè) redis.h/pubsubPattern 結(jié)構(gòu):

typedef struct pubsubPattern {
    redisClient *client;
    robj *pattern;
} pubsubPattern;

client 屬性保存著訂閱模式的客戶端,而 pattern 屬性則保存著被訂閱的模式昔字。

每當(dāng)調(diào)用 PSUBSCRIBE 命令訂閱一個(gè)模式時(shí)爆袍, 程序就創(chuàng)建一個(gè)包含客戶端信息和被訂閱模式的 pubsubPattern 結(jié)構(gòu), 并將該結(jié)構(gòu)添加到 redisServer.pubsub_patterns 鏈表中作郭。

作為例子陨囊,下圖展示了一個(gè)包含兩個(gè)模式的 pubsub_patterns 鏈表, 其中 client123 和 client256 都正在訂閱 tweet.shop.* 模式:

如果這時(shí)客戶端 client10086 執(zhí)行 PSUBSCRIBE broadcast.list.* 夹攒, 那么 pubsub_patterns 鏈表將被更新成這樣:

通過遍歷整個(gè) pubsub_patterns 鏈表蜘醋,程序可以檢查所有正在被訂閱的模式,以及訂閱這些模式的客戶端咏尝。

發(fā)送信息到模式

發(fā)送信息到模式的工作也是由 PUBLISH 命令進(jìn)行的压语, 在前面講解頻道的時(shí)候, 我們給出了這樣一段偽代碼编检, 說它定義了 PUBLISH 命令的行為:

def PUBLISH(channel, message):

    # 遍歷所有訂閱頻道 channel 的客戶端
    for client in server.pubsub_channels[channel]:

        # 將信息發(fā)送給它們
        send_message(client, message)

但是胎食,這段偽代碼并沒有完整描述 PUBLISH 命令的行為, 因?yàn)?PUBLISH 除了將 message 發(fā)送到所有訂閱 channel 的客戶端之外允懂, 它還會(huì)將 channel 和 pubsub_patterns 中的模式進(jìn)行對比厕怜, 如果 channel 和某個(gè)模式匹配的話, 那么也將 message 發(fā)送到訂閱那個(gè)模式的客戶端蕾总。

完整描述 PUBLISH 功能的偽代碼定于如下:

def PUBLISH(channel, message):

    # 遍歷所有訂閱頻道 channel 的客戶端
    for client in server.pubsub_channels[channel]:

        # 將信息發(fā)送給它們
        send_message(client, message)

    # 取出所有模式酣倾,以及訂閱模式的客戶端
    for pattern, client in server.pubsub_patterns:

        # 如果 channel 和模式匹配
        if match(channel, pattern):

            # 那么也將信息發(fā)給訂閱這個(gè)模式的客戶端
            send_message(client, message)

舉個(gè)例子,如果 Redis 服務(wù)器的 pubsub_patterns 狀態(tài)如下:

那么當(dāng)某個(gè)客戶端發(fā)送信息 "Amazon Kindle, $69." 到 tweet.shop.kindle 頻道時(shí)谤专, 除了所有訂閱了 tweet.shop.kindle 頻道的客戶端會(huì)收到信息之外躁锡, 客戶端 client123 和 client256 也同樣會(huì)收到信息, 因?yàn)檫@兩個(gè)客戶端訂閱的 tweet.shop.* 模式和 tweet.shop.kindle 頻道匹配置侍。

退訂模式

使用 PUNSUBSCRIBE 命令可以退訂指定的模式映之, 這個(gè)命令執(zhí)行的是訂閱模式的反操作: 程序會(huì)刪除 redisServer.pubsub_patterns 鏈表中拦焚, 所有和被退訂模式相關(guān)聯(lián)的 pubsubPattern 結(jié)構(gòu), 這樣客戶端就不會(huì)再收到和模式相匹配的頻道發(fā)來的信息杠输。

小結(jié)

訂閱信息由服務(wù)器進(jìn)程維持的 redisServer.pubsub_channels 字典保存赎败,字典的鍵為被訂閱的頻道,字典的值為訂閱頻道的所有客戶端蠢甲。
當(dāng)有新消息發(fā)送到頻道時(shí)僵刮,程序遍歷頻道(鍵)所對應(yīng)的(值)所有客戶端,然后將消息發(fā)送到所有訂閱頻道的客戶端上鹦牛。
訂閱模式的信息由服務(wù)器進(jìn)程維持的 redisServer.pubsub_patterns 鏈表保存搞糕,鏈表的每個(gè)節(jié)點(diǎn)都保存著一個(gè) pubsubPattern 結(jié)構(gòu),結(jié)構(gòu)中保存著被訂閱的模式曼追,以及訂閱該模式的客戶端窍仰。程序通過遍歷鏈表來查找某個(gè)頻道是否和某個(gè)模式匹配。
當(dāng)有新消息發(fā)送到頻道時(shí)礼殊,除了訂閱頻道的客戶端會(huì)收到消息之外驹吮,所有訂閱了匹配頻道的模式的客戶端,也同樣會(huì)收到消息晶伦。
退訂頻道和退訂模式分別是訂閱頻道和訂閱模式的反操作碟狞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市婚陪,隨后出現(xiàn)的幾起案子篷就,更是在濱河造成了極大的恐慌,老刑警劉巖近忙,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異智润,居然都是意外死亡及舍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門窟绷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锯玛,“玉大人,你說我怎么就攤上這事兼蜈∪敛校” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵为狸,是天一觀的道長歼郭。 經(jīng)常有香客問我,道長辐棒,這世上最難降的妖魔是什么病曾? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任牍蜂,我火速辦了婚禮,結(jié)果婚禮上泰涂,老公的妹妹穿的比我還像新娘鲫竞。我一直安慰自己,他們只是感情好逼蒙,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布从绘。 她就那樣靜靜地躺著,像睡著了一般是牢。 火紅的嫁衣襯著肌膚如雪僵井。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天妖泄,我揣著相機(jī)與錄音驹沿,去河邊找鬼。 笑死蹈胡,一個(gè)胖子當(dāng)著我的面吹牛渊季,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播罚渐,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼却汉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了荷并?” 一聲冷哼從身側(cè)響起合砂,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎源织,沒想到半個(gè)月后翩伪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谈息,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年缘屹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侠仇。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轻姿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逻炊,到底是詐尸還是另有隱情互亮,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布余素,位于F島的核電站豹休,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏桨吊。R本人自食惡果不足惜慕爬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一窑眯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧医窿,春花似錦磅甩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至独榴,卻和暖如春僧叉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背棺榔。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工瓶堕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人症歇。 一個(gè)月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓郎笆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忘晤。 傳聞我的和親對象是個(gè)殘疾皇子宛蚓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容