本文梳理筆者 MQ 知識,從消息中間件的基礎(chǔ)知識講起,在有了基礎(chǔ)知識后,對市面上各主流的消息中間件進(jìn)行詳細(xì)的解析,包括 RabbitMQ、RocketMQ、Kafka、Pulsar,最后再橫向?qū)Ρ冗@幾款主流的消息中間件。本篇是系列文章第二篇。?第一篇:關(guān)于MQ,你了解多少
RocketMQ
基礎(chǔ)概念
Tag
Tag(標(biāo)簽)可以看作子主題,它是消息的第二級類型,用于為用戶提供額外的靈活性。使用標(biāo)簽,同一業(yè)務(wù)模塊不同目的的消息就可以用相同 Topic 而不同的 Tag 來標(biāo)識。比如交易消息又可以分為:交易創(chuàng)建消息、交易完成消息等,一條消息可以沒有 Tag 。標(biāo)簽有助于保持你的代碼干凈和連貫,并且還可以為 RocketMQ 提供的查詢系統(tǒng)提供幫助。
Group
RocketMQ 中,訂閱者的概念是通過消費(fèi)組(Consumer Group)來體現(xiàn)的。每個消費(fèi)組都消費(fèi)主題中一份完整的消息,不同消費(fèi)組之間消費(fèi)進(jìn)度彼此不受影響,也就是說,一條消息被 Consumer Group1 消費(fèi)過,也會再給 Consumer Group2 消費(fèi)。消費(fèi)組中包含多個消費(fèi)者,同一個組內(nèi)的消費(fèi)者是競爭消費(fèi)的關(guān)系,每個消費(fèi)者負(fù)責(zé)消費(fèi)組內(nèi)的一部分消息。默認(rèn)情況,如果一條消息被消費(fèi)者 Consumer1 消費(fèi)了,那同組的其他消費(fèi)者就不會再收到這條消息。
Offset
在 Topic 的消費(fèi)過程中,由于消息需要被不同的組進(jìn)行多次消費(fèi),所以消費(fèi)完的消息并不會立即被刪除,這就需要? RocketMQ 為每個消費(fèi)組在每個隊列上維護(hù)一個消費(fèi)位置(Consumer Offset),這個位置之前的消息都被消費(fèi)過,之后的消息都沒有被消費(fèi)過,每成功消費(fèi)一條消息,消費(fèi)位置就加一。也可以這么說,Queue 是一個長度無限的數(shù)組,Offset 就是下標(biāo)。
RocketMQ 架構(gòu)
RabbitMQ 類似有生產(chǎn)階段、存儲階段、消費(fèi)階段,相較 RabbitMQ 的架構(gòu),增加了 NameServer 集群,橫向拓展能力較好。參考的 Kafka 做的設(shè)計,故也同樣擁有 NIO、PageCache、順序讀寫、零拷貝的技能,單機(jī)的吞吐量在十萬級,橫向拓展能力較強(qiáng),官方聲明集群下能承載萬億級吞吐。??
存儲階段,可以通過配置可靠性優(yōu)先的 Broker 參數(shù)來避免因為宕機(jī)丟消息,簡單說就是可靠性優(yōu)先的場景都應(yīng)該使用同步。??
1、消息只要持久化到 CommitLog(日志文件)中,即使 Broker 宕機(jī),未消費(fèi)的消息也能重新恢復(fù)再消費(fèi)。?
2、Broker 的刷盤機(jī)制:同步刷盤和異步刷盤,不管哪種刷盤都可以保證消息一定存儲在 Pagecache 中(內(nèi)存中),但是同步刷盤更可靠,它是 Producer 發(fā)送消息后等數(shù)據(jù)持久化到磁盤之后再返回響應(yīng)給 Producer。?
Broker 通過主從模式來保證高可用,Broker 支持 Master 和 Slave 同步復(fù)制、Master 和 Slave 異步復(fù)制模式,生產(chǎn)者的消息都是發(fā)送給 Master,但是消費(fèi)既可以從 Master 消費(fèi),也可以從 Slave 消費(fèi)。同步復(fù)制模式可以保證即使 Master 宕機(jī),消息肯定在 Slave 中有備份,保證了消息不會丟失。??
Consumer 的配置文件中,并不需要設(shè)置是從 Master 讀還是從 Slave 讀,當(dāng) Master 不可用或者繁忙的時候, Consumer 的讀請求會被自動切換到從 Slave。有了自動切換 Consumer 這種機(jī)制,當(dāng)一個 Master 角色的機(jī)器出現(xiàn)故障后,Consumer 仍然可以從 Slave 讀取消息,不影響 Consumer 讀取消息,這就實(shí)現(xiàn)了讀的高可用。? ??
如何達(dá)到發(fā)送端寫的高可用性呢?在創(chuàng)建 Topic 的時候,把 Topic 的多個 Message Queue 創(chuàng)建在多個 Broker 組上(相同 Broker 名稱,不同 BrokerId 機(jī)器組成 Broker 組),這樣當(dāng) Broker 組的 Master 不可用后,其他組Master 仍然可用, Producer 仍然可以發(fā)送消息。
此架構(gòu)下的 RocketMQ 不支持把 Slave 自動轉(zhuǎn)成 Master ,如果機(jī)器資源不足,需要把 Slave 轉(zhuǎn)成 Master ,則要手動停止 Slave 色的 Broker ,更改配置文件,用新的配置文件啟動 Broker。由此,在高可用場景下此問題變得棘手,故需要引入分布式算法的實(shí)現(xiàn),追求 CAP,但實(shí)踐情況是不能同事滿足 CA的,在互聯(lián)網(wǎng)場景下較多是在時間 BASE 理論,優(yōu)先滿足 AP,盡可能去滿足 C。RocketMQ 引入的是實(shí)現(xiàn) Raft 算法的 Dledger,擁有了選舉能力,主從切換,架構(gòu)拓?fù)鋱D是這樣的:
分布式算法中比較常常聽到的是 Paxos 算法,但是由于 Paxos 算法難于理解,且實(shí)現(xiàn)比較困難,所以不太受業(yè)界歡迎。然后出現(xiàn)新的分布式算法 Raft,其比 Paxos 更容易懂與實(shí)現(xiàn),到如今在實(shí)際中運(yùn)用的也已經(jīng)很成熟,不同的語言都有對其的實(shí)現(xiàn)。Dledger 就是其中一個 Java 語言的實(shí)現(xiàn),其將算法方面的內(nèi)容全部抽象掉,這樣開發(fā)人員只需要關(guān)系業(yè)務(wù)即可,大大降低使用難度。
事務(wù)消息
生產(chǎn)者將消息發(fā)送至 Apache RocketMQ 服務(wù)端。
Apache RocketMQ 服務(wù)端將消息持久化成功之后,向生產(chǎn)者返回 Ack 確認(rèn)消息已經(jīng)發(fā)送成功,此時消息被標(biāo)記為"暫不能投遞",這種狀態(tài)下的消息即為半事務(wù)消息。
生產(chǎn)者開始執(zhí)行本地事務(wù)邏輯。
生產(chǎn)者根據(jù)本地事務(wù)執(zhí)行結(jié)果向服務(wù)端提交二次確認(rèn)結(jié)果(Commit 或是 Rollback),服務(wù)端收到確認(rèn)結(jié)果后處理邏輯如下:
二次確認(rèn)結(jié)果為 Commit:服務(wù)端將半事務(wù)消息標(biāo)記為可投遞,并投遞給消費(fèi)者。
二次確認(rèn)結(jié)果為 Rollback:服務(wù)端將回滾事務(wù),不會將半事務(wù)消息投遞給消費(fèi)者。
在斷網(wǎng)或者是生產(chǎn)者應(yīng)用重啟的特殊情況下,若服務(wù)端未收到發(fā)送者提交的二次確認(rèn)結(jié)果,或服務(wù)端收到的二次確認(rèn)結(jié)果為 Unknown 未知狀態(tài),經(jīng)過固定時間后,服務(wù)端將對消息生產(chǎn)者即生產(chǎn)者集群中任一生產(chǎn)者實(shí)例發(fā)起消息回查。說明 服務(wù)端回查的間隔時間和最大回查次數(shù),請參見[參數(shù)限制](https://rocketmq.apache.org/zh/docs/introduction/03limits/)。
生產(chǎn)者收到消息回查后,需要檢查對應(yīng)消息的本地事務(wù)執(zhí)行的最終結(jié)果。
生產(chǎn)者根據(jù)檢查到的本地事務(wù)的最終狀態(tài)再次提交二次確認(rèn),服務(wù)端仍按照步驟4對半事務(wù)消息進(jìn)行處理。
事務(wù)消息生命周期
初始化:半事務(wù)消息被生產(chǎn)者構(gòu)建并完成初始化,待發(fā)送到服務(wù)端的狀態(tài)。
事務(wù)待提交:半事務(wù)消息被發(fā)送到服務(wù)端,和普通消息不同,并不會直接被服務(wù)端持久化,而是會被單獨(dú)存儲到事務(wù)存儲系統(tǒng)中,等待第二階段本地事務(wù)返回執(zhí)行結(jié)果后再提交。此時消息對下游消費(fèi)者不可見。
消息回滾:第二階段如果事務(wù)執(zhí)行結(jié)果明確為回滾,服務(wù)端會將半事務(wù)消息回滾,該事務(wù)消息流程終止。
提交待消費(fèi):第二階段如果事務(wù)執(zhí)行結(jié)果明確為提交,服務(wù)端會將半事務(wù)消息重新存儲到普通存儲系統(tǒng)中,此時消息對下游消費(fèi)者可見,等待被消費(fèi)者獲取并消費(fèi)。
消費(fèi)中:消息被消費(fèi)者獲取,并按照消費(fèi)者本地的業(yè)務(wù)邏輯進(jìn)行處理的過程。此時服務(wù)端會等待消費(fèi)者完成消費(fèi)并提交消費(fèi)結(jié)果,如果一定時間后沒有收到消費(fèi)者的響應(yīng),Apache RocketMQ 會對消息進(jìn)行重試處理。具體信息,請參見消費(fèi)重試。
消費(fèi)提交:消費(fèi)者完成消費(fèi)處理,并向服務(wù)端提交消費(fèi)結(jié)果,服務(wù)端標(biāo)記當(dāng)前消息已經(jīng)被處理(包括消費(fèi)成功和失?。?。Apache RocketMQ 默認(rèn)支持保留所有消息,此時消息數(shù)據(jù)并不會立即被刪除,只是邏輯標(biāo)記已消費(fèi)。消息在保存時間到期或存儲空間不足被刪除前,消費(fèi)者仍然可以回溯消息重新消費(fèi)。
消息刪除:Apache RocketMQ 按照消息保存機(jī)制滾動清理最早的消息數(shù)據(jù),將消息從物理文件中刪除。更多信息,請參見消息存儲和[清理機(jī)制](https://rocketmq.apache.org/zh/docs/featureBehavior/11messagestorepolicy/)。
RocketMQ 新發(fā)展
在過去“分”往往是技術(shù)實(shí)現(xiàn)的妥協(xié),而現(xiàn)在“合”才是用戶的真正需求。RocketMQ 5.0 基于統(tǒng)一 Commitlog 擴(kuò)展多元化索引,包括時間索引、百萬隊列索引、事務(wù)索引、KV索引、批量索引、邏輯隊列等技術(shù)。在場景上同時支撐了 RabbitMQ、Kafka、MQTT、邊緣輕量計算等產(chǎn)品能力,努力實(shí)現(xiàn)“消息、事件、流”的擴(kuò)展支持,云原生是主流。
更多信息可查看官網(wǎng) [Apache RocketMQ](https://rocketmq.apache.org/zh/)。
Kafka
Kafka 是一個分布式系統(tǒng),由通過高性能 TCP 網(wǎng)絡(luò)協(xié)議進(jìn)行通信的服務(wù)器和客戶端組成。它可以部署在本地和云環(huán)境中的裸機(jī)硬件、虛擬機(jī)和容器上。
服務(wù)器:Kafka 作為一個或多個服務(wù)器集群運(yùn)行,可以跨越多個數(shù)據(jù)中心或云區(qū)域。其中一些服務(wù)器形成存儲層,稱為代理。其他服務(wù)器運(yùn)行 Kafka Connect 以事件流的形式持續(xù)導(dǎo)入和導(dǎo)出數(shù)據(jù),以將 Kafka 與您現(xiàn)有的系統(tǒng)(例如關(guān)系數(shù)據(jù)庫以及其他 Kafka 集群)集成。為了讓您實(shí)現(xiàn)關(guān)鍵任務(wù)用例,Kafka 集群具有高度可擴(kuò)展性和容錯性:如果其中任何一臺服務(wù)器發(fā)生故障,其他服務(wù)器將接管它們的工作以確保連續(xù)運(yùn)行而不會丟失任何數(shù)據(jù)。
客戶端:它們允許您編寫分布式應(yīng)用程序和微服務(wù),即使在出現(xiàn)網(wǎng)絡(luò)問題或機(jī)器故障的情況下,也能以容錯的方式并行、大規(guī)模地讀取、寫入和處理事件流。Kafka 附帶了一些這樣的客戶端,這些客戶端由 Kafka 社區(qū)提供的 數(shù)十個客戶端進(jìn)行了擴(kuò)充:客戶端可用于 Java 和 Scala,包括更高級別的 Kafka Streams 庫,用于 Go、Python、C/C++ 和許多其他編程語言以及 REST API。
架構(gòu)
與前面兩個 MQ 類似有生產(chǎn)階段、存儲階段、消費(fèi)階段,相比 RocketMQ 這里的注冊中心是用的 Zookeeper,Kafka 的諸多事件都依賴于 ZK,元數(shù)據(jù)管理、各個角色的注冊、心跳、選舉、狀態(tài)維護(hù),這里的角色包括 Boker、 Topic、 Partition、 消費(fèi)者組等。??
所以這里也會帶來 ZK Watch 事件壓力過大的問題,大量的 ZK 節(jié)點(diǎn)事件阻塞在隊列中, 導(dǎo)致自旋鎖, 導(dǎo)致 CPU 上升, 由于大量數(shù)量事件對象導(dǎo)致占用了大量的內(nèi)存。
圖中的 Controller 是 Kakfa 服務(wù)端 Broker 的概念,Broker 集群有多臺,但只有一臺 Broker 可以扮演控制器的角色;某臺 Broker 一旦成為 Controller,它用于以下權(quán)力:完成對集群成員管理、主題維護(hù)和分區(qū)的管理,如集群 Broker 信息、Topic 維護(hù)、Partition 維護(hù)、分區(qū)選舉 ISR、同步元信息給其他 Broker 等。
存儲
Topic 是邏輯上的概念,而 Partition 是物理上的概念,即一個 Topic 劃分為多個 Partition,每個 Partition 對應(yīng)一個Log文件。?
.log文件:存儲消息數(shù)據(jù)的文件。? ??
.index文件:索引文件,記錄一條消息在log文件中的位置。??
.snapshot文件:記載著生產(chǎn)者最新的offset。??
.timeindex時間索引文件:當(dāng)前日志分段文件中建立索引的消息的時間戳,是在 0.10.0 版本后增加的,用于根據(jù)時間戳快速查找特定消息的位移值,優(yōu)化 Kafka 讀取歷史消息緩慢的問題。為了保證時間戳的單調(diào)遞增,可以將log.message.timestamp.type 設(shè)置成 logApendTime,而 CreateTime 不能保證是消息寫入時間。??
上圖是三個 Broker、兩個 Topic、兩個 Partition 的 Broker ?的存儲情況,可以延伸想象一下百萬級 Topic 的存儲情況會很復(fù)雜。
Rebalnce 問題
為了解決強(qiáng)依賴 Zookeeper 進(jìn)行 Rebalance 帶來的問題,Kafka 引入了 Coordinator 機(jī)制。??
首先,觸發(fā) Rebalance (再均衡)操作的場景目前分為以下幾種:消費(fèi)者組內(nèi)消費(fèi)者數(shù)量發(fā)生變化,包括:?
有新消費(fèi)者加入
有消費(fèi)者宕機(jī)下線,包括真正宕機(jī),或者長時間 GC、網(wǎng)絡(luò)延遲導(dǎo)致消費(fèi)者未在超時時間內(nèi)向 GroupCoordinator 發(fā)送心跳,也會被認(rèn)為下線。??
有消費(fèi)者主動退出消費(fèi)者組(發(fā)送 LeaveGroupRequest 請求) 比如客戶端調(diào)用了 unsubscrible() 方法取消對某些主題的訂閱
消費(fèi)者組對應(yīng)的 GroupCoordinator 節(jié)點(diǎn)發(fā)生了變化。??
消費(fèi)者組訂閱的主題發(fā)生變化(增減)或者主題分區(qū)數(shù)量發(fā)生了變化。??
節(jié)點(diǎn)擴(kuò)容?
更多信息可查看 Kafka 官網(wǎng) [Apache Kafka](https://kafka.apache.org/)
Pulsar
在最高層,一個 Pulsar 實(shí)例由一個或多個 Pulsar 集群組成。一個實(shí)例中的集群可以在它們之間復(fù)制數(shù)據(jù)。
在 Pulsar 集群中:
一個或多個 Broker 處理和負(fù)載平衡來自生產(chǎn)者的傳入消息,將消息分派給消費(fèi)者,與 Pulsar 配置存儲通信以處理各種協(xié)調(diào)任務(wù),將消息存儲在 BookKeeper 實(shí)例(又名 bookies)中,依賴于特定集群的 ZooKeeper 集群用于某些任務(wù)等等。
由一個或多個 Bookie 組成的 BookKeeper 集群處理消息的持久存儲。
特定于該集群的 ZooKeeper 集群處理 Pulsar 集群之間的協(xié)調(diào)任務(wù)。
下圖展示了一個 Pulsar 集群:
?
Pulsar 用 Apache BookKeeper 作為持久化存儲,Broker 持有 BookKeeper client,把未確認(rèn)的消息發(fā)送到 BookKeeper 進(jìn)行保存。
BookKeeper 是一個分布式的 WAL(Write Ahead Log)系統(tǒng),Pulsar 使用 BookKeeper 有下面幾個便利:
可以為 Topic 創(chuàng)建多個 Ledgers:Ledger 是一個只追加的數(shù)據(jù)結(jié)構(gòu),并且只有一個 Writer,這個 Writer 負(fù)責(zé)多個 BookKeeper 存儲節(jié)點(diǎn)(就是 Bookies)的寫入。Ledger 的條目會被復(fù)制到多個 Bookies;
Broker 可以創(chuàng)建、關(guān)閉和刪除 Ledger,也可以追加內(nèi)容到 Ledger;
Ledger 被關(guān)閉后,只能以只讀狀態(tài)打開,除非要明確地寫數(shù)據(jù)或者是因為 Writer 掛掉導(dǎo)致的關(guān)閉;
Ledger 只能有 Writer 這一個進(jìn)程寫入,這樣寫入不會有沖突,所以寫入效率很高。如果 Writer 掛了,Ledger 會啟動恢復(fù)進(jìn)程來確定 Ledger 最終狀態(tài)和最后提交的日志,保證之后所有 Ledger 進(jìn)程讀取到相同的內(nèi)容;??
除了保存消息數(shù)據(jù)外,還會保存 Cursors,也就是消費(fèi)端訂閱消費(fèi)的位置。這樣所有 Cursors 消費(fèi)完一個 Ledger 的消息后這個 Ledger 就可以被刪除,這樣可以實(shí)現(xiàn) Ledgers 的定期翻滾從頭寫。
節(jié)點(diǎn)對等
從架構(gòu)圖可以看出,Broker 節(jié)點(diǎn)不保存數(shù)據(jù),所有 Broker 節(jié)點(diǎn)都是對等的。如果一個 Broker 宕機(jī)了,不會丟失任何數(shù)據(jù),只需要把它服務(wù)的 Topic 遷移到一個新的 Broker 上就行。??
Broker 的 Topic 擁有多個邏輯分區(qū),同時每個分區(qū)又有多個 Segment。??
Writer 寫數(shù)據(jù)時,首先會選擇 Bookies,比如圖中的 Segment1。選擇了 Bookie1、Bookie2、Bookie4,然后并發(fā)地寫下去。這樣這 3 個節(jié)點(diǎn)并沒有主從關(guān)系,協(xié)調(diào)完全依賴于 Writer,因此它們也是對等的。
擴(kuò)展和擴(kuò)容
在遇到雙十一等大流量的場景時,必須增加 Consumer。
這時因為 Broker 不存儲任何數(shù)據(jù),可以方便的增加 Broker。Broker 集群會有一個或多個 Broker 做消息負(fù)載均衡。當(dāng)新的 Broker 加入后,流量會自動從壓力大的 Broker 上遷移過來。??
對于 BookKeeper,如果對存儲要求變高,比如之前存儲 2 個副本現(xiàn)在需要存儲 4 個副本,這時可以單獨(dú)擴(kuò)展 Bookies 而不用考慮 Broker。因為節(jié)點(diǎn)對等,之前節(jié)點(diǎn)的 Segment 又堆放整齊,加入新節(jié)點(diǎn)并不用搬移數(shù)據(jù)。Writer 會感知新的節(jié)點(diǎn)并優(yōu)先選擇使用。
容錯機(jī)制
對于 Broker,因為不保存任何數(shù)據(jù),如果節(jié)點(diǎn)宕機(jī)了就相當(dāng)于客戶端斷開,重新連接其他的 Broker 就可以了。
對于 BookKeeper,保存了多份副本并且這些副本都是對等的。因為沒有主從關(guān)系,所以當(dāng)一個節(jié)點(diǎn)宕機(jī)后,不用立即恢復(fù)。后臺有一個線程會檢查宕機(jī)節(jié)點(diǎn)的數(shù)據(jù)備份進(jìn)行恢復(fù)。
在遇到雙十一等大流量的場景時,必須增加 Consumer。
這時因為 Broker 不存儲任何數(shù)據(jù),可以方便的增加 Broker。Broker 集群會有一個或多個 Broker 做消息負(fù)載均衡。當(dāng)新的 Broker 加入后,流量會自動從壓力大的 Broker 上遷移過來。??
對于 BookKeeper,如果對存儲要求變高,比如之前存儲 2 個副本現(xiàn)在需要存儲 4 個副本,這時可以單獨(dú)擴(kuò)展 Bookies 而不用考慮 Broker。因為節(jié)點(diǎn)對等,之前節(jié)點(diǎn)的 Segment 又堆放整齊,加入新節(jié)點(diǎn)并不用搬移數(shù)據(jù)。Writer 會感知新的節(jié)點(diǎn)并優(yōu)先選擇使用。
Pulsar 可以使用多租戶來管理大集群。Pulsar 的租戶可以跨集群分布,每個租戶都可以有單獨(dú)的認(rèn)證和授權(quán)機(jī)制。租戶也是存儲配額、消息 TTL 和隔離策略的管理單元。
在和其他組件或者生態(tài)對接方面,Pulsar 可以支持很多種消息協(xié)議,對于存量系統(tǒng)的MQ首次接入、切換MQ都很方便。
更多信息可查看 Pulsar 官網(wǎng)?[Apache Pulsar](https://pulsar.apache.org/)
對比
此圖摘抄自《面渣逆襲:RocketMQ二十三問》
這個圖沒有 Pulsar 的信息,從網(wǎng)上看到的壓測報告來看,Pulsar 吞吐量大概是 Kafka 的兩倍左右,延遲表現(xiàn)比 Kafka 低不少,Pulsar 的 I/O 隔離顯著優(yōu)于 Kafka。比較詳實(shí)的 Pulsar 和 Kafka 的比對可以查閱 StreamNative 的文章《Pulsar和Kafka基準(zhǔn)測試:Pulsar 性能精準(zhǔn)解析(完整版)》,StreamNative 作為 Apache Pulsar 的商業(yè)化公司,數(shù)據(jù)和結(jié)果還是比較可靠的。
進(jìn)階
常言道,最好的學(xué)習(xí)方法是帶著問題去尋找答案,在路上撿拾更多果實(shí),增加經(jīng)驗值,快速升級。很多人推薦費(fèi)曼學(xué)習(xí)法,以教代學(xué),按可以教別人的標(biāo)準(zhǔn)來學(xué)習(xí),最終產(chǎn)出教學(xué)內(nèi)容為目的來學(xué)習(xí)一個知識,能讓自己高效學(xué)習(xí)。在我看來這很像績效考核用的 OKR 工具,為項目設(shè)定關(guān)鍵成果,實(shí)現(xiàn)成功應(yīng)該做什么?怎么做?而我寫這篇文章是在實(shí)踐費(fèi)曼學(xué)習(xí)法。??
所以,在這里我給出幾個問題,讀者可以根據(jù)自己的興趣愛好帶著問題去尋找答案吧。??
如何保證消息的可用性/可靠性/不丟失呢?
如何處理消息重復(fù)的問題呢?
順序消息如何實(shí)現(xiàn)?
怎么處理消息積壓?
怎么實(shí)現(xiàn)分布式消息事務(wù)的?半消息?
如何實(shí)現(xiàn)消息過濾?
如果自己平時想到的問題太多,不知道先看哪一個,那么自己想清楚為什么要學(xué)這些知識點(diǎn),哪個問題對于當(dāng)前的自己收益最大。
編輯:黃飛
?
評論
查看更多