共計 4559 個字符,預計需要花費 12 分鐘才能閱讀完成。
這篇文章給大家分享的是有關 vivo 基于原生 RabbitMQ 高可用架構的示例分析的內容。丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,一起跟隨丸趣 TV 小編過來看看吧。
一、背景說明
vivo 在 2016 年引入 RabbitMQ,基于開源 RabbitMQ 進行擴展,向業務提供消息中間件服務。
2016~2018 年,所有業務均使用一個集群,隨著業務規模的增長,集群負載越來越重,集群故障頻發。
2019 年,RabbitMQ 進入高可用建設階段,完成了高可用組件 MQ 名字服務以及 RabbitMQ 集群的同城雙活建設。
同時進行業務使用集群的物理拆分,嚴格按照集群負載情況和業務流量進行業務使用集群的分配以及動態調整。
在 2019 年高可用建設后至今,業務流量增加了十倍,集群未出現過嚴重故障。
RabbitMQ 是實現了 AMQP 協議的開源消息代理軟件,起源于金融系統。
具有豐富的特性:
消息可靠性保證,RabbitMQ 通過發送確認保證消息發送可靠、通過集群化、消息持久化、鏡像隊列的方式保證消息在集群的可靠、通過消費確認保證消息消費的可靠性。
RabbitMQ 提供了多種語言的客戶端。
提供了多種類型的 exchange,消息發送到集群后通過 exchange 路由到具體的 queue 中。
RabbitMQ 提供了完善的管理后臺和管理 API,通過管理 API 可以快速與自建監控系統整合。
RabbitMQ 在具體實踐中發現的問題:
為保障業務高可用使用多套集群進行物理隔離,多套集群無統一平臺進行管理。
原生 RabbitMQ 客戶端使用集群地址連接,使用多套集群時業務需要關心集群地址,使用混亂。
原生 RabbitMQ 僅有簡單的用戶名 / 密碼驗證,不對使用的業務應用方進行鑒權,不同業務容易混用 exchange/queue 信息,造成業務應用使用異常。
使用的業務應用方較多,無平臺維護消息發送方、消費方的關聯信息,多個版本迭代后無法確定對接方。
客戶端無限流,業務突發異常流量沖擊甚至擊垮集群。
客戶端無異常消息重發策略,需要使用方實現。
集群出現內存溢出等造成集群阻塞時無法快速自動轉移到其它可用集群。
使用鏡像隊列,隊列的 master 節點會落在具體某個節點上,在集群隊列數較多時,容易出現節點負載不均衡的情況。
RabbitMQ 無隊列自動平衡能力,在隊列較多時容易出現集群節點負載不均問題。
二、整體架構
1、MQ-Portal– 支持應用使用申請
過往業務團隊適用 RabbitMQ 時,應用申請的流量以及對接的應用等信息都在線下表格記錄,較為零散,更新不及時,無法準確了解業務當前真實的使用情況,因此通過一個接入申請的流程可視化、平臺化建立應用使用的元數據信息。
通過 MQ-Portal 的申請流程(如上圖),確定了消息發送應用、消費應用、使用 exchange/queue、發送流量等信息使用申請提交后將進入 vivo 內部工單流程進行審批。
工單流程審批通過后,通過工單的接口回調,分配應用具體使用的集群,并在集群上創建 exchange/queue 已經綁定關系。
由于采用多集群物理隔離的方式保證業務在正式環境的高可用,無法簡單通過一個 exchange/queue 的名稱定位到使用的集群。
每一個 exchange/queue 與集群之間通過唯一的一對 rmq.topic.key 與 rmq.secret.key 進行關聯,這樣 SDK 啟動過程中即可定位到具體使用的集群。
rmq.topic.key 與 rmq.secret.key 將在工單的回調接口中進行分配。
2、客戶端 SDK 能力概述
客戶端 SDK 基于 spring-message 和 spring-rabbit 進行封裝,并在此基礎上提供了應用使用鑒權、集群尋址、客戶端限流、生產消費重置、阻塞轉移等能力。
2.1、應用使用鑒權
開源 RabbitMQ 僅通過用戶名密碼的方式判斷是否允許連接集群,但是應用是否允許使用 exchange/queue 是未進行校驗的。
為了避免不同業務混用 exchange/queue,需要對應用進行使用鑒權。
應用鑒權由 SDK 和 MQ-NameServer 協同完成。
應用啟動時首先會上報應用配置的 rmq.topic.key 信息到 MQ-NameServer,由 MQ-NameServer 判斷使用應用與申請應用是否一致,并且在 SDK 發送消息過程中還會進行二次校驗。
/**
* 發送前校驗,并且獲取真正的發送 factory,這樣業務可以聲明多個, * 但是用其中一個 bean 就可以發送所有的消息,并且不會導致任何異常
* @param exchange 校驗參數
* @return 發送工廠
public AbstractMessageProducerFactory beforeSend(String exchange) { if(closed || stopped){
// 上下文已經關閉拋出異常,阻止繼續發送,減少發送臨界狀態數據
throw new RmqRuntimeException(String.format( producer sending message to exchange %s has closed, can t send message , this.getExchange()));
}
if (exchange.equals(this.exchange)){
return this;
}
if (!VIVO_RMQ_AUTH.isAuth(exchange)){ throw new VivoRmqUnAuthException(String.format( 發送 topic 校驗異常,請勿向無權限 exchange %s 發送數據,發送失敗 , exchange));
}
// 獲取真正的發送的 bean,避免發送錯誤
return PRODUCERS.get(exchange);
}
2.2、集群尋址
前文說過,應用使用 RabbitMQ 嚴格按照集群的負載情況和業務流量進行集群的分配,因此具體某個應用使用的的不同的 exchange/queue 可能是分配在不同的集群上的。
為了提升業務的開發效率,需要屏蔽多集群對業務的影響,因此按照應用配置的 rmq.topic.key 信息進行集群的自動尋址。
2.3、客戶端限流
原生 SDK 客戶端不進行發送流量限流,在部分應用存在異常持續向 MQ 發送消息時,可能會沖垮 MQ 集群。并且一個集群為多應用共同使用,單一應用造成集群影響將會影響使用異常集群的所有應用。
因此需要在 SDK 中提供客戶端限流的能力,必要時可以限制應用向集群發送消息,保障集群的穩定。
2.4、生產消費重置
(1)隨著業務規模增長,集群負載持續增加,此時需要進行集群的業務拆分。為了減少在拆分過程中避免業務重啟,需要有生產消費重置功能。
(2)集群出現異常,可能會造成消費者掉線,此時通過生產消費重置可以快速拉起業務消費。
為了實現生產消費重置,需要實現一下流程:
重置連接工廠連接參數
重置連接
建立新的連接
重新啟動生產消費
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(address);
connectionFactory.resetConnection();
rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitTemplate = new RabbitTemplate(connectionFactory);
同時 MQ-SDK 中有異常消息重發策略,可以避免在生產重置過程中導致的消息發送異常。
2.5、阻塞轉移
RabbitMQ 在內存使用超過 40%,或是磁盤使用超限制時會阻塞消息發送。
由于 vivo 中間件團隊已經完成了 RabbitMQ 同城雙活的建設,因此在出現一個集群發送阻塞時可以通過生產消費重置到雙活集群完成阻塞的快速轉移。
2.6、多集群調度
隨著應用的發展,單集群將無法滿足應用的流量需求,并且集群隊列均為鏡像隊列,無法簡單的通過增加集群節點的方式實現業務支撐流量單集群的水平擴容。
因此需要 SDK 支持多集群調度能力,通過將流量分散到多個集群上滿足業務大流量需求。
3、MQ-NameServer– 支持 MQ-SDK 實現故障快速切換
MQ-NameServer 為無狀態服務,通過集群部署即可保障自身高可用,主要用于解決以下問題:
MQ-SDK 啟動鑒權以及應用使用集群定位。
處理 MQ-SDK 的定時指標上報(消息發送數量、消息消費數量),并且返回當前可用集群地址,確保 SDK 在集群異常時按照正確地址進行重連。
控制 MQ-SDK 進行生產消費重置。
4、MQ-Server 高可用部署實踐
RabbitMQ 集群均采用同城雙活部署架構,依靠 MQ-SDK 和 MQ-NameServer 提供的集群尋址、故障快速切換等能力保障集群的可用性。
4.1、集群腦裂問題處理
RabbitMQ 官方提供了三種集群腦裂恢復策略。
(1)ignore
忽略腦裂問題不處理,在出現腦裂時需要進行人為干預才可恢復。由于需要人為干預,可能會造成部分消息丟失,在網絡非常可靠的情況可以使用。
(2)pause_minority
節點在與超過半數集群節點失聯時將會自動暫停,直到檢測到與集群超半數節點的通信恢復。極端情況下集群內所有節點均暫停,造成集群不可用。
(3)autoheal
少數派節點將自動重啟,此策略主要用于優先保證服務的可用性,而不是數據的可靠性,因為重啟節點上的消息會丟失。
由于 RabbitMQ 集群均為同城雙活部署,即使單集群異常業務流量也可自動遷移到雙活機房集群,因此選擇使用了 pause_minority 策略避免腦裂問題。
2018 年多次因網絡抖動造成集群腦裂,在修改集群腦裂恢復策略后,已未再出現腦裂問題。
4.2、集群高可用方案
RabbitMQ 采用集群化部署,并且因為集群腦裂恢復策略采用 pause_minority 模式,每個集群要求至少 3 個節點。
推薦使用 5 或 7 節點部署高可用集群,并且控制集群隊列數量。
集群隊列均為鏡像隊列,確保消息存在備份,避免節點異常導致消息丟失。
exchange、queue、消息均設置為持久化,避免節點異常重啟消息丟失。
隊列均設置為 lazy queues,減少節點內存使用的波動。
4.3、同城雙活建設
雙機房部署等價集群,并且通過 Federation 插件將雙集群組成聯盟集群。
本機房應用機器優先連接本機房 MQ 集群,避免因專線抖動造成應用使用異常。
通過 MQ-NameServer 心跳獲取最新的可用集群信息,異常時重連到雙活集群中,實現應用功能的快速恢復。
三、未來挑戰與展望
目前對 RabbitMQ 的使用增強主要在 MQ-SDK 和 MQ-NameServer 側,SDK 實現較為復雜,后期希望可以構建消息中間件的代理層,可以簡化 SDK 并且對業務流量做更加細致化的管理。
感謝各位的閱讀!關于“vivo 基于原生 RabbitMQ 高可用架構的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!