RabbitMQ集群方案
RabbitMQ雖然是天生的分布式消息隊列,但其本身并不支持負載均衡。
Connecting to Clusters from Clients
A client can connect as normal to any node within a cluster. If that node should fail, and the rest of the cluster survives, then the client should notice the closed connection, and should be able to reconnect to some surviving member of the cluster. Generally, it's not advisable to bake in node hostnames or IP addresses into client applications: this introduces inflexibility and will require client applications to be edited, recompiled and redeployed should the configuration of the cluster change or the number of nodes in the cluster change. Instead, we recommend a more abstracted approach: this could be a dynamic DNS service which has a very short TTL configuration, or a plain TCP load balancer, or some sort of mobile IP achieved with pacemaker or similar technologies. In general, this aspect of managing the connection to nodes within a cluster is beyond the scope of RabbitMQ itself, and we recommend the use of other technologies designed specifically to solve these problems.
正如官方所說,該問題超出了RabbitMQ本身的范疇,建議采用專門技術去解決。
不像傳統的Web應用前面掛上反向代理那樣簡單,由于它屬于中間件,后端還要從中取數據,所以場景會復雜些。
其實我覺得單就publisher這一層面來說,其實是可以采用直接掛反向代理的解決方案,尤其是對于短連接的Web應用端。對于consumer層面來說,一般情況下就不適合采用同樣的方式(除非采用了鏡像隊列)。
這些都和RabbitMQ的隊列模式有關:
為直觀起見,流程簡化為單鏈接,中間為RabbitMQ節點,上方為publisher,下方為consumer。
單一模式:最簡單的情況,非集群模式。
沒什么好說的。
普通模式:默認的集群模式。
對于Queue來說,消息實體只存在于其中一個節點,A、B兩個節點僅有相同的元數據,即隊列結構。
當消息進入A節點的Queue中后,consumer從B節點拉取時,RabbitMQ會臨時在A、B間進行消息傳輸,把A中的消息實體取出并經過B發送給consumer。
所以consumer應盡量連接每一個節點,從中取消息。即對于同一個邏輯隊列,要在多個節點建立物理Queue。否則無論consumer連A或B,出口總在A,會產生瓶頸。
該模式存在一個問題就是當A節點故障后,B節點無法取到A節點中還未消費的消息實體。
如果做了消息持久化,那么得等A節點恢復,然后才可被消費;如果沒有持久化的話,然后就沒有然后了……
鏡像模式:把需要的隊列做成鏡像隊列,均在與多個節點,屬于RabbitMQ的HA方案。
該模式解決了上述問題,其實質和普通模式不同之處在于,消息實體會主動在鏡像節點間同步,而不是在consumer取數據時臨時拉取。
該模式帶來的副作用也很明顯,除了降低系統性能外,如果鏡像隊列數量過多,加之大量的消息進入,集群內部的網絡帶寬將會被這種同步通訊大大消耗掉。
所以在對可靠性要求較高的場合中適用。
淘寶DBA們給出的方案略顯復雜,根據我們目前的場景(兩個節點)和針對Web形式publisher的實際情況,我設想的集群方案如下:
consumer基于Python實現,守護方式運行,通過異步非阻塞方式同時鏈接并監聽兩個節點中的兩個物理Queue(同一邏輯隊列),消費消息;
Web業務層的publisher直接通過反向代理鏈接任意一臺可用的節點,寫入消息;
具體隊列根據需要選擇是否建立成鏡像模式。
---------------------------------------------------------我是分割線----------------------------------------------------------
參考資料: