原文鏈接:Redis實現消息隊列的方案
Redis作為內存中的數據結構存儲,常用作數據庫、緩存和消息代理。它支持數據結構,如 字符串,散列,列表,集合,帶有范圍查詢的排序集(sorted sets),位圖(bitmaps),超級日志(hyperloglogs),具有半徑查詢和流的地理空間索引。Redis具有內置復制,Lua腳本,LRU驅逐,事務和不同級別的磁盤持久性,並通過Redis Sentinel和Redis Cluster自動分區。
為了實現其出色的性能,Redis使用內存數據集(in-memory dataset)。
MQ應用有很多,比如ActiveMQ,RabbitMQ,Kafka等,但是也可以基於redis來實現,可以降低系統的維護成本和實現復雜度,本篇介紹redis中實現消息隊列的幾種方案。
- 基於List的 LPUSH+BRPOP 的實現
- PUB/SUB,訂閱/發布模式
- 基於Sorted-Set的實現
- 基於Stream類型的實現
基於異步消息隊列List lpush-brpop(rpush-blpop)
使用rpush和lpush操作入隊列,lpop和rpop操作出隊列。
List支持多個生產者和消費者並發進出消息,每個消費者拿到都是不同的列表元素。
但是當隊列為空時,lpop和rpop會一直空輪訓,消耗資源;所以引入阻塞讀blpop和brpop(b代表blocking),阻塞讀在隊列沒有數據的時候進入休眠狀態,
一旦數據到來則立刻醒過來,消息延遲幾乎為零。
注意
你以為上面的方案很完美?還有個問題需要解決:空閑連接的問題。
如果線程一直阻塞在那里,Redis客戶端的連接就成了閑置連接,閑置過久,服務器一般會主動斷開連接,減少閑置資源占用,這個時候blpop和brpop或拋出異常,
所以在編寫客戶端消費者的時候要小心,如果捕獲到異常,還有重試。
缺點:
- 做消費者確認ACK麻煩,不能保證消費者消費消息后是否成功處理的問題(宕機或處理異常等),通常需要維護一個Pending列表,保證消息處理確認。
- 不能做廣播模式,如pub/sub,消息發布/訂閱模型
- 不能重復消費,一旦消費就會被刪除
- 不支持分組消費
如何實現:Redis應用-異步消息隊列與延時隊列
PUB/SUB,訂閱/發布模式
SUBSCRIBE,用於訂閱信道
PUBLISH,向信道發送消息
UNSUBSCRIBE,取消訂閱
此模式允許生產者只生產一次消息,由中間件負責將消息復制到多個消息隊列,每個消息隊列由對應的消費組消費。
優點
典型的廣播模式,一個消息可以發布到多個消費者
多信道訂閱,消費者可以同時訂閱多個信道,從而接收多類消息
消息即時發送,消息不用等待消費者讀取,消費者會自動接收到信道發布的消息
缺點
消息一旦發布,不能接收。換句話就是發布時若客戶端不在線,則消息丟失,不能尋回
不能保證每個消費者接收的時間是一致的
若消費者客戶端出現消息積壓,到一定程度,會被強制斷開,導致消息意外丟失。通常發生在消息的生產遠大於消費速度時
可見,Pub/Sub 模式不適合做消息存儲,消息積壓類的業務,而是擅長處理廣播,即時通訊,即時反饋的業務。
基於Sorted-Set的實現
Sortes Set(有序列表),類似於java的SortedSet和HashMap的結合體,一方面她是一個set,保證內部value的唯一性,另一方面它可以給每個value賦予一個score,代表這個value的
排序權重。內部實現是“跳躍表”。
有序集合的方案是在自己確定消息順ID時比較常用,使用集合成員的Score來作為消息ID,保證順序,還可以保證消息ID的單調遞增。通常可以使用時間戳+序號的方案。確保了消息ID的單調遞增,利用SortedSet的依據
Score排序的特征,就可以制作一個有序的消息隊列了。
優點
就是可以自定義消息ID,在消息ID有意義時,比較重要。
缺點
缺點也明顯,不允許重復消息(因為是集合),同時消息ID確定有錯誤會導致消息的順序出錯。
基於Stream類型的實現
剩下的東西有待補充