RabbitMQ實戰:可用性分析和實現

本系列是「RabbitMQ實戰:高效部署分佈式消息隊列」書籍的總結筆記。安全

上一篇介紹了各類場景下的最佳實踐,大部分場景可使用「發後即忘」的模式,不須要響應,若是須要響應,可使用RabbitMQ的RPC模型。服務器

RabbitMQ以異步的方式解耦系統間的關係,調用者將業務請求發送到Rabbit服務器,就能夠返回了,Rabbit會確保請求被正確處理,即便遇到網絡異常、Rabbit服務器崩潰、整個機房斷電等特殊場景,針對這些場景,Rabbit提供了各類機制確保其可用性。微信

本篇經過總結可能出現的特殊場景,對Rabbit提供的可用性保證進行分析,學習它的實現方式,你會了解到:網絡

  • 總結異常場景
  • 集羣並處理失敗
  • 鏈接丟失和故障轉移
  • 主/備方式
  • 跨機房複製

推廣下個人我的公衆號「情情說」,第一時間分享個人工做、學習和生活,若是對你有幫助,但願能夠關注下。架構

異常場景

在實際工做中,有很大一部分時間用在解決各類異常狀況,好比針對用戶輸入的驗證,JDK中提供的各類異常類,網絡異常等,這些相對來講比較好解決。負載均衡

Rabbit服務做爲調用者和處理者的橋樑,相當重要,若是由於網絡異常、單臺服務器崩潰、機房癱瘓等緣由致使Rabbit服務不可用,會影響全部依賴的業務系統。異步

網絡異常

處理者和服務端是經過長鏈接交互的,這樣能夠將消息實時推送,網絡異常可能會致使長鏈接斷開,若是客戶端沒法感知,處理者將接收不到任何消息,這種狀況稱爲「鏈接丟失」。分佈式

經過捕獲鏈接異常,進行重連,能夠解決這種問題,另外,Rabbit客戶端進行了封裝,很容易處理這種問題。性能

服務器崩潰

若是隻有一臺服務器服務,服務器崩潰將致使服務不可用,通常會使用集羣將多個服務器當作一個總體對外提供服務,這樣,單臺服務器崩潰不會影響總體的服務。學習

使用集羣后,就要考慮一些問題:

  • 客戶端鏈接到哪臺服務器是隨機的,而一個隊列只會在某個服務器中,因此,每臺服務器都要保存隊列元數據(相似索引),而且可從其餘服務器獲取實際的隊列數據;
  • 服務器崩潰,會致使非持久化的隊列、交換器丟失,客戶端端重連後,要再次進行建立,但未消費的消息將沒法恢復;
  • 若是隊列、交換器、消息等是持久化的,如何進行恢復呢,Rabbit提供了幾種方式進行處理,後面會詳細介紹;
  • 訂閱者也須要從新創建鏈接,進行監聽;
機房癱瘓

若是考慮機房癱瘓,就要建多個數據中心,RabbitMQ提供了一種機制,能夠方便地在不一樣數據中心的Rabbit間複製消息。

集羣並處理失敗

RabbitMQ最優秀的功能之一就是其內建集羣,主要用於完成2個目標:

  • 容許消費者和生產者在Rabbit節點崩潰的狀況下繼續運行;
  • 經過添加更多的節點線性擴展消息通訊的吞吐量;
集羣架構

RabbitMQ會始終記錄四種類型的內部元數據(相似索引):

  • 隊列元數據:隊列名稱和它的屬性;
  • 交換器元數據:交換器名稱、類型和屬性;
  • 綁定元數據:一張簡單的表格展現瞭如何將消息路由到隊列;
  • vhost元數據:爲vhost內的隊列、交換器和綁定提供命名空間和安全屬性;

當引入集羣時,就須要追蹤新的元數據類型:集羣節點位置,以及節點與已記錄的其餘類型元數據的關係。

不是每一個節點都有全部隊列的徹底拷貝,若是在集羣中建立隊列,只會在單個節點上建立完整的隊列信息(元數據、狀態、內容),全部其餘節點只知道隊列的元數據和指向該隊列的節點指針。

若是節點崩潰了,附加在隊列上的消費者也就沒法接收新的消息了。可讓消費者重連到集羣並從新建立隊列,這種作法僅當隊列沒設置持久化時纔可行,這是爲了確保當失敗的節點恢復後加入集羣,節點上的隊列消息不會丟失。

爲何不將隊列內容和狀態複製到全部節點:第一,存儲空間,若是每一個集羣節點都擁有全部隊列的徹底拷貝,添加新節點不會帶來更多存儲空間;第二,性能,消息的發佈者須要將消息複製到每個集羣節點,對於持久化消息,網絡和磁盤複製都會增長。

而交換器只是一張查詢表,而非實際的消息路由器,所以將交換器在整個集羣中進行復制會更加簡單

能夠把每一個隊列想象成節點上運行的進程,每一個進程擁有本身的進程ID,交換器只是路由模式列表和匹配消息應發往的隊列進程ID列表。

集羣架構中的交換器和隊列

每一個Rabbit節點,要麼是內存節點,要麼是磁盤節點,單節點系統只運行磁盤類型的節點,在集羣中,能夠選擇配置部分節點爲內存節點。

在集羣中聲明隊列、交換器或綁定的時候,這些操做直到全部集羣節點都成功提交元數據變動後才返回。

RabbitMQ只要求集羣中至少有一個磁盤節點,若是隻有一個磁盤節點,恰好又崩潰了,集羣能夠繼續路由消息,但不能建立隊列、交換器、綁定、添加用戶、更改權限等操做。因此,建議設置兩個磁盤節點,當內存節點重啓後,會鏈接到預先配置的磁盤節點,下載當前集羣元數據拷貝,因此要將全部磁盤節點告訴內存節點。

鏡像隊列

前面提到,隊列只會在集羣中的一個節點,節點崩潰後,隊列消息就會丟失,RabbitMQ2.6版本以後,提供了鏡像隊列,一旦主隊列不可用,從隊列將被選舉爲新的主隊列。

對於鏡像隊列,除了將消息按照路由綁定規則投遞到合適的隊列,也會將消息投遞到鏡像隊列的從拷貝。

對於發送方確認消息,Rabbit會在全部隊列和隊列的從拷貝安全地接收到消息時,纔會通知發送方。

另外,使用鏡像隊列時,有一個問題:若是主拷貝節點發送故障,從隊列會選舉Wie主隊列,全部該隊列的消費者須要從新附加並監聽新的隊列主拷貝。對於經過故障節點進行鏈接的消費者,能夠經過丟失到節點的TCP鏈接檢測到,但對於那些經過節點附加到鏡像隊列且正常運行的消費者將沒法檢測到。

Rabbit經過給消費者發送一個消費者取消通知,告知再也不附加在隊列主拷貝了,須要從新鏈接。

鏈接丟失和故障轉移

這一小節主要討論消費者如何檢測鏈接丟失,並進行重連操做。

處理到集羣的重連有多重策略,比較好的一種方式是使用負載均衡,不只能夠減小應用程序處理節點故障代碼的複雜性,又能確保在集羣中鏈接的平均分配。

關於負載均衡,網上介紹的比較多了,這裏就再也不過多介紹了,主要看看如何感知故障,並進行重連操做。

感知故障比較簡單,當長鏈接斷開時,會拋出異常,捕獲對應的異常便可。

當集羣節點出現故障時,應用程序須要考慮:下一個該連向哪裏?這個工做已經交由負載均衡器決定。

關於重連處理,要考慮:

  • 若是重連到新的服務器,信道以及其上的全部消費循環都會失效,須要對他們進行重建;
  • 當進行重連時,全部的隊列、綁定有可能都不存在了,須要從新構造隊列和綁定。

主/備方式

當對可用性要求特別高時,不容許消息丟失,須要將隊列、交換器、消息設置成持久化,若是一個節點崩潰了,在恢復以前,將沒法轉發消息,由於默認的羣集架構不容許在集羣其餘節點建立隊列,防止故障節點恢復後,歷史消息丟失。

能夠經過構建主/備機的獨立RabbitMQ,也就是warren模式,解決這個問題。一個warren是指一對主/備獨立服務器,並前置一套負載均衡器來處理故障轉移。

主服務器和備用服務器之間沒有協做,只有當主服務器崩潰時,備用服務器纔會處理消息。能夠保證,主節點故障後,經過備用節點從新建立隊列、交換器繼續服務,故障節點恢復後,能夠繼續消費主節點未消費的消息。

跨機房複製

在只有一個數據中心的時候,RabbitMQ集羣對於提高消息通訊性能來講是很棒的方案,但須要把消息從一個程序路由到另外一個城市的時候,就比較麻煩了,能夠經過Shovel解決。

Shovel是RabbitMQ的一個插件,可使你可以定義RabbitMQ上的隊列和另外一個RabbitMQ上的交換器之間的複製關係。說白了就是生產者和消費者離得比較遠。

經過在機房1建立一個新的隊列,用於接收網站發佈的消息,而後讓shovel消費這些消息並從新將消息經過WAN鏈接發佈到機房2上的交換器。

這樣對於用戶來講,只要發佈到機房1的隊列便可返回,減小了響應時間。機房1能夠持續將消息發佈到機房2上。

Shovel處理過程

經過上面的介紹能夠看到,保證高可用須要作不少工做,能夠根據業務對可用性的要求,選擇不一樣的架構方式。

下一篇重點介紹RabbitMQ管理界面和監控。

歡迎掃描下方二維碼,關注個人我的微信公衆號 ~

情情說
相關文章
相關標籤/搜索