redis原始碼分析之發布訂閱(pub sub)

2022-08-09 03:42:12 字數 3862 閱讀 5766

redis算是快取界的老大哥了,最近做的事情對redis依賴較多,使用了裡面的發布訂閱功能,事務功能以及sortedset等資料結構,後面準備好好學習總結一下redis的一些知識點。

先看下redis發布訂閱的結構:

其中發布者跟訂閱者之間通過channel進行互動,channel分為兩種模式。

redis中為發布訂閱(pub/sub)功能提供了六個命令,分為兩種模式。

由subscribe,unsubscribe組成,它們是負責訂閱有確定名稱的channel,例如subscribe test表示訂閱名字為test的channel。

由psubscribe,punsubscribe組成,是負責訂閱模糊名字的channel,例如psubscribe test* 表示訂閱所有以test開頭的channel。

最後再加上發布命令publish以及檢視訂閱相關資訊的pubsub命令組成。

redis所有的命令及其處理函式都放在了server.c檔案的開頭,從其中找出發布訂閱功能相關的命令資訊。

,,,,,,

這裡可以看出建立一條命令需要很多引數,我們這裡只需要關注前兩個引數,第乙個引數表示命令的內容,第二個表示該命令對應的處理函式。

普通模式訂閱subscribe函式:

該命令支援多個引數,即subscribe channel1,channel2...

void subscribecommand(client *c)
在server.c檔案中,processcommand函式是在呼叫具體命令函式之前的判斷邏輯,其中有一段:

/* only allow subscribe and unsubscribe in the context of pub/sub */

if (c->flags & client_pubsub &&

c->cmd->proc != pingcommand &&

c->cmd->proc != subscribecommand &&

c->cmd->proc != unsubscribecommand &&

c->cmd->proc != psubscribecommand &&

c->cmd->proc != punsubscribecommand)

這裡注釋也寫的很清楚,就是當client處於pub/sub上下文時,只接收訂閱相關命令以及乙個ping命令,這就解釋了上面subscribecommand函式中為什麼要設定客戶端flag欄位。

接下來看下訂閱的具體邏輯:

int pubsubsubscribechannel(client *c, robj *channel)  else 

//把client加入到該channel的訂閱列表中

listaddnodetail(clients,c);

}//一系列通知客戶端的操作

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

addreply(c,shared.subscribebulk);

addreplybulk(c,channel);

addreplylonglong(c,clientsubscriptionscount(c));

return retval;

}

總結一下,訂閱其實就是把指定channel分別加入到client跟server的pub/sub雜湊表中,然後在server端儲存訂閱了該channle的所有client列表,如下圖:

下面看一下publish發布命令:

例如:publish channelname msg

void publishcommand(client *c)
重點看下發布函式的原始碼:

int pubsubpublishmessage(robj *channel, robj *message) 

}//開始模糊匹配的邏輯處理,模糊模式使用的是鍊錶而不是雜湊表,後面會講

if (listlength(server.pubsub_patterns))

}decrrefcount(channel);

}return receivers;

}

從上面的publish處理函式可以看出每次進行訊息發布的時候,都會向普通模式跟模糊模式發布訊息,同時也能看出普通模式跟模糊模式使用的是兩種不同的資料結構,下面看下模糊訂閱模式。

模糊模式訂閱psubscribe函式:

//psubscribe命令對應的處理函式

void psubscribecommand(client *c)

int pubsubsubscribepattern(client *c, robj *pattern)

//通知客戶端

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

addreply(c,shared.psubscribebulk);

addreplybulk(c,pattern);

addreplylonglong(c,clientsubscriptionscount(c));

return retval;

}

通過分析上面的原始碼可以總結一下模糊訂閱中的資料結構,如下圖:

注:正如上面提到的,模糊模式中,乙個pat物件中包含乙個pattern規則跟乙個client指標,也就是說當多個client模糊訂閱同乙個pattern時同樣會為每個client都建立乙個節點。

普通模式取消訂閱unsubscribe函式:

取消就相對簡單了,說白了就是把上面鎖儲存在server跟client端的資料刪除。

取消訂閱入口

void unsubscribecommand(client *c) else

//如果channel被全部取消,則修改client狀態,這樣client就可以傳送其他命令了

if (clientsubscriptionscount(c) == 0) c->flags &= ~client_pubsub;

}//一次性取消訂閱所有channel

int pubsubunsubscribeallchannels(client *c, int notify)

//如果client上面都沒有訂閱,依然返回響應

if (notify && count == 0)

//釋放空間

dictreleaseiterator(di);

return count;

}//取消訂閱指定channel

int pubsubunsubscribechannel(client *c, robj *channel, int notify)

}//返回client響應

if (notify)

//引用計數-1

decrrefcount(channel);

return retval;

}

由於模糊模式的取消訂閱與普通模式類似,這裡就不再貼**了。

整個發布訂閱的**比較簡單清晰,乙個值得思考的問題時普通模式跟模糊模式中分別使用了雜湊表跟鍊錶兩種結構進行處理,而不是統一的,原因在於模糊模式不能精確匹配,需要遍歷挨個判斷,而雜湊表的優勢在於快速定位查詢,在需要遍歷跟模糊匹配的場景中並不適用。

Redis發布與訂閱以及日常訪問原始碼分享

需要jar包 commons pool2 2.4.2 jedis 2.9.0 發布與訂閱 訂閱者 先啟動訂閱後,才能獲取發布者發布的資訊 public class redis subscribe jedis.subscribe jedispubsub,jredischat 發布者 public cl...

Redis之發布 訂閱機制

相關命令 publish 發布 subscribe 訂閱 psubscribe 一種訂閱符合給定模式的所有頻道的方法 unsubscribe 退訂 punsubscribe 退訂乙個訂閱的模式這些命令被廣泛用於構建即時通訊應用,比如網路聊天室 chatroom 和實時廣播 實時提醒等。redis相關...

redis之訊息訂閱發布

傳送訊息到指定的channel通道 subscribe 訂閱乙個或者多個通道 unsubscribe 取消訂閱指定的通道 psubscribe 訂閱乙個或多個符合給定模式的通道 punsubscribe 取消訂閱乙個或多個符合給定模式的通道 pubsub 檢視訂閱發布的狀態 下面通過一些簡答的示例說...