您好,登錄后才能下訂單哦!
本系列是「RabbitMQ實戰(zhàn):高效部署分布式消息隊列」書籍的總結(jié)筆記。
上一篇介紹了各種場景下的最佳實踐,大部分場景可以使用「發(fā)后即忘」的模式,不需要響應,如果需要響應,可以使用RabbitMQ的RPC模型。
RabbitMQ以異步的方式解耦系統(tǒng)間的關系,調(diào)用者將業(yè)務請求發(fā)送到Rabbit服務器,就可以返回了,Rabbit會確保請求被正確處理,即使遇到網(wǎng)絡異常、Rabbit服務器崩潰、整個機房斷電等特殊場景,針對這些場景,Rabbit提供了各種機制確保其可用性。
本篇通過總結(jié)可能出現(xiàn)的特殊場景,對Rabbit提供的可用性保證進行分析,學習它的實現(xiàn)方式,你會了解到:
在實際工作中,有很大一部分時間用在解決各種異常情況,比如針對用戶輸入的驗證,JDK中提供的各種異常類,網(wǎng)絡異常等,這些相對來說比較好解決。
Rabbit服務作為調(diào)用者和處理者的橋梁,至關重要,如果因為網(wǎng)絡異常、單臺服務器崩潰、機房癱瘓等原因?qū)е翿abbit服務不可用,會影響所有依賴的業(yè)務系統(tǒng)。
處理者和服務端是通過長連接交互的,這樣可以將消息實時推送,網(wǎng)絡異常可能會導致長連接斷開,如果客戶端無法感知,處理者將接收不到任何消息,這種情況稱為「連接丟失」。
通過捕獲連接異常,進行重連,可以解決這種問題,另外,Rabbit客戶端進行了封裝,很容易處理這種問題。
如果只有一臺服務器服務,服務器崩潰將導致服務不可用,一般會使用集群將多個服務器看成一個整體對外提供服務,這樣,單臺服務器崩潰不會影響整體的服務。
使用集群后,就要考慮一些問題:
如果考慮機房癱瘓,就要建多個數(shù)據(jù)中心,RabbitMQ提供了一種機制,可以方便地在不同數(shù)據(jù)中心的Rabbit間復制消息。
RabbitMQ最優(yōu)秀的功能之一就是其內(nèi)建集群,主要用于完成2個目標:
RabbitMQ會始終記錄四種類型的內(nèi)部元數(shù)據(jù)(類似索引):
當引入集群時,就需要追蹤新的元數(shù)據(jù)類型:集群節(jié)點位置,以及節(jié)點與已記錄的其他類型元數(shù)據(jù)的關系。
不是每個節(jié)點都有所有隊列的完全拷貝,如果在集群中創(chuàng)建隊列,只會在單個節(jié)點上創(chuàng)建完整的隊列信息(元數(shù)據(jù)、狀態(tài)、內(nèi)容),所有其他節(jié)點只知道隊列的元數(shù)據(jù)和指向該隊列的節(jié)點指針。
如果節(jié)點崩潰了,附加在隊列上的消費者也就無法接收新的消息了??梢宰屜M者重連到集群并重新創(chuàng)建隊列,這種做法僅當隊列沒設置持久化時才可行,這是為了確保當失敗的節(jié)點恢復后加入集群,節(jié)點上的隊列消息不會丟失。
為什么不將隊列內(nèi)容和狀態(tài)復制到所有節(jié)點:第一,存儲空間,如果每個集群節(jié)點都擁有所有隊列的完全拷貝,添加新節(jié)點不會帶來更多存儲空間;第二,性能,消息的發(fā)布者需要將消息復制到每一個集群節(jié)點,對于持久化消息,網(wǎng)絡和磁盤復制都會增加。
而交換器只是一張查詢表,而非實際的消息路由器,因此將交換器在整個集群中進行復制會更加簡單
可以把每個隊列想象成節(jié)點上運行的進程,每個進程擁有自己的進程ID,交換器只是路由模式列表和匹配消息應發(fā)往的隊列進程ID列表。
每個Rabbit節(jié)點,要么是內(nèi)存節(jié)點,要么是磁盤節(jié)點,單節(jié)點系統(tǒng)只運行磁盤類型的節(jié)點,在集群中,可以選擇配置部分節(jié)點為內(nèi)存節(jié)點。
在集群中聲明隊列、交換器或綁定的時候,這些操作直到所有集群節(jié)點都成功提交元數(shù)據(jù)變更后才返回。
RabbitMQ只要求集群中至少有一個磁盤節(jié)點,如果只有一個磁盤節(jié)點,剛好又崩潰了,集群可以繼續(xù)路由消息,但不能創(chuàng)建隊列、交換器、綁定、添加用戶、更改權限等操作。所以,建議設置兩個磁盤節(jié)點,當內(nèi)存節(jié)點重啟后,會連接到預先配置的磁盤節(jié)點,下載當前集群元數(shù)據(jù)拷貝,所以要將所有磁盤節(jié)點告訴內(nèi)存節(jié)點。
前面提到,隊列只會在集群中的一個節(jié)點,節(jié)點崩潰后,隊列消息就會丟失,RabbitMQ2.6版本之后,提供了鏡像隊列,一旦主隊列不可用,從隊列將被選舉為新的主隊列。
對于鏡像隊列,除了將消息按照路由綁定規(guī)則投遞到合適的隊列,也會將消息投遞到鏡像隊列的從拷貝。
對于發(fā)送方確認消息,Rabbit會在所有隊列和隊列的從拷貝安全地接收到消息時,才會通知發(fā)送方。
另外,使用鏡像隊列時,有一個問題:如果主拷貝節(jié)點發(fā)送故障,從隊列會選舉Wie主隊列,所有該隊列的消費者需要重新附加并監(jiān)聽新的隊列主拷貝。對于通過故障節(jié)點進行連接的消費者,可以通過丟失到節(jié)點的TCP連接檢測到,但對于那些通過節(jié)點附加到鏡像隊列且正常運行的消費者將無法檢測到。
Rabbit通過給消費者發(fā)送一個消費者取消通知,告知不再附加在隊列主拷貝了,需要重新連接。
這一小節(jié)主要討論消費者如何檢測連接丟失,并進行重連操作。
處理到集群的重連有多重策略,比較好的一種方式是使用負載均衡,不僅可以減少應用程序處理節(jié)點故障代碼的復雜性,又能確保在集群中連接的平均分配。
關于負載均衡,網(wǎng)上介紹的比較多了,這里就不再過多介紹了,主要看看如何感知故障,并進行重連操作。
感知故障比較簡單,當長連接斷開時,會拋出異常,捕獲對應的異常即可。
當集群節(jié)點出現(xiàn)故障時,應用程序需要考慮:下一個該連向哪里?這個工作已經(jīng)交由負載均衡器決定。
關于重連處理,要考慮:
當對可用性要求特別高時,不允許消息丟失,需要將隊列、交換器、消息設置成持久化,如果一個節(jié)點崩潰了,在恢復之前,將無法轉(zhuǎn)發(fā)消息,因為默認的群集架構不允許在集群其他節(jié)點創(chuàng)建隊列,防止故障節(jié)點恢復后,歷史消息丟失。
可以通過構建主/備機的獨立RabbitMQ,也就是warren模式,解決這個問題。一個warren是指一對主/備獨立服務器,并前置一套負載均衡器來處理故障轉(zhuǎn)移。
主服務器和備用服務器之間沒有協(xié)作,只有當主服務器崩潰時,備用服務器才會處理消息??梢员WC,主節(jié)點故障后,通過備用節(jié)點重新創(chuàng)建隊列、交換器繼續(xù)服務,故障節(jié)點恢復后,可以繼續(xù)消費主節(jié)點未消費的消息。
在只有一個數(shù)據(jù)中心的時候,RabbitMQ集群對于提升消息通信性能來說是很棒的方案,但需要把消息從一個程序路由到另一個城市的時候,就比較麻煩了,可以通過Shovel解決。
Shovel是RabbitMQ的一個插件,可以使你能夠定義RabbitMQ上的隊列和另一個RabbitMQ上的交換器之間的復制關系。說白了就是生產(chǎn)者和消費者離得比較遠。
通過在機房1創(chuàng)建一個新的隊列,用于接收網(wǎng)站發(fā)布的消息,然后讓shovel消費這些消息并重新將消息通過WAN連接發(fā)布到機房2上的交換器。
這樣對于用戶來說,只要發(fā)布到機房1的隊列即可返回,減少了響應時間。機房1可以持續(xù)將消息發(fā)布到機房2上。
通過上面的介紹可以看到,保證高可用需要做很多工作,可以根據(jù)業(yè)務對可用性的要求,選擇不同的架構方式。
下一篇重點介紹RabbitMQ管理界面和監(jiān)控。
歡迎掃描下方二維碼,關注我的個人微信公眾號 ~
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。