RocketMQ 端云一体化设计与实践
簡(jiǎn)介:本文首先介紹了端云消息場(chǎng)景一體化的背景,然后重點(diǎn)分析了終端消息場(chǎng)景特點(diǎn),以及終端消息場(chǎng)景支撐模型,最后對(duì)架構(gòu)和存儲(chǔ)內(nèi)核進(jìn)行了闡述。我們期望基于 RocketMQ 統(tǒng)一內(nèi)核一體化支持終端和服務(wù)端不同場(chǎng)景的消息接入目標(biāo),以能夠給使用者帶來(lái)一體化的價(jià)值,如降低存儲(chǔ)成本,避免數(shù)據(jù)在不同系統(tǒng)間同步帶來(lái)的一致性挑戰(zhàn)。
作者 | 悟幻
一體化背景
不止于分發(fā)
我們都知道以 RocketMQ 為代表的消息(隊(duì)列)起源于不同應(yīng)用服務(wù)之間的異步解耦通信,與以 Dubbo 為代表的 RPC 類服務(wù)通信一同承載了分布式系統(tǒng)(服務(wù))之間的通信場(chǎng)景,所以服務(wù)間的消息分發(fā)是消息的基礎(chǔ)訴求。然而我們看到,在消息(隊(duì)列)這個(gè)領(lǐng)域,近些年我們業(yè)界有個(gè)很重要的趨勢(shì),就是基于消息這份數(shù)據(jù)可以擴(kuò)展到流批計(jì)算、事件驅(qū)動(dòng)等不同場(chǎng)景,如 RocketMQ-streams,Kafka-Streams、Rabbit-Streams 等等。
不止于服務(wù)端
傳統(tǒng)的消息隊(duì)列 MQ 主要應(yīng)用于服務(wù)(端)之間的消息通信,比如電商領(lǐng)域的交易消息、支付消息、物流消息等等。然而在消息這個(gè)大類下,還有一個(gè)非常重要且常見的消息領(lǐng)域,即終端消息。消息的本質(zhì)就是發(fā)送和接受,終端和服務(wù)端并沒有本質(zhì)上的大區(qū)別。
一體化價(jià)值
如果可以有一個(gè)統(tǒng)一的消息系統(tǒng)(產(chǎn)品)來(lái)提供多場(chǎng)景計(jì)算(如 stream、event)、多場(chǎng)景(IoT、APP)接入,其實(shí)是非常有價(jià)值的,因?yàn)橄⒁彩且环N重要數(shù)據(jù),數(shù)據(jù)如果只存在一個(gè)系統(tǒng)內(nèi),可以最大地降低存儲(chǔ)成本,同時(shí)可以有效地避免數(shù)據(jù)因在不同系統(tǒng)間同步帶來(lái)的一致性難題。
終端消息分析
本文將主要描述的是終端消息和服務(wù)端消息一體化設(shè)計(jì)與實(shí)踐問(wèn)題,所以首先我們對(duì)面向終端的這一大類消息做一下基本分析。
場(chǎng)景介紹
近些年,我們看到隨著智能家居、工業(yè)互聯(lián)而興起的面向 IoT 設(shè)備類的消息正在呈爆炸式增長(zhǎng),而已經(jīng)發(fā)展十余年的移動(dòng)互聯(lián)網(wǎng)的手機(jī) APP 端消息仍然是數(shù)量級(jí)龐大。面向終端設(shè)備的消息數(shù)量級(jí)比傳統(tǒng)服務(wù)端的消息要大很多量級(jí),并仍然在快速增長(zhǎng)。
特性分析
盡管無(wú)論是終端消息還是服務(wù)端消息,其本質(zhì)都是消息的發(fā)送和接受,但是終端場(chǎng)景還是有和服務(wù)端不太一樣的特點(diǎn),下面簡(jiǎn)要分析一下:
- 輕量
服務(wù)端一般都是使用很重的客戶端 SDK 封裝了很多功能和特性,然而終端因?yàn)檫\(yùn)行環(huán)境受限且龐雜必須使用輕量簡(jiǎn)潔的客戶端 SDK。
- 標(biāo)準(zhǔn)協(xié)議
服務(wù)端正是因?yàn)橛辛酥亓考?jí)客戶端 SDK,其封裝了包括協(xié)議通信在內(nèi)的全部功能,甚至可以弱化協(xié)議的存在,使用者無(wú)須感知,而終端場(chǎng)景因?yàn)橐С指黝慅嬰s的設(shè)備和場(chǎng)景接入,必須要有個(gè)標(biāo)準(zhǔn)協(xié)議定義。
- P2P
服務(wù)端消息如果一臺(tái)服務(wù)器處理失敗可以由另外一臺(tái)服務(wù)器處理成功即可,而終端消息必須明確發(fā)給具體終端,若該終端處理失敗則必須一直重試發(fā)送該終端直到成功,這個(gè)和服務(wù)端很不一樣。
- 廣播比
服務(wù)端消息比如交易系統(tǒng)發(fā)送了一條訂單消息,可能有如營(yíng)銷、庫(kù)存、物流等幾個(gè)系統(tǒng)感興趣,而終端場(chǎng)景比如群聊、直播可能成千上萬(wàn)的終端設(shè)備或用戶需要收到。
- 海量接入
終端場(chǎng)景接入的是終端設(shè)備,而服務(wù)端接入的就是服務(wù)器,前者在量級(jí)上肯定遠(yuǎn)大于后者。
架構(gòu)與模型
消息基礎(chǔ)分析
實(shí)現(xiàn)一體化前我們先從理論上分析一下問(wèn)題和可行性。我們知道,無(wú)論是終端消息還是服務(wù)端消息,其實(shí)就是一種通信方式,從通信的層面看要解決的基礎(chǔ)問(wèn)題簡(jiǎn)單總結(jié)就是:協(xié)議、匹配、觸達(dá)。
- 協(xié)議
協(xié)議就是定義了一個(gè)溝通語(yǔ)言頻道,通信雙方能夠聽懂內(nèi)容語(yǔ)義。在終端場(chǎng)景,目前業(yè)界廣泛使用的是 MQTT 協(xié)議,起源于物聯(lián)網(wǎng) IoT 場(chǎng)景,OASIS 聯(lián)盟定義的標(biāo)準(zhǔn)的開放式協(xié)議。
MQTT 協(xié)議定義了是一個(gè) Pub/Sub 的通信模型,這個(gè)與 RocketMQ 類似的,不過(guò)其在訂閱方式上比較靈活,可以支持多級(jí) Topic 訂閱(如 “/t/t1/t2”),可以支持通配符訂閱(如 “/t/t1/+”)
- 匹配
匹配就是發(fā)送一條消息后要找到所有的接受者,這個(gè)匹配查找過(guò)程是不可或缺的。
在 RocketMQ 里面實(shí)際上有這個(gè)類似的匹配過(guò)程,其通過(guò)將某個(gè) Queue 通過(guò) rebalance 方式分配到消費(fèi)組內(nèi)某臺(tái)機(jī)器上,消息通過(guò) Queue 就直接對(duì)應(yīng)上了消費(fèi)機(jī)器,再通過(guò)訂閱過(guò)濾(Tag 或 SQL)進(jìn)行精準(zhǔn)匹配消費(fèi)者。之所以通過(guò) Queue 就可以匹配消費(fèi)機(jī)器,是因?yàn)榉?wù)端場(chǎng)景消息并不需要明確指定某臺(tái)消費(fèi)機(jī)器,一條消息可以放到任意 Queue 里面,并且任意一臺(tái)消費(fèi)機(jī)器對(duì)應(yīng)這個(gè) Queue 都可以,消息不需要明確匹配消費(fèi)機(jī)器。
而在終端場(chǎng)景下,一條消息必須明確指定某個(gè)接受者(設(shè)備),必須準(zhǔn)確找到所有接受者,而且終端設(shè)備一般只會(huì)連到某個(gè)后端服務(wù)節(jié)點(diǎn)即單連接,和消息產(chǎn)生的節(jié)點(diǎn)不是同一個(gè),必須有個(gè)較復(fù)雜的匹配查找目標(biāo)的過(guò)程,還有如 MQTT 通配符這種更靈活的匹配特性。
- 觸達(dá)
觸達(dá)即通過(guò)匹配查找后找到所有的接受者目標(biāo),需要將消息以某種可靠方式發(fā)給接受者。常見的觸發(fā)方式有兩種:Push、Pull。Push,即服務(wù)端主動(dòng)推送消息給終端設(shè)備,主動(dòng)權(quán)在服務(wù)端側(cè),終端設(shè)備通過(guò) ACK 來(lái)反饋消息是否成功收到或處理,服務(wù)端需要根據(jù)終端是否返回 ACK 來(lái)決定是否重投。Pull,即終端設(shè)備主動(dòng)來(lái)服務(wù)端獲取其所有消息,主動(dòng)權(quán)在終端設(shè)備側(cè),一般通過(guò)位點(diǎn) Offset 來(lái)依次獲取消息,RocketMQ 就是這種消息獲取方式。
對(duì)比兩種方式,我們可以看到 Pull 方式需要終端設(shè)備主動(dòng)管理消息獲取邏輯,這個(gè)邏輯其實(shí)有一定的復(fù)雜性(可以參考 RocketMQ 的客戶端管理邏輯),而終端設(shè)備運(yùn)行環(huán)境和條件都很龐雜,不太適應(yīng)較復(fù)雜的 Pull 邏輯實(shí)現(xiàn),比較適合被動(dòng)的 Push 方式。
另外,終端消息有一個(gè)很重要的區(qū)別是可靠性保證的 ACK 必須是具體到一個(gè)終端設(shè)備的,而服務(wù)端消息的可靠性在于只要有一臺(tái)消費(fèi)者機(jī)器成功處理即可,不太關(guān)心是哪臺(tái)消費(fèi)者機(jī)器,消息的可靠性 ACK 標(biāo)識(shí)可以集中在消費(fèi)組維度,而終端消息的可靠性 ACK 標(biāo)識(shí)需要具體離散到終端設(shè)備維度。簡(jiǎn)單地說(shuō),一個(gè)是客戶端設(shè)備維度的 Retry 隊(duì)列,一個(gè)是消費(fèi)組維度的 Retry 隊(duì)列。
模型與組件
基于前面的消息基礎(chǔ)一般性分析,我們來(lái)設(shè)計(jì)消息模型,主要是要解決好匹配查找和可靠觸達(dá)兩個(gè)核心問(wèn)題。
- 隊(duì)列模型
消息能夠可靠性觸達(dá)的前提是要可靠存儲(chǔ),消息存儲(chǔ)的目的是為了讓接受者能獲取到消息,接受者一般有兩種消息檢索維度:
1)根據(jù)訂閱的主題 Topic 去查找消息;
2)根據(jù)訂閱者 ID 去查找消息。這個(gè)就是業(yè)界常說(shuō)的放大模型:讀放大、寫放大。
讀放大:即消息按 Topic 進(jìn)行存儲(chǔ),接受者根據(jù)訂閱的 Topic 列表去相應(yīng)的 Topic 隊(duì)列讀取消息。
寫放大:即消息分別寫到所有訂閱的接受者隊(duì)列中,每個(gè)接受者讀取自己的客戶端隊(duì)列。
可以看到讀放大場(chǎng)景下消息只寫一份,寫到 Topic 維度的隊(duì)列,但接受者讀取時(shí)需要按照訂閱的 Topic 列表多次讀取,而寫放大場(chǎng)景下消息要寫多份,寫到所有接受者的客戶端隊(duì)列里面,顯然存儲(chǔ)成本較大,但接受者讀取簡(jiǎn)單,只需讀取自己客戶端一個(gè)隊(duì)列即可。
我們采用的讀放大為主,寫放大為輔的策略,因?yàn)榇鎯?chǔ)的成本和效率對(duì)用戶的體感最明顯。寫多份不僅加大了存儲(chǔ)成本,同時(shí)也對(duì)性能和數(shù)據(jù)準(zhǔn)確一致性提出了挑戰(zhàn)。但是有一個(gè)地方我們使用了寫放大模式,就是通配符匹配,因?yàn)榻邮苷哂嗛喌氖峭ㄅ浞拖⒌?Topic 不是一樣的內(nèi)容,接受者讀消息時(shí)沒法反推出消息的 Topic,因此需要在消息發(fā)送時(shí)根據(jù)通配符的訂閱多寫一個(gè)通配符隊(duì)列,這樣接受者直接可以根據(jù)其訂閱的通配符隊(duì)列讀取消息。
上圖描述的接受我們的隊(duì)列存儲(chǔ)模型,消息可以來(lái)自各個(gè)接入場(chǎng)景(如服務(wù)端的 MQ/AMQP,客戶端的 MQTT),但只會(huì)寫一份存到 commitlog 里面,然后分發(fā)出多個(gè)需求場(chǎng)景的隊(duì)列索引(ConsumerQueue),如服務(wù)端場(chǎng)景(MQ/AMQP)可以按照一級(jí) Topic 隊(duì)列進(jìn)行傳統(tǒng)的服務(wù)端消費(fèi),客戶端 MQTT 場(chǎng)景可以按照 MQTT 多級(jí) Topic 以及通配符訂閱進(jìn)行消費(fèi)消息。
這樣的一個(gè)隊(duì)列模型就可以同時(shí)支持服務(wù)端和終端場(chǎng)景的接入和消息收發(fā),達(dá)到一體化的目標(biāo)。
- 推拉模型
介紹了底層的隊(duì)列存儲(chǔ)模型后,我們?cè)僭敿?xì)描述一下匹配查找和可靠觸達(dá)是怎么做的。
上圖展示的是一個(gè)推拉模型,圖中的 P 節(jié)點(diǎn)是一個(gè)協(xié)議網(wǎng)關(guān)或 broker 插件,終端設(shè)備通過(guò) MQTT 協(xié)議連到這個(gè)網(wǎng)關(guān)節(jié)點(diǎn)。消息可以來(lái)自多種場(chǎng)景(MQ/AMQP/MQTT)發(fā)送過(guò)來(lái),存到 Topic 隊(duì)列后會(huì)有一個(gè) notify 邏輯模塊來(lái)實(shí)時(shí)感知這個(gè)新消息到達(dá),然后會(huì)生成消息事件(就是消息的 Topic 名稱),將該事件推送至網(wǎng)關(guān)節(jié)點(diǎn),網(wǎng)關(guān)節(jié)點(diǎn)根據(jù)其連上的終端設(shè)備訂閱情況進(jìn)行內(nèi)部匹配,找到哪些終端設(shè)備能匹配上,然后會(huì)觸發(fā) pull 請(qǐng)求去存儲(chǔ)層讀取消息再推送終端設(shè)備。
一個(gè)重要問(wèn)題,就是 notify 模塊怎么知道一條消息在哪些網(wǎng)關(guān)節(jié)點(diǎn)上面的終端設(shè)備感興趣,這個(gè)其實(shí)就是關(guān)鍵的匹配查找問(wèn)題。一般有兩種方式:1)簡(jiǎn)單的廣播事件;2)集中存儲(chǔ)在線訂閱關(guān)系(如圖中的 lookup 模塊),然后進(jìn)行匹配查找再精準(zhǔn)推送。事件廣播機(jī)制看起來(lái)有擴(kuò)展性問(wèn)題,但是其實(shí)性能并不差,因?yàn)槲覀兺扑偷臄?shù)據(jù)很小就是 Topic 名稱,而且相同 Topic 的消息事件可以合并成一個(gè)事件,我們線上就是默認(rèn)采用的這個(gè)方式。集中存儲(chǔ)在線訂閱關(guān)系,這個(gè)也是常見的一種做法,如保存到 Rds、Redis 等,但要保證數(shù)據(jù)的實(shí)時(shí)一致性也有難度,而且要進(jìn)行匹配查找對(duì)整個(gè)消息的實(shí)時(shí)鏈路 RT 開銷也會(huì)有一定的影響。
可靠觸達(dá)及實(shí)時(shí)性這塊,上圖的推拉過(guò)程中首先是通過(guò)事件通知機(jī)制來(lái)實(shí)時(shí)告知網(wǎng)關(guān)節(jié)點(diǎn),然后網(wǎng)關(guān)節(jié)點(diǎn)通過(guò) Pull 機(jī)制來(lái)?yè)Q取消息,然后 Push 給終端設(shè)備。Pull+Offset 機(jī)制可以保證消息的可靠性,這個(gè)是 RocketMQ 的傳統(tǒng)模型,終端節(jié)點(diǎn)被動(dòng)接受網(wǎng)關(guān)節(jié)點(diǎn)的 Push,解決了終端設(shè)備輕量問(wèn)題,實(shí)時(shí)性方面因?yàn)樾孪⑹录ㄖ獧C(jī)制而得到保障。
上圖中還有一個(gè) Cache 模塊用于做消息隊(duì)列 cache,因?yàn)樵诖髲V播比場(chǎng)景下如果為每個(gè)終端設(shè)備都去發(fā)起隊(duì)列 Pull 請(qǐng)求則對(duì) broker 讀壓力較大,既然每個(gè)請(qǐng)求都去讀取相同的 Topic 隊(duì)列,則可以復(fù)用本地隊(duì)列 cache。
- lookup組件
上面的推拉模型通過(guò)新消息事件通知機(jī)制來(lái)解決實(shí)時(shí)觸達(dá)問(wèn)題,事件推送至網(wǎng)關(guān)的時(shí)候需要一個(gè)匹配查找過(guò)程,盡管簡(jiǎn)單的事件廣播機(jī)制可以到達(dá)一定的性能要求,但畢竟是一個(gè)廣播模型,在大規(guī)模網(wǎng)關(guān)節(jié)點(diǎn)接入場(chǎng)景下仍然有性能瓶頸。另外,終端設(shè)備場(chǎng)景有很多狀態(tài)查詢?cè)V求,如查找在線狀態(tài),連接互踢等等,仍然需要一個(gè) KV 查找組件,即 lookup。
我們當(dāng)然可以使用外部 KV 存儲(chǔ)如 Redis,但我們不能假定系統(tǒng)(產(chǎn)品)在用戶的交付環(huán)境,尤其是專有云的特殊環(huán)境一定有可靠的外部存儲(chǔ)服務(wù)依賴。
這個(gè) lookup 查詢組件,實(shí)際上就是一個(gè) KV 查詢,可以理解為是一個(gè)分布式內(nèi)存 KV,但要比分布式 KV 實(shí)現(xiàn)難度至少低一個(gè)等級(jí)。我們回想一下一個(gè)分布式 KV 的基本要素有哪些:
如上圖所示,一般一個(gè)分布式 KV 讀寫流程是,Key 通過(guò) hash 得到一個(gè)邏輯 slot,slot 通過(guò)一個(gè)映射表得到具體的 node。Hash 算法一般是固定模數(shù),映射表一般是集中式配置或使用一致性協(xié)議來(lái)配置。節(jié)點(diǎn)擴(kuò)縮一般通過(guò)調(diào)整映射表來(lái)實(shí)現(xiàn)。
分布式 KV 實(shí)現(xiàn)通常有三個(gè)基本關(guān)鍵點(diǎn):
1)映射表一致性
讀寫都需要根據(jù)上圖的映射表進(jìn)行查找節(jié)點(diǎn)的,如果規(guī)則不一致數(shù)據(jù)就亂了。映射規(guī)則配置本身可以通過(guò)集中存儲(chǔ),或者 zk、raft 這類協(xié)議保證強(qiáng)一致性,但是新舊配置的切換不能保證節(jié)點(diǎn)同時(shí)進(jìn)行,仍然存在不一致性窗口。
2)多副本
通過(guò)一致性協(xié)議同步存儲(chǔ)多個(gè)備份節(jié)點(diǎn),用于容災(zāi)或多讀。
3)負(fù)載分配
slot 映射 node 就是一個(gè)分配,要保證 node 負(fù)載均衡,比如擴(kuò)縮情況可能要進(jìn)行 slot 數(shù)據(jù)遷移等。
我們主要查詢和保存的是在線狀態(tài)數(shù)據(jù),如果存儲(chǔ)的 node 節(jié)點(diǎn)宕機(jī)丟失數(shù)據(jù),我們可以即時(shí)重建數(shù)據(jù),因?yàn)槎际窃诰€的,所以不需要考慮多副本問(wèn)題,也不需要考慮擴(kuò)縮情況 slot 數(shù)據(jù)遷移問(wèn)題,因?yàn)榭梢灾苯觼G失重建,只需要保證關(guān)鍵的一點(diǎn):映射表的一致性,而且我們有一個(gè)兜底機(jī)制——廣播,當(dāng)分片數(shù)據(jù)不可靠或不可用時(shí)退化到廣播機(jī)制。
架構(gòu)設(shè)計(jì)
基于前面的理論和模型分析介紹,我們?cè)诳紤]用什么架構(gòu)形態(tài)來(lái)支持一體化的目標(biāo),我們從分層、擴(kuò)展、交付等方面進(jìn)行一下描述。
- 分層架構(gòu)
我們的目標(biāo)是期望基于 RocketMQ 實(shí)現(xiàn)一體化且自閉環(huán),但不希望 Broker 被侵入更多場(chǎng)景邏輯,我們抽象了一個(gè)協(xié)議計(jì)算層,這個(gè)計(jì)算層可以是一個(gè)網(wǎng)關(guān),也可以是一個(gè) broker 插件。Broker 專注解決 Queue 的事情以及為了滿足上面的計(jì)算需求做一些 Queue 存儲(chǔ)的適配或改造。協(xié)議計(jì)算層負(fù)責(zé)協(xié)議接入,并且要可插拔部署。
- 擴(kuò)展設(shè)計(jì)
我們都知道消息產(chǎn)品屬于 PaaS 產(chǎn)品,與上層 SaaS 業(yè)務(wù)貼得最近,為了適應(yīng)業(yè)務(wù)的不同需求,我們大致梳理一下關(guān)鍵的核心鏈路,在上下行鏈路上添加一些擴(kuò)展點(diǎn),如鑒權(quán)邏輯這個(gè)最偏業(yè)務(wù)化的邏輯,不同的業(yè)務(wù)需求都不一樣,又比如 Bridge 擴(kuò)展,其能夠把終端設(shè)備狀態(tài)和消息數(shù)據(jù)與一些外部生態(tài)系統(tǒng)(產(chǎn)品)打通。
- 交付設(shè)計(jì)
好的架構(gòu)設(shè)計(jì)還是要考慮最終的落地問(wèn)題,即怎么交付。如今面臨的現(xiàn)狀是公共云、專有云,甚至是開源等各種環(huán)境條件的落地,挑戰(zhàn)非常大。其中最大的挑戰(zhàn)是外部依賴問(wèn)題,如果產(chǎn)品要強(qiáng)依賴一個(gè)外部系統(tǒng)或產(chǎn)品,那對(duì)整個(gè)交付就會(huì)有非常大的不確定性。
為了應(yīng)對(duì)各種復(fù)雜的交付場(chǎng)景,一方面會(huì)設(shè)計(jì)好擴(kuò)展接口,根據(jù)交付環(huán)境條件進(jìn)行適配實(shí)現(xiàn);另一方面,我們也會(huì)盡可能對(duì)一些模塊提供默認(rèn)內(nèi)部實(shí)現(xiàn),如上文提到的 lookup 組件,重復(fù)造輪子也是不得已而為之,這個(gè)也許就是做產(chǎn)品與做平臺(tái)的最大區(qū)別。
統(tǒng)一存儲(chǔ)內(nèi)核
前面對(duì)整個(gè)協(xié)議模型和架構(gòu)進(jìn)行了詳細(xì)介紹,在 Broker 存儲(chǔ)層這塊還需要進(jìn)一步的改造和適配。我們希望基于 RocketMQ 統(tǒng)一存儲(chǔ)內(nèi)核來(lái)支撐終端和服務(wù)端的消息收發(fā),實(shí)現(xiàn)一體化的目標(biāo)。
前面也提到了終端消息場(chǎng)景和服務(wù)端一個(gè)很大的區(qū)別是,終端必須要有個(gè)客戶端維度的隊(duì)列才能保證可靠觸達(dá),而服務(wù)端可以使用集中式隊(duì)列,因?yàn)橄㈦S便哪臺(tái)機(jī)器消費(fèi)都可以,但是終端消息必須明確可靠推送給具體客戶端。客戶端維度的隊(duì)列意味著數(shù)量級(jí)上比傳統(tǒng)的 RocketMQ 服務(wù)端 Topic 隊(duì)列要大得多。
另外前面介紹的隊(duì)列模型里面,消息也是按照 Topic 隊(duì)列進(jìn)行存儲(chǔ)的,MQTT 的 Topic 是一個(gè)靈活的多級(jí) Topic,客戶端可以任意生成,而不像服務(wù)端場(chǎng)景 Topic 是一個(gè)很重的元數(shù)據(jù)強(qiáng)管理,這個(gè)也意味著 Topic 隊(duì)列的數(shù)量級(jí)很大。
海量隊(duì)列
我們都知道像 Kafka 這樣的消息隊(duì)列每個(gè) Topic 是獨(dú)立文件,但是隨著 Topic 增多消息文件數(shù)量也增多,順序?qū)懢屯嘶闪穗S機(jī)寫,性能下降明顯。RocketMQ 在 Kafka 的基礎(chǔ)上進(jìn)行了改進(jìn),使用了一個(gè) Commitlog 文件來(lái)保存所有的消息內(nèi)容,再使用 CQ 索引文件來(lái)表示每個(gè) Topic 里面的消息隊(duì)列,因?yàn)?CQ 索引數(shù)據(jù)較小,文件增多對(duì) IO 影響要小很多,所以在隊(duì)列數(shù)量上可以達(dá)到十萬(wàn)級(jí)。然而這終端設(shè)備隊(duì)列場(chǎng)景下,十萬(wàn)級(jí)的隊(duì)列數(shù)量還是太小了,我們希望進(jìn)一步提升一個(gè)數(shù)量級(jí),達(dá)到百萬(wàn)級(jí)隊(duì)列數(shù)量,我們引入了 Rocksdb 引擎來(lái)進(jìn)行 CQ 索引分發(fā)。
Rocksdb 是一個(gè)廣泛使用的單機(jī) KV 存儲(chǔ)引擎,具有高性能的順序?qū)懩芰ΑR驗(yàn)槲覀冇辛?commitlog 已具備了消息順序流存儲(chǔ),所以可以去掉 Rocksdb 引擎里面的 WAL,基于 Rocksdb 來(lái)保存 CQ 索引。在分發(fā)的時(shí)候我們使用了 Rocksdb 的 WriteBatch 原子特性,分發(fā)的時(shí)候把當(dāng)前的 MaxPhyOffset 注入進(jìn)去,因?yàn)?Rocksdb 能夠保證原子存儲(chǔ),后續(xù)可以根據(jù)這個(gè) MaxPhyOffset 來(lái)做 Recover 的 checkpoint。我們提供了一個(gè) Compaction 的自定義實(shí)現(xiàn),來(lái)進(jìn)行 PhyOffset 的確認(rèn),以清理已刪除的臟數(shù)據(jù)。
輕量Topic
我們都知道 RocketMQ 中的 Topic 是一個(gè)重要的元數(shù)據(jù),使用前要提前創(chuàng)建,并且會(huì)注冊(cè)到 namesrv 上,然后通過(guò) Topicroute 進(jìn)行服務(wù)發(fā)現(xiàn)。前面說(shuō)了,終端場(chǎng)景訂閱的 Topic 比較靈活可以任意生成,如果基于現(xiàn)有的 RocketMQ 的 Topic 重管理邏輯顯然有些困難。我們定義了一種輕量的 Topic,專門支持終端這種場(chǎng)景,不需要注冊(cè) namesrv 進(jìn)行管理,由上層協(xié)議邏輯層進(jìn)行自管理,broker 只負(fù)責(zé)存儲(chǔ)。
總結(jié)
本文首先介紹了端云消息場(chǎng)景一體化的背景,然后重點(diǎn)分析了終端消息場(chǎng)景特點(diǎn),以及終端消息場(chǎng)景支撐模型,最后對(duì)架構(gòu)和存儲(chǔ)內(nèi)核進(jìn)行了闡述。我們期望基于 RocketMQ 統(tǒng)一內(nèi)核一體化支持終端和服務(wù)端不同場(chǎng)景的消息接入目標(biāo),以能夠給使用者帶來(lái)一體化的價(jià)值,如降低存儲(chǔ)成本,避免數(shù)據(jù)在不同系統(tǒng)間同步帶來(lái)的一致性挑戰(zhàn)。
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。?
總結(jié)
以上是生活随笔為你收集整理的RocketMQ 端云一体化设计与实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java应用结构规范
- 下一篇: Apsara Stack 技术百科 |