從RabbitMQ平滑遷移到RocketMQ技術實戰

来源:https://www.cnblogs.com/vivotech/archive/2022/08/01/16539051.html
-Advertisement-
Play Games

作者:vivo 互聯網中間件團隊- Liu Runyun 大量業務使用消息中間件進行系統間的解耦、非同步化、削峰填谷設計實現。公司內部前期基於RabbitMQ實現了一套高可用的消息中間件平臺。隨著業務的持續增長,消息體量隨之增大,對消息中間件平臺提出了更高的要求,此外在運維過程中也遇到了高可用難以保障 ...


作者:vivo 互聯網中間件團隊- Liu Runyun

大量業務使用消息中間件進行系統間的解耦、非同步化、削峰填谷設計實現。公司內部前期基於RabbitMQ實現了一套高可用的消息中間件平臺。隨著業務的持續增長,消息體量隨之增大,對消息中間件平臺提出了更高的要求,此外在運維過程中也遇到了高可用難以保障,功能特性不足等諸多問題。基於遇到的這些問題,決定引入RocketMQ進行替換。本文將介紹基於RocketMQ建設消息中間件平臺並實現線上業務無感知的平滑遷移。

一、背景說明

vivo互聯網中間件團隊於2016年開始基於開源RabbitMQ向業務提供高可用消息中間件平臺服務。

為解決好業務流量快速增長的問題,我們通過合理的業務集群拆分和動態調整,較好的交付了業務對消息中間件平臺的平臺能力需求

但是隨著業務長周期的迅猛發展,消息體量也越來越大,在高併發、大流量場景下RabbitMQ的系統架構設計存在著一定的限制,主要有以下問題:

1.1 高可用能力不足

架構設計存在腦裂風險,並且預設腦裂後無法自動恢復,人工介入恢復存在數據丟失的風險。

為解決腦裂問題,可以選擇將網路異常後的處理調整為pause_minority模式,但是也帶來了可能微小的網路抖動也會導致集群故障無法恢復的問題。

1.2. 性能不足

業務消息發送後通過exchange路由到對應的queue中,每一個queue由集群中的某個節點實際承載流量,高流量下集群中的某個節點可能會成為瓶頸。

queue由某個節點承載流量後無法快速遷移,強制遷移到其它低負載節點可能會導致queue不可用,這也導致了向集群中添加節點並無法快速提升集群的流量承載能力。

集群性能較低,經測試使用三台機器組成集群,可承載大概數萬tps左右,並且由於queue是由集群中某個節點實際承載的,也無法繼續提升某個queue的性能,這樣就無法支撐大流量業務。

消息堆積到千萬或更多後會導致集群性能下降,甚至海量堆積後如果消費請求tps特別高,可能會因為磁碟的性能損耗導致發送性能下降,並且在消息堆積太多時恢復時間長甚至無法恢復。

1.3 功能特性不足

RabbitMQ 預設情況下消費異常會執行立即重新投遞,少量的異常消息也可能導致業務無法消費後續消息。

功能特性上未支持事務消息、順序消息功能。

雖可自行實現消息軌跡邏輯,但是會對集群產生非常大的性能損耗,在正式環境中實際無法基於RabbitMQ原生的能力實現消息軌跡功能。

二、消息中間件平臺的項目目標

基於以上問題,中間件團隊於2020年Q4開始進行了下一代消息中間件平臺方案的調研,為保證下一代消息中間件平臺符合業務新的需求,我們首先明確了消息中間件平臺的建設目標,主要包含兩部分:

  • 業務需求

  • 平臺需求

2.1 業務需求分析

高性能:可支撐極高的tps,並且支持水平擴展,可快速滿足業務的流量增長需求,消息中間件不應成為業務請求鏈路性能提升的瓶頸點。

高可用:極高的平臺可用性(>99.99%),極高的數據可靠性(>99.99999999%)。

豐富的功能特性:支持集群、廣播消費;支持事務消息、順序消息、延時消息、死信消息;支持消息軌跡。

2.2 平臺運維需求分析

  • 可運維:業務使用許可權校驗;業務生產消費流量限制;業務流量隔離與快速遷移能力。

  • 可觀測:豐富的性能指標觀察集群的運行情況。

  • 可掌握:可基於開源組件快速進行二次開發,豐富平臺功能特性和進行相關問題修複。

  • 雲原生:後續可基於容器化提供雲原生消息中間件,提供更高的彈性和可伸縮能力。

  • 總結:需要建設高性能、高可靠的下一代消息中間件,具備極高的數據可靠性,豐富的功能特性,並且需要完美相容當前的RabbitMQ平臺,幫助業務快速遷移到新消息中間件平臺,減少業務遷移成本。

三、開源組件選型調研

基於當前RabbitMQ平臺的問題和對下一代消息中間件平臺的項目需求,我們開展了針對當前較流行的兩款消息中間件:RocketMQ、Pulsar的調研。

調研過程中主要針對以下兩方面進行對比:

3.1 高可用能力分析對比

3.1.1 高可用架構與負載均衡能力對比

圖片

Pulsar部署架構(來源:Pulsar社區

圖片

RocketMQ部署架構(來源:RocketMQ社區

  • Pulsar:
  • 採用計算與存儲分離架構設計,可以實現海量數據存儲,並且支持冷熱數據分離存儲。

  • 基於ZK和Manager節點控制Broker的故障切換以實現高可用。

  • Zookeeper採用分層分片存儲設計,天然支持負載均衡。

  • RocketMQ:
  • 採用存算一體架構設計,主從模式部署,master節點異常不影響消息讀取,Topic採用分片設計。

  • 需要二次開發支持主從切換實現高可用。

  • 未實現Broker的自動負載均衡,可以將top n流量Topic分佈到不同的Broker中實現簡單的負載均衡。

3.1.2 擴縮容與故障恢復對比

  • Pulsar
  • Broker與BooKeeper獨立擴縮容,並且擴縮容後會完成自動負載均衡。

  • Broker節點無狀態,故障後承載Topic會自動轉移到其它Broker節點,完成故障秒級恢復。

  • BooKeeper由自動恢復服務進行ledger數據對齊,並恢復到設置的QW份。

  • 故障期間已ack消息不會丟失,未ack消息需要客戶端重發。

  • RocketMQ
  • Broker擴縮容後需要人工介入完成Topic流量均衡,可開發自動負載均衡組件結合Topic的讀寫許可權控制自動化完成擴縮容後的負載均衡。

  • 基於主從切換實現高可用,由於客戶端定期30秒從NameSrv更新路由,因此故障恢復時間在30~60秒,可以結合客戶端降級策略讓客戶端主動剔除異常Broker節點,實現更快故障恢復。

  • 採用同步複製非同步刷盤部署架構,在極端情況下會造成少量消息丟失,採用同步複製同步刷盤,已寫入消息不會丟失。

3.1.3 性能對比

  • Pulsar
  • 可支撐百萬Topic數量,實際受到ZK存儲元數據限制。

  • 根據內部壓測1KB消息可支撐TPS達數十萬。

  • RocketMQ
  • 邏輯上可支撐百萬Topic,實際在達到數萬時Broker與NameSrv傳輸心跳包可能超時,建議單集群不超過5萬。

  • 根據壓測可支撐1KB消息體TPS達10萬+。

3.2 功能特性對比

圖片

3.3  總結

從高可用架構分析,Pulsar基於Bookeeper組件實現了架構的計算與存儲分離,可以實現故障的快速恢復;RocketMQ採用了主從複製的架構,故障恢復依賴主從切換。

從功能特性分析,Pulsar支持了豐富的過期策略,支持了消息去重,可以支持實時計算中消息只消費一次的語義;RocketMQ在事務消息、消息軌跡、消費模式等特性對線上業務有更好的支持。

從這兩方面對比,最終選擇了RocketMQ構建我們下一代的消息中間件平臺

四、平滑遷移建設

通過技術調研,確定了基於RocketMQ建設下一代消息中間件平臺。

為了實現業務從RabbitMQ平滑遷移到RocketMQ,就需要建設消息網關實現消息從AMQP協議轉換到RocketMQ;RabbitMQ與RocketMQ的元數據語義與存儲存在差異,需要實現元數據語義的映射與元數據的獨立存儲。

主要有以下四個事項需要完成:

4.1 消息網關獨立部署與嵌入式部署差異對比

圖片

4.2 元數據定義映射與維護

圖片

4.3 互不幹擾的高性能消息推送

RabbitMQ採用推模式進行消息消費,雖然RocketMQ也支持消息推送消費,但是因為AMQP協議中通過prefetch參數限制了客戶端緩存消息數量以保證不會因緩存太多消息導致客戶端記憶體異常,因此在消息網關實現消息推送時也需要滿足AMQP協議的語義。

同時每個消息網關都需要數千甚至數萬的queue的消息推送,每個queue消息消費速率存在差異,並且每個隊列可能隨時有消息需要推送到客戶端進行消費,要保證不同queue之間的推送互不幹擾且及時。

為了實現高效的、互不幹擾的消息推送,有以下策略:

  1. 每個queue採用獨立的線程,保證互不幹擾和時效性,缺點是無法支撐海量queue的消息推送。

  2. 基於信號量、阻塞隊列等,在感知到有可推送消息和可消費服務端時按需進行消息的推送,這樣可使用少量的線程即可完成高效的消息推送。

最終選擇了第2種方案,數據流轉圖如下圖所示:

圖片

一個消息消費過程:客戶端在啟動連接到消息網關後,在消息網關中會構建RocketMQ推送消費客戶端實例,並且註入自定義的ConsumeMessageService實例,同時使用一個信號量保存客戶端允許推送的消息數量。

當消息從集群側推送到消息網關時,將消息按照推送的批次封裝為一個任務保存在ConsumeMessageService實例的BlockingQueue中,同時推送線程會輪詢所有的ConsumeMessageService實例,如果發現本地緩存有待消費的消息並且有可消費消息的業務客戶端,將任務提交到線程池中完成消息的推送。

為了保證不會因為少量消費速率特別高的queue導致其它queue的消息推送時效性降低,會限制每一個ConsumeMessageService只允許推送一定數量的消息即轉到推送其它queue的消息,以此即可保證所有queue的消息推送的互不幹擾和時效性。

在客戶端消費ack/uack後再次通過信號量通知下一次推送,這樣也保證了使用少量的線程資源即可完成海量消息的推送需求。

4.4 消費啟停與消費限流能力實現

基於消息網關,可以在消息推送邏輯中增加消費啟停和消費限流邏輯。

消費啟停可以幫助業務快速實現消費的暫停或是部分異常節點停止消息消費。

消費限流可以幫助業務控制消息消費速率,避免對底層依賴產生太大壓力。

4.5 平臺架構

圖片

  • 最終形成了以上的平臺架構。新建設了一個AMQP-proxy消息網關服務實現AMQP消息轉換到RocketMQ,支持業務的消息生產消費。

  • 建設了mq-meta服務維護集群的元數據信息。

  • 通過mq-controller控制集群的主從切換,實現集群的高可用,同時增加了集群監控,負載均衡模塊保障集群的高可用。

五、平臺建設進展與遷移收益

5.1 業務使用收益

5.1.1 更高、更穩定的消息發送性能

圖片

原生RabbitMQ集群業務壓測性能

圖片

使用消息網關後業務壓測性能

5.1.2 更豐富的功能特性

  • 統一的消息過期時間

  • 消費異常消息將按照梯度延時重投遞

  • 直接支持廣播消費模式

  • 全環境按需提供消息軌跡功能

  • 支持消費重置到以前的某個位點

5.1.3 業務使用特性變化

  • 消息將不再無限期保留,預設保留3~7天(實際保留時間根據集群配置決定)

  • 消費異常將不再立即重投遞,將按照一定的梯度延時重投遞,多次異常後將變為死信消息

  • 直接支持廣播消費,註意廣播消費模式消費無異常重投遞,每個消息每個節點只消費一次

  • 業務生產消費性能可支持水平擴展

  • 不支持消費優先順序功能

  • 預設消費超時時間15分鐘,消費超時後消息重新投遞,消費超時時間可按需調整

  • 支持消費啟停(全局或限制部分節點消費)

  • 支持全局消費限流

  • 限制消息體大小,當前限製為256KB,超過將直接返回失敗,後續將進行流量治理,限制發送大消息體業務流量

5.2 平臺運維收益

業務從RabbitMQ遷移到RocketMQ後,可支撐業務流量從萬TPS級別提升到十萬TPS級別,可支撐業務容量從數億提升至百億級別。耗用機器資源下降50%以上,運維難度和成本均大大降低,同時可以基於消息網關實現更加豐富的功能特性。

六、未來展望

未來,中間件團隊計劃在三個方面對消息中間件進行迭代演進:

  1. 基於消息網關能力豐富現有平臺功能特性,進行業務消息治理。

  2. 過去五年中間件團隊基於開源RabbitMQ進行了RabbitMQ的高可用建設,發現直接讓業務方使用基於開源組件的SDK接入會帶來SDK升級困難,與後端消息中間件類型綁定的問題,未來我們計劃基於GPRC和消息網關,實現消息隊列引擎服務化,業務無需關心底層具體使用的開源消息中間件選型。

  3. 調研RocketMQ5.0計算與存儲分離構架,進行消息中間件架構的再升級。

分享 vivo 互聯網技術乾貨與沙龍活動,推薦最新行業動態與熱門會議。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們都理解B+樹和Hash索引的區別有助於我們預測索引在不同的存儲引擎中是怎麼執行查詢的。 B+ TREE 索引特性B數是一種在資料庫索引中流行的樹數據結構。該結構始終保持排序,從而可以快速查找精確匹配。MySQL中使用的是B樹的一種變體,B+樹,這種類型的索引可用於大多數存儲引擎,例如InnoDB ...
  • 綠幕摳圖是影視製作過程中常見的技術手段,常用於視頻中摳除並替換背景,通過後期加工實現視頻剪輯製作的更多可能性。然而,綠幕摳圖技術製作成本費時費力,無法應用於日常生活。 華為視頻編輯服務近期上線目標分割能力,可通過AI智能摳圖精細化分割視頻中的目標物體,並且不局限於特定的物體類別,在主體明確、背景相對 ...
  • DAY01 電腦的介紹 特點: 1.可以進行數值計算,可以進行邏輯計算 2.具有存儲記憶功能 硬體:看得見,摸得著的 顯示器,主機,存儲器 軟體:看得見,摸不著的 系統軟體:操作系統:windows、Linux、UNIX等 應用軟體:各類app C/S架構和B/S架構 1.C/S架構:需要安裝 a ...
  • 字元串 字元串概述(個人理解字元串就是把一串字元連接在一起,而且他的值類型是常量,所以不能改變,返回值只能返回一個新的字元串) 字元串也是一個數據結構(串),將同樣的內容串在一塊。因為在對應的js裡面字元串屬於一個值類型(值類型是常量 常量是不能變)。字元串是不能改變的。結合昨天提到的數據結構裡面串 ...
  • 在我們平時開發中,經常會遇到頁面數據初始化時,頻繁調同一個介面的情況。比如echarts項目中,一個頁面可能會有幾十張圖表,如果一個介面返回所有圖表數據的話,會造成用戶過長的等待時間,再者過多圖表同時渲染,也會給頁面增加壓力,造成卡頓的現象。 我們通常會讓每個圖表單獨調一個介面,入參不同,這樣更有利 ...
  • 今天開發中遇到一個問題,echarts圖表觸摸x軸觸發tooltip會將x軸上所有的數據展示出來,但是有些場合只需要展示某些數據就可以,並不需要全部展示,如下圖: 這裡警戒線因為需要開關,所以使用填充的數據模擬,但是,觸摸後會導致連警戒線數據也顯示出來,如圖: 實際上圖表中只有荷載是真實數據需要顯示 ...
  • 原文鏈接:基於 Hexo 鍵入分享功能 前言 本站基於Hexo搭建,用的 🦋 hexo-theme-butterfly 主題 v3.7.1,請註意最新的🦋 hexo-theme-butterfly 版本已經更新到 v4.2.2 。 如果你是 v3.7.1 之外的版本,可能有些地方會有出入,請留意 ...
  • 1.獲取鍵名 2.下載excel文件靜態資源路徑報錯 // 獲取鍵名 const obj={a:1,b:2,c:3}; console.log(Object.keys(obj));//["a","b","c"] // 下載excel文件靜態資源路徑報錯問題 解決方案 將文件放在src外的public ...
一周排行
    -Advertisement-
    Play Games
  • 使用原因: 在我們服務端調用第三方介面時,如:支付寶,微信支付,我們服務端需要模擬http請求並加上一些自己的邏輯響應給前端最終達到我們想要的效果 1.使用WebClient 引用命名空間 using System.Net; using System.Collections.Specialized; ...
  • WPF 實現帶蒙版的 MessageBox 消息提示框 WPF 實現帶蒙版的 MessageBox 消息提示框 作者:WPFDevelopersOrg 原文鏈接: https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal 框架使用大於等於.N ...
  • 一、JSON(JavaScript Object Notation)的簡介: ① JSON和XML類似,主要用於存儲和傳輸文本信息,但是和XML相比,JSON更小、更快、更易解析、更易編寫與閱讀。 ② C、Python、C++、Java、PHP、Go等編程語言都支持JSON。 二、JSON語法規則: ...
  • 1.避免Scoped模式註冊的服務變成Singleton模式 當提供一個生命周期模式為Singleton的服務實例時,如果發現該服務中還依賴生命周期模式為Scoped的服務實例(Scoped服務實例將被一個Singleton服務實例所引用),那麼這個被依賴的Scoped服務實例最終會成為一個Sing ...
  • 索引時資料庫提高數據查詢處理性能的一個非常關鍵的技術,索引的使用可以對性能產生上百倍甚至上千倍的影響。接下來,會介紹索引的基本原理、概念,並深入學習資料庫中所使用的索引結構和存儲方式,以及如何管理、維護索引等。 1.索引的基本概念 索引時用來快速查詢表記錄的一種存儲結構,一般使用索引有一下兩個方面: ...
  • django2 路由控制器 Route路由,是一種映射關係。路由是把客戶端請求的url路徑和用戶請求的應用程式,這裡意指django裡面的視圖進行綁定映射的一種關係。 請求路徑和視圖函數不是一一對應的關係 在django中所有的路由最終都被保存到一個叫urlpatterns的文件里,並且該文件必須在 ...
  • 1、我們的目標是獲取微博某博主的全部圖片、視頻 2、拿到網址後 我們先觀察 打開F12 隨著下滑我們發現載入出來了一個叫mymblog的東西,展開響應發現需要的東西就在裡面 3、重點來了!!! 通過觀察發現第二頁比第一頁多了參數since_id 而第二頁的since_id參數剛好在上一頁中能獲取到, ...
  • 一、實現原理 在Servlet3協議規範中,包含在JAR文件/META-INFO/resources/路徑下的資源可以直接訪問。 二、舉例說明 如下圖所示,是我新建的一個Spring Boot Starter項目:zimug-minitor-threadpool,用於實現可配置、可觀測的線程池。其中 ...
  • 精華筆記: static final常量:應用率高 必須聲明同時初始化 由類名打點來訪問,不能被改變 建議:常量所有字母都大寫,多個單詞用_分隔 編譯器在編譯時會將常量直接替換為具體的數,效率高 何時用:數據永遠不變,並且經常使用 抽象方法: 由abstract修飾 只有方法的定義,沒有具體的實現( ...
  • Python有一個for...else語法,它的寫法如下 for i in range(0,100): if i == 3: break else: print("Not found") 該語句表示:若for迴圈遍歷完畢,則執行else部分的語句。也就是說上述代碼不會有任何輸出,而下述代碼會輸出“N ...