MQ(Message Queue)作為一種用於實現非同步通信的技術,具有重要的作用和應用場景。在面試過程中,MQ相關的問題經常被問到,因此瞭解MQ的用途和設計原則是必不可少的。本文總結了MQ的常見面試題,包括MQ的作用、產品選型、消息不丟失的保證、消息消費的冪等性、消息順序的保證、消息的高效讀寫、分佈... ...
MQ有什麼用?
MQ(消息隊列)是一種FIFO(先進先出)的數據結構,主要用於實現非同步通信、削峰平谷和解耦等功能。它通過將生產者生成的消息發送到隊列中,然後由消費者進行消費。這樣,生產者和消費者之間就不存在直接的耦合關係。
其中,MQ的優勢主要體現在以下幾個方面:
- 非同步通信:由於存在MQ這個中間件,生產者將消息發送到隊列後,可以立即返回,無需等待消費者處理完畢。這樣可以提高系統的響應速度和併發能力。
- 削峰平谷:當系統出現峰值請求時,MQ可以存儲大量的請求消息,將峰值數據緩衝下來,然後由消費者按照自己的處理能力逐步消費。這樣可以避免系統因突發流量而崩潰,提高系統的穩定性和可靠性。
- 解耦功能:MQ可以將生產者和消費者兩端分離開來,實現系統之間的解耦。尤其在跨語言的場景下,MQ可以輕鬆實現不同語言程式之間的通信,簡化開發和維護的複雜性。
然而,引入MQ也存在一些劣勢需要註意:
- 高可用性要求:為了保證MQ的正常運行,需要對MQ進行高可用性的設計和部署。一旦MQ宕機,整個業務流程可能會受到影響,導致系統不可用。
- 系統複雜性提高:引入MQ後,需要專門的人員進行維護和管理,並對MQ產品有深入的瞭解。同時,為了保證消息的不丟失和消費冪等性,還需要進行一些額外的工作。
- 系統一致性問題:由於MQ是非同步通信的方式,當一個業務生成後,如果需要兩個系統之間的一致性,就需要保證兩個系統都成功執行完成。否則,可能會出現數據不一致的情況。
綜上所述,MQ在提供非同步通信、削峰平谷和解耦等功能的同時,也需要註意高可用性、系統複雜性和系統一致性等問題。在使用MQ時,需要綜合考慮這些因素,併進行適當的設計和調優。
如何進行產品選型
目前市場上有三大主流MQ產品供選擇,它們分別是kafka、rabbitmq和rocketmq。
- kafka的性能最快,效率最高,適用於處理日誌分析、大數據分析等場景。然而,kafka存在數據丟失的風險,並且功能相對單一,不保證消息的可靠性。
- rabbitmq保證了消息的可靠性,但無法處理大數據量的消息隊列。一旦數據量增大,整個MQ伺服器的性能將下降。因此,rabbitmq適用於小規模場景。
- rocketmq吸取了kafka和rabbitmq的優點,幾乎可以應用於各種場景。它既具有高效率又具有高可靠性。不過需要註意的是,開源版本的rocketmq可能不如商業版本穩定和可靠。
因此,在進行產品選型時,您需要綜合考慮各個MQ產品的特點和適用場景。如果您需要處理大數據量的消息隊列,可以考慮kafka或者商業版本的rocketmq。如果您對消息的可靠性要求較高,可以選擇rabbitmq或者商業版本的rocketmq。
如何保證消息不丟失?
首先,我們要檢查可能導致消息丟失的部分:
- 生產者將消息發送到消息隊列伺服器;
- 消息隊列伺服器宕機;
- 消息隊列伺服器未將消息刷新到磁碟;
- 消息隊列將消息發送給消費者。
然後根據每一步開始分析如何保證消息不丟失;
RocketMQ獨有的事務消息機制:
- 對於使用Kafka、RocketMQ、RabbitMQ的情況,它們都有消息確認機制。例如,消息只有在到達消息隊列後才會返回確認信息。RocketMQ還有獨有的事務消息機制,可以確認消息是否成功發送到消息隊列伺服器,並與相關業務進行關聯。當消息隊列伺服器監聽到生產者伺服器未返回成功時,會持續回調生產者伺服器,直到成功或超時。
- 如果消息隊列伺服器宕機,說明需要保證消息隊列的高可用性。因此,必須使用集群環境。對於RocketMQ來說,它的節點分為主節點和從節點。一旦主節點宕機,從節點會立即啟動,確保消息不丟失。但是主從同步是非同步進行的,因此需要使用Dledger集群的兩階段提交來確保超過半數的機器同步成功後才能返回給生產者。對於RabbitMQ集群,普通集群是分散存儲的,即所有集群的總和等於隊列的總數,沒有備份。這可能導致機器宕機後丟失部分數據,所以RabbitMQ有一個鏡像集群,會主動在節點之間進行同步,解決了數據丟失的問題。至於Kafka,本身允許丟失數據的情況,因此不需要對Kafka進行大量的消息可靠性優化以減少效率問題。但它有一個ack確認機制。
- 對於RocketMQ,可以採用非同步刷盤來確保效率,但如果要確保消息的可靠性,就需要使用同步刷盤機制,即損失一部分效率。對於RabbitMQ,可以設置隊列持久化來確保消息刷盤。
- 當消息隊列將消息投遞給消費者時,消費者自己需要採取相應的策略。對於RocketMQ、RabbitMQ和Kafka,都應將消息的偏移量設置為手動提交,而不是自動提交。否則,如果某個消費者消費失敗,該條消息將會丟失。
如何保證消息消費的冪等性?
為了保證消息消費的冪等性,我們可以採取以下策略。首先,在生產者端,我們需要為每條消息設置一個唯一的業務ID,確保消息的唯一性。這可以通過生成全局唯一的UUID或者使用分散式ID生成演算法來實現。
然後,在消費端,我們可以利用一些中間件,比如Redis,來記錄已經消費過的消息。這可以通過將消費過的消息的業務ID存儲在Redis中來實現。在消費端處理消息之前,我們首先查詢Redis,判斷當前消息的業務ID是否已經存在。如果存在,說明該消息已經被消費過,可以直接忽略。如果不存在,說明該消息是新的,可以進行消費處理。
通過以上的策略,我們可以確保消息的冪等性,避免重覆消費同一條消息。同時,使用中間件來記錄已經消費過的消息,可以提高查詢效率和降低存儲空間的占用。這樣,即使消費端出現異常或者重啟,也能夠保證消息的消費狀態不會丟失,從而保證消息消費的可靠性。
如何保證消息的順序
如何保證消息的順序呢?雖然消息隊列(MQ)本身可以保證局部的消息順序,但並不能保證全局的消息順序。這是因為在實際的系統中,為了提高可用性,通常會使用多個隊列來存儲消息,而無法將同一個業務的消息全部放入同一個隊列中。因此,需要瞭解各種MQ的特性。
RocketMQ提供了有序隊列的實現機制。它在主題(Topic)和隊列(Queue)之間引入了一個Message Select機制,可以將同一個業務的消息發送到同一個隊列中,從而保證消息的有序性。在消費端,如果你使用OrderMessageListen監聽器來消費消息,它會在獲取消息時,鎖定一個隊列,將該隊列中的消息全部消費完,然後再獲取下一個隊列的消息。這樣就能夠保證消息的有序消費。
相比之下,RabbitMQ和Kafka並沒有專門提供對消息順序的支持。如果你確實需要保證消息的順序,你可以將隊列和消費者設置成一個,這樣就能夠保證有序性。但是這種方式效率較低,因此在實際應用中,需要仔細考慮是否真的需要使用有序性。
總之,在設計消息消費時,需要根據實際情況來選擇是否需要保證消息的順序。如果確實需要有序性,可以考慮使用RocketMQ等支持有序隊列的MQ,或者將隊列和消費者設置成一個。但需要註意,有序性可能會犧牲一定的性能,因此需要權衡利弊來做出決策。
如何保證消息的高效讀寫
傳統文件複製方式: 需要對文件在記憶體中進行四次拷貝。
讀寫操作涉及到IO操作,而有關IO操作的優化,我們會想到零拷貝技術。在這方面,Kafka和RocketMQ都採用了零拷貝技術來優化文件讀寫性能。
零拷貝: 有兩種方式, mmap和transfile
- RocketMQ是一個分散式消息隊列系統,它也使用了零拷貝技術來提高性能。RocketMQ通過使用DirectByteBuffer和FileChannel來實現零拷貝。
在消息發送過程中,RocketMQ使用DirectByteBuffer作為消息緩衝區,並將消息直接寫入到DirectByteBuffer中,而無需將數據從用戶空間複製到內核緩衝區。然後,RocketMQ使用FileChannel將DirectByteBuffer中的數據直接寫入到磁碟文件中,避免了數據的多次複製。
在消息消費過程中,RocketMQ同樣使用DirectByteBuffer作為消息緩衝區,並使用FileChannel將磁碟文件中的數據直接讀取到DirectByteBuffer中,而無需將數據從內核緩衝區複製到用戶空間。
通過使用DirectByteBuffer和FileChannel,RocketMQ實現了零拷貝,從而提高了消息發送和消費的效率和性能。
- 在讀取和寫入消息時,Kafka利用零拷貝技術來提高性能。具體來說,Kafka使用操作系統的"sendfile"系統調用,該調用允許直接將文件中的數據發送到網路套接字,而無需將數據從內核緩衝區複製到應用程式緩衝區。這樣可以避免數據的多次複製,提高了數據傳輸的效率。
此外,Kafka還使用了mmap(記憶體映射)技術,它可以將磁碟文件映射到記憶體中。通過使用mmap,Kafka可以避免將數據從磁碟讀取到內核緩衝區,而是直接將文件映射到記憶體中,從而實現快速的數據讀取和寫入。
總的來說,Kafka通過使用"sendfile"系統調用和mmap技術來實現零拷貝,提高了數據的傳輸效率和性能。
使用MQ如何保證分散式事務的最終一致性?
分散式事務是一種要求只要有一個系統處理失敗,整個事務都失敗的機制。換句話說,要麼所有的系統都成功地完成了它們的處理,要麼所有的系統都失敗了。這樣可以確保數據的一致性。
最終一致性則是指在分散式系統中,允許存在中間狀態,只要最終的狀態保持一致即可,而不必要求強一致性。
在實現分散式事務和最終一致性時,有一些關鍵的優化策略:
- 首先,生產者在完成業務處理後,必須確保消息被正確地投遞到MQ伺服器。這是為了防止消息丟失,因為如果消息丟失,就無法保證整個事務的一致性。
- 其次,消費者需要保證消息的消費具有冪等性,即不會重覆消費同一條消息。這可以通過在消費端記錄已經消費過的消息的標識來實現。這樣即使有重覆的消息投遞到消費者,消費者也可以正確地處理,而不會對業務數據造成重覆影響。
讓你設計一個MQ,你會如何設計?
首先,基於現有的MQ基礎上進行定製化設計,不可放飛自我,避免漫無邊際。可以站在現有MQ的巨人肩膀上,確保設計的東西不會出現漏洞。
- 設計隊列時,可以選擇使用阻塞隊列(blockingmq),將消息作為實體存放在隊列中,包括消息體、消息ID等內容。同時,需要考慮單隊列如何進行擴容和縮容的設計。
- 為了提高分散式和效率,可以設計成多隊列的形式。在多隊列的情況下,引入一個中間角色來保存消息,並根據一定的策略將消息放入隊列中,比如輪詢等方式,以保證隊列的均衡。此外,需要考慮生產者的消息確認機制,確保消息的可靠性。
- 為了確保MQ的高可用性,可以設計MQ的高可用集群,保證系統在面對故障時能夠自動切換,提供持續穩定的服務。
- 在多消費者情況下,需要考慮如何從隊列中獲取消息,併進行消費。可以與隊列形成多對一的關係,確保消息能夠被所有消費者平均消費。
- 為了進一步優化MQ的性能,可以考慮使用一些技術,比如順序寫、零拷貝等,提高數據傳輸的效率。
- 最後,可以根據需求定製一些高級功能,如延遲隊列、死信隊列、有序隊列等,以滿足不同場景下的需求。
總結
MQ(Message Queue)是一種重要的技術,用於實現應用程式之間的非同步通信,提高系統的可擴展性和可靠性。在選用MQ產品時,需要考慮以下幾個方面:
- 瞭解不同MQ產品的特點和適用場景,根據實際需求進行產品選型。
- 為了保證消息的可靠傳遞,可以採用持久化機制,確保消息不會丟失。
- 冪等性是保證消息消費的重要概念,可以通過唯一標識和消息狀態進行判斷。
- 保證消息的順序可以採用單一消費者或者分區有序的方式。
- 高效讀寫可以通過批量發送和接收消息、消息壓縮等方式進行優化。
- 在分散式環境下,確保事務的最終一致性可以通過兩階段提交或者最大努力通知等方式實現。
- 在設計一個自己的MQ時,需要考慮消息的存儲和傳輸方式、高可用集群的設計、多消費者消費的問題以及性能優化和定製高級功能等方面。
通過對這些面試題的瞭解和思考,可以更好地理解MQ的作用和設計原則,為面試和實際應用提供參考。