redis--發(fā)布訂閱

[TOC]

1. 介紹

軟件架構(gòu)中腊尚,發(fā)布-訂閱是一種消息范式,消息的發(fā)送者(稱為發(fā)布者)不會將消息直接發(fā)送給特定的接收者(稱為訂閱者)褐捻。而是將發(fā)布的消息分為不同的類別痪宰,無需了解哪些訂閱者(如果有的話)可能存在。同樣的匆光,訂閱者可以表達對一個或多個類別的興趣套像,只接收感興趣的消息,無需了解哪些發(fā)布者(如果有的話)存在终息。

https://zh.wikipedia.org/wiki/%E5%8F%91%E5%B8%83/%E8%AE%A2%E9%98%85

image-20200720165238990

發(fā)布/訂閱的拓撲:

image.png

2. redis 實現(xiàn)

redis文檔你可以找到發(fā)布/訂閱

image-20200720165352065
image-20200720165556078

發(fā)布訂閱只有6條命令夺巩。

3. 命令

發(fā)布/訂閱的嗎,命令很少周崭,你可以在redis發(fā)布/訂閱命令查看

image-20200720165517577

4. 訂閱

命令:==SUBSCRIBE channel [channel1 ...]==

命令說明:當前線程訂閱channel柳譬,如果有其他線程向channel發(fā)布消息,那么我們的線程就能收到發(fā)布的消息续镇。==阻塞線程==

返回值:訂閱的channel數(shù)量美澳,還有一些提示信息。

image-20200720170834284

命令:==PSUBSCRIBE pattern [pattern1 ...]==

命令說明:當前線程訂閱指定模式的channel摸航,如果有其他線程向滿足模式的channel發(fā)布消息制跟,那么我們的線程就能收到發(fā)布的消息。==阻塞線程==

==在結(jié)合前面的例子中:訂閱了c:*的channel酱虎,在第一個用例中雨膨,存在c:test1,c:test2,此時c:*會收到2個channel的消息。==

返回值:訂閱的channel模式數(shù)量读串,還有一些提示信息聊记。

image-20200720172602104

命令:==UNSUBSCRIBE channel [channel1 ...]==

命令說明:取消訂閱channel撒妈。

命令:==PUNSUBSCRIBE pattern [pattern1 ...]==

命令說明:取消訂閱channel模式。

5. 發(fā)布

命令:==PUBLISH channel message==

命令說明:發(fā)布message到channel.

返回值:實際訂閱的線程數(shù)量甥雕。(收到消息的數(shù)量)

image-20200720172815937
image-20200720172922260

6. 狀態(tài)

命令:==PUBSUB CHANNELS [pattern]==

命令說明:列出全部活躍的channel或者符合pattern的channel.

channel活躍指:至少有一個訂閱者踩身。

返回值:活躍channel名稱胀茵。

image-20200720173658940

命令:==PUBSUB NUMSUB [channel1 ....]==

命令說明:獲取指定channel的訂閱個數(shù)社露。==如果不指定channel,返回空==

返回值:訂閱指定channel的數(shù)量(活躍的).==輸入channel模式為0==

image-20200720174040443

命令:==PUBSUB NUMPAT==

命令說明:獲取channel模板個數(shù)琼娘。

返回值:獲取channel模板個數(shù)峭弟。

image-20200720182712899

7. 作用域

發(fā)布/訂閱與key所在空間沒有關系,它不會受任何級別的干擾脱拼,包括不同數(shù)據(jù)庫編碼瞒瘸。 發(fā)布在db 10,訂閱可以在db 1。 如果你需要區(qū)分某些頻道熄浓,可以通過在頻道名稱前面加上所在環(huán)境的名稱(例如:測試環(huán)境情臭,演示環(huán)境,線上環(huán)境等)赌蔑。

8. 資料

官網(wǎng)書籍資料

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

Redis 的 SUBSCRIBE 命令可以讓客戶端訂閱任意數(shù)量的頻道俯在, 每當有新信息發(fā)送到被訂閱的頻道時, 信息就會被發(fā)送給所有訂閱指定頻道的客戶端娃惯。

作為例子跷乐, 下圖展示了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 趾浅、 client5client1 之間的關系:

[圖片上傳失敗...(image-76938a-1596192874374)]

當有新消息通過 PUBLISH 命令發(fā)送給頻道 channel1 時愕提, 這個消息就會被發(fā)送給訂閱它的三個客戶端:

[圖片上傳失敗...(image-794f29-1596192874374)]

在后面的內(nèi)容中, 我們將探討 SUBSCRIBEPUBLISH 命令的實現(xiàn)皿哨, 以及這套訂閱與發(fā)布機制的運作原理浅侨。

訂閱頻道

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

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

其中如输,字典的鍵為正在被訂閱的頻道, 而字典的值則是一個鏈表椎例, 鏈表中保存了所有訂閱這個頻道的客戶端挨决。

比如說,在下圖展示的這個 pubsub_channels 示例中订歪, client2 脖祈、 client5client1 就訂閱了 channel1 , 而其他頻道也分別被別的客戶端所訂閱:

[圖片上傳失敗...(image-7bbbc-1596192874374)]

當客戶端調(diào)用 SUBSCRIBE 命令時刷晋, 程序就將客戶端和要訂閱的頻道在 pubsub_channels 字典中關聯(lián)起來盖高。

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

[圖片上傳失敗...(image-ceebff-1596192874374)]

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

def SUBSCRIBE(client, channels):

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

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

通過 pubsub_channels 字典喻奥, 程序只要檢查某個頻道是否為字典的鍵席纽, 就可以知道該頻道是否正在被客戶端訂閱; 只要取出某個鍵的值撞蚕, 就可以得到所有訂閱該頻道的客戶端的信息润梯。

發(fā)送信息到頻道

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

比如說刀疙,對于以下這個 pubsub_channels 實例舶赔, 如果某個客戶端執(zhí)行命令 PUBLISH channel1 "hello moto" ,那么 client2 谦秧、 client5client1 三個客戶端都將接收到 "hello moto" 信息:

[圖片上傳失敗...(image-1948ef-1596192874374)]

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

def PUBLISH(channel, message):

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

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

退訂頻道

使用 UNSUBSCRIBE 命令可以退訂指定的頻道竟纳, 這個命令執(zhí)行的是訂閱的反操作: 它從 pubsub_channels 字典的給定頻道(鍵)中, 刪除關于當前客戶端的信息疚鲤, 這樣被退訂頻道的信息就不會再發(fā)送給這個客戶端锥累。

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

當使用 PUBLISH 命令發(fā)送信息到某個頻道時, 不僅所有訂閱該頻道的客戶端會收到信息石咬, 如果有某個/某些模式和這個頻道匹配的話揩悄, 那么所有訂閱這個/這些頻道的客戶端也同樣會收到信息。

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

[圖片上傳失敗...(image-e75ebe-1596192874374)]

當有信息發(fā)送到 tweet.shop.kindle 頻道時, 信息除了發(fā)送給 clientXclientY 之外焕窝, 還會發(fā)送給訂閱 tweet.shop.* 模式的 client123client256

[圖片上傳失敗...(image-9e6cc8-1596192874374)]

另一方面蹬挺, 如果接收到信息的是頻道 tweet.shop.ipad , 那么 client123client256 同樣會收到信息:

[圖片上傳失敗...(image-ae1877-1596192874374)]

訂閱模式

redisServer.pubsub_patterns 屬性是一個鏈表它掂,鏈表中保存著所有和模式相關的信息:

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

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

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

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

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

作為例子客给,下圖展示了一個包含兩個模式的 pubsub_patterns 鏈表用押, 其中 client123client256 都正在訂閱 tweet.shop.* 模式:

[圖片上傳失敗...(image-daa4ed-1596192874374)]

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

[圖片上傳失敗...(image-4c438e-1596192874374)]

通過遍歷整個 pubsub_patterns 鏈表靶剑,程序可以檢查所有正在被訂閱的模式蜻拨,以及訂閱這些模式的客戶端池充。

發(fā)送信息到模式

發(fā)送信息到模式的工作也是由 PUBLISH 命令進行的, 在前面講解頻道的時候缎讼, 我們給出了這樣一段偽代碼收夸, 說它定義了 PUBLISH 命令的行為:

def PUBLISH(channel, message):

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

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

但是,這段偽代碼并沒有完整描述 PUBLISH 命令的行為血崭, 因為 PUBLISH 除了將 message 發(fā)送到所有訂閱 channel 的客戶端之外卧惜, 它還會將 channelpubsub_patterns 中的模式進行對比, 如果 channel 和某個模式匹配的話功氨, 那么也將 message 發(fā)送到訂閱那個模式的客戶端序苏。

完整描述 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ā)給訂閱這個模式的客戶端
            send_message(client, message)

舉個例子捷凄,如果 Redis 服務器的 pubsub_patterns 狀態(tài)如下:

[圖片上傳失敗...(image-eccf4c-1596192874374)]

那么當某個客戶端發(fā)送信息 "Amazon Kindle, $69."tweet.shop.kindle 頻道時, 除了所有訂閱了 tweet.shop.kindle 頻道的客戶端會收到信息之外围来, 客戶端 client123client256 也同樣會收到信息跺涤, 因為這兩個客戶端訂閱的 tweet.shop.* 模式和 tweet.shop.kindle 頻道匹配。

退訂模式

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末院刁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子粪狼,更是在濱河造成了極大的恐慌退腥,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件再榄,死亡現(xiàn)場離奇詭異狡刘,居然都是意外死亡,警方通過查閱死者的電腦和手機困鸥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門嗅蔬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人疾就,你說我怎么就攤上這事澜术。” “怎么了猬腰?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵鸟废,是天一觀的道長。 經(jīng)常有香客問我漆诽,道長侮攀,這世上最難降的妖魔是什么锣枝? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮兰英,結(jié)果婚禮上撇叁,老公的妹妹穿的比我還像新娘。我一直安慰自己畦贸,他們只是感情好陨闹,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著薄坏,像睡著了一般趋厉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胶坠,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天君账,我揣著相機與錄音,去河邊找鬼沈善。 笑死乡数,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的闻牡。 我是一名探鬼主播净赴,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼罩润!你這毒婦竟也來了玖翅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤割以,失蹤者是張志新(化名)和其女友劉穎金度,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拳球,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡审姓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了祝峻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魔吐。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖莱找,靈堂內(nèi)的尸體忽然破棺而出酬姆,到底是詐尸還是另有隱情,我是刑警寧澤奥溺,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布辞色,位于F島的核電站,受9級特大地震影響浮定,放射性物質(zhì)發(fā)生泄漏相满。R本人自食惡果不足惜层亿,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望立美。 院中可真熱鬧匿又,春花似錦、人聲如沸建蹄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽洞慎。三九已至痛单,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間劲腿,已是汗流浹背旭绒。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谆棱,地道東北人快压。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像垃瞧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坪郭,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355