天天看點

面試必背:RocketMQ精選面試題

1、RocketMQ Broker中的消息被消費後會立即删除嗎?

不會,每條消息都會持久化到CommitLog中,每個Consumer連接配接到Broker後會維持消費進度資訊,當有消息消費後隻是目前Consumer的消費進度(`CommitLog的offset)更新了。

追問:那麼消息會堆積嗎?什麼時候清理過期消息?

預設72小時後會删除不再使用的CommitLog檔案

檢查這個檔案最後通路時間

判斷是否大于過期時間

指定時間删除,預設淩晨4點

2、RocketMQ消費模式有幾種?

消費模型由Consumer決定,消費次元為Topic。

叢集消費

一條消息隻會被同Group中的一個Consumer消費

多個Group同時消費一個Topic時,每個Group都會有一個Consumer消費到資料

廣播消費

消息将對一個Consumer Group下的各個Consumer執行個體都消費一遍。即使這些Consumer屬于同一個Consumer Group,消息也會被Consumer Group中的每個Consumer都消費一次。

/**
     * 3. 設定消息模式,預設是CLUSTERING
     * MessageModel.BROADCASTING 廣播消費模式
     * MessageModel.CLUSTERING   叢集消費模式
     */
    consumer.setMessageModel(MessageModel.BROADCASTING);
           

3、消費消息是push還是pull?

RocketMQ沒有真正意義的push,都是pull,雖然有push類,但實際底層實作采用的是長輪詢機制,即拉取方式。

broker端屬性 longPollingEnable 标記是否開啟長輪詢。預設開啟

追問:為什麼要主動拉取消息而不使用事件監聽方式?

事件驅動方式是建立好長連接配接,由事件(發送資料)的方式來實時推送。

如果broker主動推送消息的話有可能push速度快,消費速度慢的情況,那麼就會造成消息在consumer端堆積過多,同時又不能被其他consumer消費的情況。而pull的方式可以根據目前自身情況來pull,不會造成過多的壓力而造成瓶頸。是以采取了pull的方式。

// 1. 建立消費者(Pull)對象
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("GROUP_TEST");
           
// 1. 建立消費者(Push)對象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("GROUP_TEST");
           

4、broker如何處理拉取請求的?

Consumer首次請求Broker

Broker中是否有符合條件的消息

有 ->響應Consumer

等待下次Consumer的請求

沒有

PullRequestHoldService 來Hold連接配接,每個5s執行一次檢查pullRequestTable有沒有消息,有的話立即推送

每隔1ms檢查commitLog中是否有新消息,有的話寫入到pullRequestTable

當有新消息的時候傳回請求

挂起consumer的請求,即不斷開連接配接,也不傳回資料使用consumer的offset。

5、RocketMQ如何做負載均衡?

1)producer發送消息的負載均衡:預設會輪詢向Topic的所有queue發送消息,以達到消息平均落到不同的queue上;而由于queue可以落在不同的broker上,就可以發到不同broker上(當然也可以指定發送到某個特定的queue上)

2)consumer訂閱消息的負載均衡:假設有5個隊列,兩個消費者,則第一個消費者消費3個隊列,第二個則消費2個隊列,以達到平均消費的效果。而需要注意的是,當consumer的數量大于隊列的數量的話,根據rocketMq的機制,多出來的Consumer 不會去消費資料,是以建議consumer的數量小于或者等于queue的數量,避免不必要的浪費

通過Topic在多Broker中分布式存儲實作。

producer端

發送端指定message queue發送消息到相應的broker,來達到寫入時的負載均衡

提升寫入吞吐量,當多個producer同時向一個broker寫入資料的時候,性能會下降

消息分布在多broker中,為負載消費做準備

預設政策是随機選擇:

producer維護一個index

每次取節點會自增

index向所有broker個數取餘

自帶容錯政策

其他實作:

SelectMessageQueueByHash

hash的是傳入的args

SelectMessageQueueByRandom

SelectMessageQueueByMachineRoom 沒有實作

也可以自定義實作MessageQueueSelector接口中的select方法

MessageQueue select(final List mqs, final Message msg, final Object arg);

consumer端

采用的是平均配置設定算法來進行負載均衡。

其他負載均衡算法

平均配置設定政策(預設)(AllocateMessageQueueAveragely) 環形配置設定政策(AllocateMessageQueueAveragelyByCircle) 手動配置配置設定政策(AllocateMessageQueueByConfig) 機房配置設定政策(AllocateMessageQueueByMachineRoom) 一緻性哈希配置設定政策(AllocateMessageQueueConsistentHash) 靠近機房政策(AllocateMachineRoomNearby)

追問:當消費負載均衡consumer和queue不對等的時候會發生什麼?

Consumer和queue會優先平均配置設定,如果Consumer少于queue的個數,則會存在部分Consumer消費多個queue的情況,如果Consumer等于queue的個數,那就是一個Consumer消費一個queue,如果Consumer個數大于queue的個數,那麼會有部分Consumer空餘出來,白白的浪費了。

6、消息重複消費

影響消息正常發送和消費的重要原因是網絡的不确定性。

引起重複消費的原因

ACK

正常情況下在consumer真正消費完消息後應該發送ack,通知broker該消息已正常消費,從queue中剔除

當ack因為網絡原因無法發送到broker,broker會認為此條消息沒有被消費,此後會開啟消息重投機制把消息再次投遞到consumer

消費模式

在CLUSTERING模式下,消息在broker中會保證相同group的consumer消費一次,但是針對不同group的consumer會推送多次

解決方案

去重操作直接放在了消費端,消費端處理消息的業務邏輯保持幂等性。那麼不管來多少條重複消息,可以實作處理的結果都一樣。

資料庫表

處理消息前,使用消息主鍵在表中帶有限制的字段中insert。建立一張日志表,使用消息主鍵作為表的主鍵,在處理消息前,先insert表,再做消息處理。這樣可以避免消息重複消費

Map

單機時可以使用map ConcurrentHashMap -> putIfAbsent guava cache

Redis

分布式鎖搞起來。

7、如何讓RocketMQ保證消息的順序消費

你們線上業務用消息中間件的時候,是否需要保證消息的順序性?

如果不需要保證消息順序,為什麼不需要?假如我有一個場景要保證消息的順序,你們應該如何保證?

首先多個queue隻能保證單個queue裡的順序,queue是典型的FIFO,天然順序。多個queue同時消費是無法絕對保證消息的有序性的。是以總結如下:

同一topic,同一個QUEUE,發消息的時候一個線程去發送消息,消費的時候 一個線程去消費一個queue裡的消息。

追問:怎麼保證消息發到同一個queue?

Rocket MQ給我們提供了MessageQueueSelector接口,可以自己重寫裡面的接口,實作自己的算法,舉個最簡單的例子:判斷i % 2 == 0,那就都放到queue1裡,否則放到queue2裡。

for (int i = 0; i < 5; i++) {
    Message message = new Message("orderTopic", ("hello!" + i).getBytes());
    producer.send(
        // 要發的那條消息
        message,
        // queue 選擇器 ,向 topic中的哪個queue去寫消息
        new MessageQueueSelector() {
            // 手動 選擇一個queue
            @Override
            public MessageQueue select(
                // 目前topic 裡面包含的所有queue
                List<MessageQueue> mqs,
                // 具體要發的那條消息
                Message msg,
                // 對應到 send() 裡的 args,也就是2000前面的那個0
                Object arg) {
                // 向固定的一個queue裡寫消息,比如這裡就是向第一個queue裡寫消息
                if (Integer.parseInt(arg.toString()) % 2 == 0) {
                    return mqs.get(0);
                } else {
                    return mqs.get(1);
                }
            }
        },
        // 自定義參數:0
        // 2000代表2000毫秒逾時時間
        i, 2000);
}
           

8、RocketMQ如何保證消息不丢失

首先在如下三個部分都可能會出現丢失消息的情況:

Producer端、 Broker端 、 Consumer端

8.1、Producer端如何保證消息不丢失

采取send()同步發消息,發送結果是同步感覺的。

發送失敗後可以重試,設定重試次數。預設3次。

producer.setRetryTimesWhenSendFailed(10);

叢集部署,比如發送失敗了的原因可能是目前Broker當機了,重試的時候會發送到其他Broker上。

8.2、Broker端如何保證消息不丢失

修改刷盤政策為同步刷盤。預設情況下是異步刷盤的。

flushDiskType = SYNC\_FLUSH

叢集部署,主從模式,高可用。

8.3、Consumer端如何保證消息不丢失

完全消費正常後在進行手動ack确認。

9、rocketMQ的消息堆積如何處理

下遊消費系統如果當機了,導緻幾百萬條消息在消息中間件裡積壓,此時怎麼處理?

你們線上是否遇到過消息積壓的生産故障?如果沒遇到過,你考慮一下如何應對?

首先要找到是什麼原因導緻的消息堆積,是Producer太多了,Consumer太少了導緻的還是說其他情況,總之先定位問題。

然後看下消息消費速度是否正常,正常的話,可以通過上線更多consumer臨時解決消息堆積問題

追問:如果Consumer和Queue不對等,上線了多台也在短時間内無法消費完堆積的消息怎麼辦?

準備一個臨時的topic

queue的數量是堆積的幾倍

queue分布到多Broker中

上線一台Consumer做消息的搬運工,把原來Topic中的消息挪到新的Topic裡,不做業務邏輯處理,隻是挪過去

上線N台Consumer同時消費臨時Topic中的資料

改bug

恢複原來的Consumer,繼續消費之前的Topic

追問:堆積時間過長消息逾時了?

RocketMQ中的消息隻會在commitLog被删除的時候才會消失,不會逾時。也就是說未被消費的消息不會存在逾時删除這情況。

追問:堆積的消息會不會進死信隊列?

不會,消息在消費失敗後會進入重試隊列(%RETRY%+ConsumerGroup),18次(預設18次,網上所有文章都說是16次,無一例外。但是我沒搞懂為啥是16次,這不是18個時間嗎 ?)才會進入死信隊列(%DLQ%+ConsumerGroup)。

源碼如下:

public class MessageStoreConfig {
// 每隔如下時間會進行重試,到最後一次時間重試失敗的話就進入死信隊列了。
private String messageDelayLevel = “1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”;
}
           

首先要分析一下消息堆積可能造成的原因

1、如果是機器本身的原因,比如消費者組有幾個消費者服務挂掉了,剩下少量消費者消費能力不足導緻的消費積壓,那就正常重新啟動,然後慢慢再去消費積壓的消息。

2、如果是生産者端由業務暴增引起的生産過快,而消費者端消費能力不足,這個時候就可以采取生産者端限流或者進行消費者擴容;這個時候要注意,如果生産者隻是短期暴增或者消息的業務不是很重要可以采用限流,如果是長期暴增真正的業務量上漲就必須要進行消費者擴容。

3、比如說消費者挂了,然後broker堆積了很多消息,然後可以先把堆積的消息讀到别的地方比如mysql或者es然後去後續進行處理,然後把RocketMQ堆積的消息删掉,啟動消費者保障消費者正常消費,這裡要注意的是删除堆積消息之前,需要停止mq。

10、RocketMQ在分布式事務支援這塊機制的底層原理?

你們用的是RocketMQ?RocketMQ很大的一個特點是對分布式事務的支援,你說說他在分布式事務支援這塊機制的底層原理?

分布式系統中的事務可以使用TCC(Try、Confirm、Cancel)、2pc來解決分布式系統中的消息原子性

RocketMQ 4.3+提供分布事務功能,通過 RocketMQ 事務消息能達到分布式事務的最終一緻

RocketMQ實作方式:

Half Message:預處理消息,當broker收到此類消息後,會存儲到RMQ\_SYS\_TRANS\_HALF\_TOPIC的消息消費隊列中

檢查事務狀态:Broker會開啟一個定時任務,消費RMQ\_SYS\_TRANS\_HALF\_TOPIC隊列中的消息,每次執行任務會向消息發送者确認事務執行狀态(送出、復原、未知),如果是未知,Broker會定時去回調在重新檢查。

逾時:如果超過回查次數,預設復原消息。

也就是他并未真正進入Topic的queue,而是用了臨時queue來放所謂的half message,等送出事務後才會真正的将half message轉移到topic下的queue。

11、如果讓你來動手實作一個分布式消息中間件,整體架構你會如何設計實作?

我個人覺得從以下幾個點回答吧:

需要考慮能快速擴容、天然支援叢集

持久化的姿勢

高可用性

資料0丢失的考慮

服務端部署簡單、client端使用簡單

12、看過RocketMQ 的源碼沒有。如果看過,說說你對RocketMQ 源碼的了解?

裡面比較典型的設計模式有單例、工廠、政策、門面模式。單例工廠無處不在,政策印象深刻比如發消息和消費消息的時候queue的負載均衡就是N個政策算法類,有随機、hash等,這也是能夠快速擴容天然支援叢集的必要原因之一。持久化做的也比較完善,采取的CommitLog來落盤,同步異步兩種方式。

13、高吞吐量下如何優化生産者和消費者的性能?

開發

同一group下,多機部署,并行消費

單個Consumer提高消費線程個數

批量消費

消息批量拉取

業務邏輯批量處理

運維

網卡調優

jvm調優

多線程與cpu調優

Page Cache

14、說說RocketMQ是如何保證資料的高容錯性的?

在不開啟容錯的情況下,輪詢隊列進行發送,如果失敗了,重試的時候過濾失敗的Broker

如果開啟了容錯政策,會通過RocketMQ的預測機制來預測一個Broker是否可用。

如果上次失敗的Broker可用那麼還是會選擇該Broker的隊列

如果上述情況失敗,則随機選擇一個進行發送

在發送消息的時候會記錄一下調用的時間與是否報錯,根據該時間去預測broker的可用時間。其實就是send消息的時候queue的選擇。

源碼:

org.apache.rocketmq.client.latency.MQFaultStrategy#selectOneMessageQueue()

15、任何一台Broker突然當機了怎麼辦?

Broker主從架構以及多副本政策。

Master收到消息後會同步給Slave,Master當機了還有slave中的消息可用,保證了MQ的可靠性和高可用性。而且Rocket MQ4.5.1開始就支援了Dlegder模式,基于raft做到了真正意義的HA。

16、Broker把自己的資訊注冊到哪個NameServer上?

Broker會向所有的NameServer上注冊自己的資訊,而不是某一個,是全部。多線程的方式向所有NameServer上注冊。’

17、RocketMQ由哪些角色組成,每個角色作用和特點是什麼?

Nameserver 無狀态,動态清單;這也是和zookeeper的重要差別之一。zookeeper是有狀态的。

Producer 消息生産者,負責發消息到Broker。

Broker 就是MQ本身,負責收發消息、持久化消息等。

Consumer 消息消費者,負責從Broker上拉取消息進行消費,消費完進行ack。

18、RocketMQ中的Topic和JMS的queue有什麼差別?

queue就是來源于資料結構的FIFO隊列。而Topic是個抽象的概念,每個Topic底層對應N個queue,而資料也真實存在queue上的。

19、為什麼要用RocketMq?

總得來說,RocketMq具有以下幾個優勢:

吞吐量高:單機吞吐量可達十萬級

可用性高:分布式架構

消息可靠性高:經過參數優化配置,消息可以做到0丢失

功能支援完善:MQ功能較為完善,還是分布式的,擴充性好

支援10億級别的消息堆積:不會因為堆積導緻性能下降

源碼是java:友善我們檢視源碼了解它的每個環節的實作邏輯,并針對不同的業務場景進行擴充

可靠性高:天生為金融網際網路領域而生,對于要求很高的場景,尤其是電商裡面的訂單扣款,以及業務削峰,在大量交易湧入時,後端可能無法及時處理的情況

穩定性高:RoketMQ在上可能更值得信賴,這些業務場景在阿裡雙11已經經曆了多次考驗

20、RocketMq的部署架構了解嗎?

這個是rocketMq的叢集架構圖,裡面包含了四個主要部分:NameServer叢集,Producer叢集,Cosumer叢集以及Broker叢集

NameServer 擔任路由消息的提供者。生産者或消費者能夠通過NameServer查找各Topic相應的Broker IP清單分别進行發送消息和消費消息。nameServer由多個無狀态的節點構成,節點之間無任何資訊同步

broker會定期向NameServer以發送心跳包的方式,輪詢向所有NameServer注冊以下中繼資料資訊:

1)broker的基本資訊(ip port等)

2)主題topic的位址資訊

3)broker叢集資訊

4)存活的broker資訊

5)filter 過濾器

也就是說,每個NameServer注冊的資訊都是一樣的,而且是目前系統中的所有broker的中繼資料資訊

Producer負責生産消息,一般由業務系統負責生産消息。一個消息生産者會把業務應用系統裡産生的消息發送到broker伺服器。RocketMQ提供多種發送方式,同步發送、異步發送、順序發送、單向發送。同步和異步方式均需要Broker傳回确認資訊,單向發送不需要

Broker,消息中轉角色,負責存儲消息、轉發消息。在RocketMQ系統中負責接收從生産者發送來的消息并存儲、同時為消費者的拉取請求作準備

Consumer負責消費消息,一般是背景系統負責異步消費。一個消息消費者會從Broker伺服器拉取消息、并将其提供給應用程式。從使用者應用的角度而言提供了兩種消費形式:拉取式消費、推動式消費

21、它有哪幾種部署類型?分别有什麼特點?

RocketMQ有4種部署類型

1)單Master

單機模式, 即隻有一個Broker, 如果Broker當機了, 會導緻RocketMQ服務不可用, 不推薦使用

2)多Master模式

組成一個叢集, 叢集每個節點都是Master節點, 配置簡單, 性能也是最高, 某節點當機重新開機不會影響RocketMQ服務

缺點:如果某個節點當機了, 會導緻該節點存在未被消費的消息在節點恢複之前不能被消費

3)多Master多Slave模式,異步複制

每個Master配置一個Slave, 多對Master-Slave, Master與Slave消息采用異步複制方式, 主從消息一緻隻會有毫秒級的延遲

優點是彌補了多Master模式(無slave)下節點當機後在恢複前不可訂閱的問題。在Master當機後, 消費者還可以從Slave節點進行消費。采用異步模式複制,提升了一定的吞吐量。總結一句就是,采用多Master多Slave模式,異步複制模式進行部署,系統将會有較低的延遲和較高的吞吐量

缺點就是如果Master當機, 磁盤損壞的情況下, 如果沒有及時将消息複制到Slave, 會導緻有少量消息丢失

4)多Master多Slave模式,同步雙寫

與多Master多Slave模式,異步複制方式基本一緻,唯一不同的是消息複制采用同步方式,隻有master和slave都寫成功以後,才會向用戶端傳回成功

優點:資料與服務都無單點,Master當機情況下,消息無延遲,服務可用性與資料可用性都非常高

缺點就是會降低消息寫入的效率,并影響系統的吞吐量

實際部署中,一般會根據業務場景的所需要的性能和消息可靠性等方面來選擇後兩種

22、你自己部署過RocketMq嗎?簡單說一下你當時部署的過程

由于我們項目中主要使用rocketMq做鍊路跟蹤功能,是以需要比較高的性能,并且偶爾丢失幾條消息也關系不大,是以我們就選擇多Master多Slave模式,異步複制方式進行部署

部署過程簡單說一下:

我部署的是雙master和雙slave模式叢集,并部署了兩個nameserver節點

1)伺服器配置設定

配置設定是兩台伺服器,A和B,其中A伺服器部署nameserv1,master1,slave2;B伺服器部署nameserv2,master2和slave1節點

2)broker的配置

分别配置rocketmq安裝目錄下四個配置檔案:

master1:/conf/2m-2s-async/broker-a.properties
slave2:/conf/2m-2s-async/broker-b-s.properties
master2:/conf/2m-2s-async/broker-b.properties
slave1:/conf/2m-2s-async/broker-a-s.properties
           

總的思路是:

a.master節點的brokerId為0,slave節點的brokerId為1(大于0即可);

b.同一組broker的broker-Name相同,如master1和slave1都為broker-a;

c.每個broker節點配置相同的NameServer;

d.複制方式配置:master節點配置為ASYNC-MASTER,slave節點配置為SLAVE即可;

e.刷盤方式分為同步刷盤和異步刷盤,為了保證性能而不去考慮少量消息的丢失,是以統一配置為異步刷盤

3)啟動叢集

a 檢查修改參數

啟動前分别檢查修改runbroker.sh和runserver.sh兩個檔案中的JVM參數,預設的JAVA\_OPT參數的值比較大,若直接啟動可能會失敗,需要根據實際情況重新配置

b 分别啟動兩個namerser節點

nohup sh bin/mqnamesrv > /dev/null 2>&1 &

檢視日志

tail -f ~/logs/rocketmqlogs/namesrv.log

c 分别啟動4個broker節點

maste1

nohup sh bin/mqbroker -c

/usr/local/rocketmq/conf/2m-2s-async/broker-a.properties &

slave1

nohup sh bin/mqbroker -c

/usr/local/rocketmq/conf/2m-2s-async/broker-a-s.properties &

maste2

nohup sh bin/mqbroker -c

/usr/local/rocketmq/conf/2m-2s-async/broker-b.properties &

slave2

nohup sh bin/mqbroker -c

/usr/local/rocketmq/conf/2m-2s-async/broker-b-s.properties &

檢視日志:

tail -f ~/logs/rocketmqlogs/broker.log

總結:叢集環境部署,主要就是以上三個步驟,需要注意的是過程中broker配置檔案的配置正确性,還需要注意一下啟動前對jvm參數的檢查

23、rocketmq如何保證高可用性?

1)叢集化部署NameServer。Broker叢集會将所有的broker基本資訊、topic資訊以及兩者之間的映射關系,輪詢存儲在每個NameServer中(也就是說每個NameServer存儲的資訊完全一樣)。是以,NameServer叢集化,不會因為其中的一兩台伺服器挂掉,而影響整個架構的消息發送與接收;

2)叢集化部署多broker。producer發送消息到broker的master,若目前的master挂掉,則會自動切換到其他的master

cosumer預設會通路broker的master節點擷取消息,那麼master節點挂了之後,該怎麼辦呢?它就會自動切換到同一個broker組的slave節點進行消費

那麼你肯定會想到會有這樣一個問題:consumer要是直接消費slave節點,那master在當機前沒有來得及把消息同步到slave節點,那這個時候,不就會出現消費者不就取不到消息的情況了?

這樣,就引出了下一個措施,來保證消息的高可用性

3)設定同步複制

前面已經提到,消息發送到broker的master節點上,master需要将消息複制到slave節點上,rocketmq提供兩種複制方式:同步複制和異步複制

異步複制,就是消息發送到master節點,隻要master寫成功,就直接向用戶端傳回成功,後續再異步寫入slave節點

同步複制,就是等master和slave都成功寫入記憶體之後,才會向用戶端傳回成功

那麼,要保證高可用性,就需要将複制方式配置成同步複制,這樣即使master節點挂了,slave上也有目前master的所有備份資料,那麼不僅保證消費者消費到的消息是完整的,并且當master節點恢複之後,也容易恢複消息資料

在master的配置檔案中直接配置brokerRole:SYNC\_MASTER即可

24、rocketmq的工作流程是怎樣的?

RocketMq的工作流程如下:

1)首先啟動NameServer。NameServer啟動後監聽端口,等待Broker、Producer以及Consumer連上來

2)啟動Broker。啟動之後,會跟所有的NameServer建立并保持一個長連接配接,定時發送心跳包。心跳包中包含目前Broker資訊(ip、port等)、Topic資訊以及Borker與Topic的映射關系

3)建立Topic。建立時需要指定該Topic要存儲在哪些Broker上,也可以在發送消息時自動建立Topic

4)Producer發送消息。啟動時先跟NameServer叢集中的其中一台建立長連接配接,并從NameServer中擷取目前發送的Topic所在的Broker;然後從隊列清單中輪詢選擇一個隊列,與隊列所在的Broker建立長連接配接,進行消息的發送

5)Consumer消費消息。跟其中一台NameServer建立長連接配接,擷取目前訂閱Topic存在哪些Broker上,然後直接跟Broker建立連接配接通道,進行消息的消費

25、RocketMq的存儲機制了解嗎?

RocketMq采用檔案系統進行消息的存儲,相對于ActiveMq采用關系型資料庫進行存儲的方式就更直接,性能更高了

RocketMq與Kafka在寫消息與發送消息上,繼續沿用了Kafka的這兩個方面:順序寫和零拷貝

1)順序寫

我們知道,作業系統每次從磁盤讀寫資料的時候,都需要找到資料在磁盤上的位址,再進行讀寫。而如果是機械硬碟,尋址需要的時間往往會比較長而一般來說,如果把資料存儲在記憶體上面,少了尋址的過程,性能會好很多;

但Kafka 的資料存儲在磁盤上面,依然性能很好,這是為什麼呢?

這是因為,Kafka采用的是順序寫,直接追加資料到末尾。實際上,磁盤順序寫的性能極高,在磁盤個數一定,轉數一定的情況下,基本和記憶體速度一緻

是以,磁盤的順序寫這一機制,極大地保證了Kafka本身的性能

2)零拷貝

比如:讀取檔案,再用socket發送出去這一過程

buffer = File.read

Socket.send(buffer)

傳統方式實作:

先讀取、再發送,實際會經過以下四次複制

1、将磁盤檔案,讀取到作業系統核心緩沖區Read Buffer

2、将核心緩沖區的資料,複制到應用程式緩沖區Application Buffer

3、将應用程式緩沖區Application Buffer中的資料,複制到socket網絡發送緩沖區

4、将Socket buffer的資料,複制到網卡,由網卡進行網絡傳輸

面試必背:RocketMQ精選面試題

傳統方式,讀取磁盤檔案并進行網絡發送,經過的四次資料copy是非常繁瑣的

重新思考傳統IO方式,會注意到在讀取磁盤檔案後,不需要做其他處理,直接用網絡發送出去的這種場景下,第二次和第三次資料的複制過程,不僅沒有任何幫助,反而帶來了巨大的開銷。那麼這裡使用了零拷貝,也就是說,直接由核心緩沖區Read Buffer将資料複制到網卡,省去第二步和第三步的複制。

那麼采用零拷貝的方式發送消息,必定會大大減少讀取的開銷,使得RocketMq讀取消息的性能有一個質的提升

此外,還需要再提一點,零拷貝技術采用了MappedByteBuffer記憶體映射技術,采用這種技術有一些限制,其中有一條就是傳輸的檔案不能超過2G,這也就是為什麼RocketMq的存儲消息的檔案CommitLog的大小規定為1G的原因

小結:RocketMq采用檔案系統存儲消息,并采用順序寫寫入消息,使用零拷貝發送消息,極大得保證了RocketMq的性能

26、RocketMq的存儲結構是怎樣的?

如圖所示,消息生産者發送消息到broker,都是會按照順序存儲在CommitLog檔案中,每個commitLog檔案的大小為1G

面試必背:RocketMQ精選面試題

CommitLog-存儲所有的消息中繼資料,包括Topic、QueueId以及message

CosumerQueue-消費邏輯隊列:存儲消息在CommitLog的offset

IndexFile-索引檔案:存儲消息的key和時間戳等資訊,使得RocketMq可以采用key和時間區間來查詢消息

也就是說,rocketMq将消息均存儲在CommitLog中,并分别提供了CosumerQueue和IndexFile兩個索引,來快速檢索消息

27、RocketMq性能比較高的原因?

就是前面在檔案存儲機制中所提到的:RocketMq采用檔案系統存儲消息,采用順序寫的方式寫入消息,使用零拷貝發送消息,這三者的結合極大地保證了RocketMq的性能

私信我:面試題,擷取面試題合集

繼續閱讀