redis的pub/sub機制
一個Redis client釋出訊息,其他多個redis client訂閱訊息,釋出的訊息“即發即失”,redis不會持久儲存釋出的訊息;訊息訂閱者也將只能得到訂閱之後的訊息,通道中此前的訊息將無從獲得。
訊息釋出者,即publish客戶端,無需獨佔連結,你可以在publish訊息的同時,使用同一個redis-client連結進行其他操作(例如:INCR等) 訊息訂閱者,即subscribe客戶端,需要獨佔連結,即進行subscribe期間,redis-client無法穿插其他操作, 此時client以阻塞的方式等待“publish端”的訊息;因此這裡subscribe端需要使用單獨的連結,甚至需要在額外的執行緒中使用。 Tcp預設連線時間固定,如果在這時間內sub端沒有接收到pub端訊息,或pub端沒有訊息產生,sub端的連線都會被強制回收, 這裡就需要使用特殊手段解決,用定時器來模擬pub和sub之間的保活機制,定時器時間不能超過TCP最大連線時間,具體根據機器環境來定;
一旦subscribe端斷開連結,將會失去部分訊息,即連結失效期間的訊息將會丟失,所以這裡就需要考慮到藉助redis的list來持久化;
總結:pub釋出的訊息不會持久化,sub是阻塞等待訊息,只能獲取訂閱之後的產生的訊息,一段時間內sub沒有收到訊息或pub沒有生產訊息,sub連線會被回收(因為sub是阻塞的).
如果你非常關注每個訊息,那麼你應該基於Redis做一些額外的補充工作,如果你期望訂閱是持久的,那麼如下的設計思路可以借鑑:
1) subscribe端: 首先向一個Set集合中增加“訂閱者ID”, 此Set集合儲存了“活躍訂閱”者,訂閱者ID標記每個唯一的訂閱者,此Set為 "活躍訂閱者集合"
2) subcribe端開啟訂閱操作,並基於Redis建立一個以 "訂閱者ID" 為KEY的LIST資料結構,此LIST中儲存了所有的尚未消費的訊息,此List稱為 "訂閱者訊息佇列" 3) publish端: 每釋出一條訊息之後,publish端都需要遍歷 "活躍訂閱者集合",並依次向每個 "訂閱者訊息佇列" 尾部追加此次釋出的訊息. 4) 到此為止,我們可以基本保證,釋出的每一條訊息,都會持久儲存在每個 "訂閱者訊息佇列" 中. 5) subscribe端,每收到一個訂閱訊息,在消費之後,必須刪除自己的 "訂閱者訊息佇列" 頭部的一條記錄. 6) subscribe端啟動時,如果發現自己的 "訂閱者訊息佇列" 有殘存記錄, 那麼將會首先消費這些記錄,然後再去訂閱.
以上方法可以保證成功到達的訊息必消費不丟失;