redis的發布訂閱模式

來源:互聯網
上載者:User

標籤:

概要redis的每個server執行個體都維護著一個儲存伺服器狀態的redisServer結構struct redisServer{    /* Pubsub */    // 字典,鍵為頻道,值為鏈表    // 鏈表中儲存了所有訂閱某個頻道的用戶端    // 新用戶端總是被添加到鏈表的表尾    dict *pubsub_channels;  /* Map channels to list of subscribed clients */    // 這個鏈表記錄了客訂閱的所有模式的名字    list *pubsub_patterns;  /* A list of pubsub_patterns */};pubsub_channels記錄了所有客訂閱的頻道的資訊。 redis的發布訂閱模式

 

 

訂閱者通過sub命令訂閱頻道,server使用pub命令把訊息推送到合格pubsub_channels中。

 

內部資料結構pubsub_channels是一個字典結構,字典內部使用hash表格儲存體和索引資料。 實現字典的可選資料結構     hash表:簡單但是不穩定的基於數組的衝突解決法;簡單且平均效率穩定的鏈式地址的衝突解決法;     字典樹:使用樹結構。redis採用hash的鏈式地址法作為實現,好處是直觀、簡單、可期待平均時間複雜度較小且平穩。typedef struct dict {    // 類型特定函數    dictType *type;    // 私人資料    void *privdata;    // 雜湊表    dictht ht[2];    // rehash 索引    // 當 rehash 不在進行時,值為 -1    int rehashidx; /* rehashing not in progress if rehashidx == -1 */    // 目前正在啟動並執行安全迭代器的數量    int iterators; /* number of iterators currently running */} dict;dict使用兩個hash表進行索引,當進行rehash的時候使用ht[1]的hash表,其他時候都使用hd[0]的hash表。使用的hash表的定義如下typedef struct dictht {    // 雜湊表數組    dictEntry **table;    // 雜湊表大小    unsigned long size;    // 雜湊表大小掩碼,用於計算索引值    // 總是等於 size - 1    unsigned long sizemask;    // 該雜湊表已有節點的數量    unsigned long used;} dictht;table是一個儲存dictEntry*指標的數組,table中的每一項都是一個指向DictEntry結構的指標,同時每一項也都帶有一個指向下一項的指標。可以看出這是一個使用鏈地址法解決衝突的hash結構。dictEntry的定義,包含key、value、next。/* * 雜湊表節點 */typedef struct dictEntry {    // 鍵    void *key;    // 值    union {        void *val;        uint64_t u64;        int64_t s64;    } v;    // 指向下個雜湊表節點,形成鏈表    struct dictEntry *next;} dictEntry; 客訂閱頻道就是字典中插入新元素的過程        // 關聯        // {        //  頻道名        訂閱頻道的用戶端        //  ‘channel-a‘ : [c1, c2, c3],        //  ‘channel-b‘ : [c5, c2, c1],        //  ‘channel-c‘ : [c10, c2, c1]        // }        /* Add the client to the channel -> list of clients hash table */        // 從 pubsub_channels 字典中取出儲存著所有訂閱了 channel 的用戶端的鏈表        // 如果 channel 不存在於字典,那麼添加進去        de = dictFind(server.pubsub_channels,channel);        if (de == NULL) {            clients = listCreate();            dictAdd(server.pubsub_channels,channel,clients);            incrRefCount(channel);        } else {            clients = dictGetVal(de);        }        // before:        // ‘channel‘ : [c1, c2]        // after:        // ‘channel‘ : [c1, c2, c3]        // 將用戶端添加到鏈表的末尾        listAddNodeTail(clients,c);1.查詢server是否包含指定頻道2.如果頻道存在就擷取頻道指向的list地址;如果頻道不存在,就建立頻道和list3.使用步驟2的list,將用戶端添加到list的末尾。完成這三步以後,server維護的發布訂閱頻道就新增了一個頻道和關注的用戶端,server發布時檢測發布訂閱字典,擷取訂閱用戶端並依次發送。 server發布訊息到channel/* Send to clients listening for that channel */    // 取出包含所有訂閱頻道 channel 的用戶端的鏈表    // 並將訊息發送給它們    de = dictFind(server.pubsub_channels,channel);    if (de) {        list *list = dictGetVal(de);        listNode *ln;        listIter li;        // 遍曆用戶端鏈表,將 message 發送給它們        listRewind(list,&li);        while ((ln = listNext(&li)) != NULL) {            redisClient *c = ln->value;            // 回複用戶端。            // 樣本:            // 1) "message"            // 2) "xxx"            // 3) "hello"            addReply(c,shared.mbulkhdr[3]);            // "message" 字串            addReply(c,shared.messagebulk);            // 訊息的來源頻道            addReplyBulk(c,channel);            // 訊息內容            addReplyBulk(c,message);            // 接收用戶端計數            receivers++;        }    }結合訂閱者訂閱的過程,發布過程就是一個尋找訂閱者並輪詢發送訊息給訂閱者的過程。

redis的發布訂閱模式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.