Redis實現消息隊列&發布/訂閱模式使用


  • Redis的列表類型鍵可以用來實現隊列,並且支持阻塞式讀取,可以很容易的實現一個高性能的優先隊列。
  • 同時在更高層面上,Redis還支持"發布/訂閱"的消息模式,可以基於此構建一個聊天系統。

redis的列表類型天生支持用作消息隊列(類似於MQ的隊列模型--任何時候都可以消費,一條消息只能消費一次)

  在Redis中,List類型是按照插入順序排序的字符串鏈表。和數據結構中的普通鏈表一樣,我們可以在其頭部(left)和尾部(right)添加新的元素。在插入時,如果該鍵並不存在,Redis將為該鍵創建一個新的鏈表。與此相反,如果鏈表中所有的元素均被移除,那么該鍵也將會被從數據庫中刪除。List中可以包含的最大元素數量是4294967295。
      從元素插入和刪除的效率視角來看,如果我們是在鏈表的兩頭插入或刪除元素,這將會是非常高效的操作,即使鏈表中已經存儲了百萬條記錄,該操作也可以在常量時間內完成。然而需要說明的是,如果元素插入或刪除操作是作用於鏈表中間,那將會是非常低效的。

  redis中簡單的操作list,簡單的在命令行操作實現隊列

    (1)從左向右插入,從右向左彈出:

    執行完   lpush mylist a b c d  之后數據結構如下:(滿足先進先出的隊列模式)

    

    執行完第一次:rpop mylist之后數據結構如下:

    

 

    (2)從右向左插入,從左向右彈出:

    執行完:rpush mylist2 a b c d之后的數據結構如下

    

    第一次執行完   lpop mylist2  之后數據結構如下:(滿足先進先出的隊列模式)

    

    但上述例子中消息消費者有一個問題存在,即需要不停的調用rpop方法查看List中是否有待處理消息。每調用一次都會發起一次連接,這會造成不必要的浪費。

    也就是上面的操作需要一直調用rpop命令或者lpop命令才可以實現不停的監聽且消費消息。為了解決這一問題,redis提供了阻塞命令 brpop和blpop。

  補充:brpop和blpop實現阻塞讀取(重要)

     brpop命令可以接收多個鍵,其完整的命令格式為 BRPOP key [key ...] timeout,如:brpop key1 0。意義是同時檢測多個鍵,如果所有鍵都沒有元素則阻塞,如果其中一個有元素則從該鍵中彈出該元素(會按照key的順序進行讀取,可以實現具有優先級的隊列)

    執行brpop命令:(會返回讀取的key和value,第一個是返回的key,第二個是value)

      lpush queue1 1 2

      lpush queue2 3 4

      127.0.0.1:6379> brpop queue1 queue2 2

      1) "queue1"
      2) "1"

    也就是brpop會阻塞隊列,並且每次也是彈出一個消息,如果沒有消息會阻塞。當彈出一個元素之后就會解除阻塞

    如果多個鍵都有元素則按照從左到右讀取第一個鍵中的一個元素,借此特性可以實現區分優先級的任務隊列

  補充:B|RPOPLPUSH source destination timeout

  原子性的從與source鍵關聯的鏈表尾部彈出一個元素,同時再將彈出的元素插入到與destination鍵關聯的鏈表的頭部。如果source鍵不存在,該命令將返回nil,同時不再做任何其它的操作了。如果source和destination是同一個鍵,則相當於原子性的將其關聯鏈表中的尾部元素移到該鏈表的頭部。如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素為止,返回彈出和插入的元素。

  Redis鏈表經常會被用於消息隊列的服務,以完成多程序之間的消息交換。假設一個應用程序正在執行LPUSH操作向鏈表中添加新的元素,我們通常將這樣的程序稱之為"生產者(Producer)",而另外一個應用程序正在執行B|RPOP操作從鏈表中取出元素,我們稱這樣的程序為"消費者(Consumer)"。如果此時,消費者程序在取出消息元素后立刻崩潰,由於該消息已經被取出且沒有被正常處理,那么我們就可以認為該消息已經丟失,由此可能會導致業務數據丟失,或業務狀態的不一致等現象的發生。然而通過使用B|RPOPLPUSH命令,消費者程序在從主消息隊列中取出消息之后再將其插入到備份隊列中,直到消費者程序完成正常的處理邏輯后再將該消息從備份隊列中刪除。同時我們還可以提供一個守護進程,當發現備份隊列中的消息過期時,可以重新將其再放回到主消息隊列中,以便其它的消費者程序繼續處理。

 

發布/訂閱模式(類似於MQ的主題模式-只能消費訂閱之后發布的消息,一個消息可以被多個訂閱者消費)

  Redis 發布訂閱(pub/sub)是一種消息通信模式:發送者(pub)發送消息,訂閱者(sub)接收消息。Redis 客戶端可以訂閱任意數量的頻道。

  "發布/訂閱"模式包含兩種角色,分別是發布者和訂閱者。訂閱者可以訂閱一個或者多個頻道(channel),而發布者可以向指定的頻道(channel)發送消息,所有訂閱此頻道的訂閱者都會收到此消息。

  圖展示了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 、 client5 和 client1 之間的關系:

  

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

  

  (1)發布消息

  發布者發布消息的命令是  publish,用法是 publish channel message,如向 channel1.1說一聲hi

127.0.0.1:6379> publish channel:1 hi
(integer) 0

  這樣消息就發出去了。返回值表示接收這條消息的訂閱者數量。發出去的消息不會被持久化,也就是有客戶端訂閱channel:1后只能接收到后續發布到該頻道的消息,之前的就接收不到了。

  (2)訂閱頻道

  訂閱頻道的命令是 subscribe,可以同時訂閱多個頻道,用法是 subscribe channel1 [channel2 ...],例如新開一個客戶端訂閱上面頻道:(不會收到消息,因為不會收到訂閱之前就發布到該頻道的消息)

127.0.0.1:6379> subscribe channel:1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:1"
3) (integer) 1

  執行上面命令客戶端會進入訂閱狀態,處於此狀態下客戶端不能使用除subscribe、unsubscribe、psubscribe和punsubscribe這四個屬於"發布/訂閱"之外的命令,否則會報錯。

  進入訂閱狀態后客戶端可能收到3種類型的回復。每種類型的回復都包含3個值,第一個值是消息的類型,根據消類型的不同,第二個和第三個參數的含義可能不同。

  消息類型的取值可能是以下3個:

  • subscribe:表示訂閱成功的反饋信息。第二個值是訂閱成功的頻道名稱,第三個是當前客戶端訂閱的頻道數量。
  • message:表示接收到的消息,第二個值表示產生消息的頻道名稱,第三個值是消息的內容。
  • unsubscribe:表示成功取消訂閱某個頻道。第二個值是對應的頻道名稱,第三個值是當前客戶端訂閱的頻道數量,當此值為0時客戶端會退出訂閱狀態,之后就可以執行其他非"發布/訂閱"模式的命令了。

  除了可以使用subscribe命令訂閱指定的頻道外,還可以使用psubscribe命令訂閱指定的規則。規則支持通配符格式。

  命令格式為:psubscribe pattern [pattern ...] 訂閱多個模式的頻道。如: psubscribe c? b* d?*

  通配符中?表示1個占位符,*表示任意個占位符(包括0),?*表示1個以上占位符。

  注意:

  (1)使用psubscribe命令可以重復訂閱同一個頻道,如客戶端執行了psubscribe c? c?*。這時向c1發布消息客戶端會接受到兩條消息,而同時publish命令的返回值是2而不是。.同樣的,如果有另一個客戶端執行了subscribe c1 和psubscribe c?*的話,向c1發送一條消息該客戶頓也會受到兩條消息(但是是兩種類型:message和pmessage),同時publish命令也返回2.

  (2)punsubscribe命令可以退訂指定的規則,用法是: punsubscribe [pattern [pattern ...]],如果沒有參數則會退訂所有規則。

  (3)使用punsubscribe只能退訂通過psubscribe命令訂閱的規則,不會影響直接通過subscribe命令訂閱的頻道;同樣unsubscribe命令也不會影響通過psubscribe命令訂閱的規則。另外需要注意punsubscribe命令退訂某個規則時不會將其中的通配符展開,而是進行嚴格的字符串匹配,所以punsubscribe * 無法退訂c*規則,而是必須使用punsubscribe c*才可以退訂。

至此實現了兩種方式的消息隊列:

  redis自帶的list類型(lpush和rpop或者brpop,rpush和lpop或者blpop)---blpop和brpop是阻塞讀取。

  "發布/訂閱"模式(publish channel message 和 subscribe channel [channel ...] 或者 psubscribe pattern [pattern ...] 通配符訂閱多個頻道)

1.發布訂閱執行訂閱之后該線程處於阻塞狀態,線程不會終止,如果終止線程需要退訂,unsubscribe

2.BRPOP:當給定列表內沒有任何元素可供彈出的時候,連接將被BRPOP命令阻塞,直到等待超時或發現可彈出元素為止。(每次只彈出一個元素,當沒有元素的時候處於阻塞,當彈出一個元素之后就會解除阻塞)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM