Java ZooKeeper-RocketMQ 面試題
- 前言
- 1、談談你對ZooKeeper的理解 ?
- 2、Zookeeper的工作原理(Zab協議)
- 3、談談你對分布式鎖的理解,以及分布式鎖的實現?
- 4、 zookeeper 是如何保證事務的順序一致性的?
- 5、 zookeeper主從同步機制:
- 6、分布式集群中為什么會有 Master?
- 7、 zk 節點宕機如何處理?
- 8、說幾個 zookeeper 常用的命令?
- 9、ZK 如何投票實現Leader選舉?
- MQ中間件
- 10、什么是 RocketMq?
- 11、什么是消息隊列?
- 12、RocketMq的路由類型和發送消息的方式?
- 13、死信消息的生命周期?
- 14、如何保證消息的順序性?
- 15、如何防止消息丟失?
- 16、如何保證消息不被重復消費?
- 17、MQ處理消息失敗了怎么辦?
- 18、消息基于什么傳輸?
- 19、RocketMQ 底層原理?
- 20、RocketMQ為什么速度快, 吞吐量?
- 21、什么是零拷貝?
- 22、死信隊列?
- 總結
前言
最新的 Java 面試題,技術棧涉及 Java 基礎、集合、多線程、Mysql、分布式、Spring全家桶、MyBatis、Dubbo、緩存、消息隊列、Linux…等等,會持續更新。
如果對老鐵有幫助,幫忙免費點個贊,謝謝你的發財手!
1、談談你對ZooKeeper的理解 ?
ZooKeeper 是一個開源的分布式協調服務,為分布式系統提高了一系列的服務,提供的服務包括了:服務注冊與訂閱、統一命名服務、集群管理、分布式鎖和分布式通知等。
Zookeeper提供了三個核心功能,分別是:文件系統、監聽機制和集群管理
- 1、文件系統
Zookeeper存儲數據的結構,類似于一個文件系統,每個節點(znode)都是類似于K-V的結構,每個節點的名字相當于key,每個節點保存的數據,相當于value。 - 2、監聽機制
客戶端先向ZooKeeper服務端的某個節點注冊一個 Watcher 監聽,當監聽的數據狀態發生變化時,服務端會向指定客戶端發送一個事件通知,客戶端收到事件以后,調用對應的回調方法,完成事件變更的通知。 - 3、集群管理
Zookeeper提供了CP的模型,來保證集群中的每個節點的數據一致性。
zookeeper本身是一個集群結構,它有3種角色:
leader領導者:處理所有的事務請求(寫請求),也可以處理讀請求,集群中只有一個leader;
follower追隨者:只能處理讀請求,參與leader選舉
observer觀察者:只能處理讀請求,不參與leader選舉,作用是為了提升集群的讀性能
2、Zookeeper的工作原理(Zab協議)
- Zookeeper的工作原理核心是原子廣播機制,這個機制保證了各個節點之間的數據一致性,實現這個機制的協議叫做Zab協議。
Zab協議有兩種模式,分別是恢復模式(選主)和廣播模式(同步),當服務啟動或者 Leader 服務器宕機,Zab就進入了恢復模式,此時不對外提供服務。 - 首先選舉出新的 Leader 服務器,然后與其他的Follower 服務器進行數據同步,當集群中超過半數機器完成數據同步之后,退出恢復模式進入廣播模式,然后就可以接收客戶端的事務請求了。
3、談談你對分布式鎖的理解,以及分布式鎖的實現?
- 分布式鎖,是一種跨進程的跨機器節點的互斥鎖,它可以保證同一時刻只能有一個線程去訪問共享資源。
目前實現分布式鎖最常用的中間件是 Redis 和 Zookeeper: - Redis:利用它提供的 SETNX 命令,如果設置key返回1,說明獲取鎖成功,返回0獲取鎖失敗。然后還可以通過 EX參數來設置key的過期時間,從而避免死鎖問題。
但也可能存在一些極端情況,比如鎖過期了,但是業務邏輯還沒處理完。這種極端情況可以使用Redisson 客戶端來實現,Redisson 中有一個 watchdog 的機制,翻譯過來就是看門狗,它是基于Netty下面的一個時間輪的實現類來實現。(getLock、lock、unlock) - Zookeeper:利用它提供的有序節點,當線程創建節點后,如果該節點是當前目錄下所有節點序號最小的節點,則認為獲取鎖成功。如果不是最小的節點,則對該節點序號的前一個節點添加一個監聽事件,當監聽的節點釋放鎖之后,觸發回調通知,從而再次去嘗試搶占鎖。
總的來說,這兩種方案都有各自的優缺點:
Redis它獲取鎖的方式簡單粗暴,如果獲取不到鎖,會不斷嘗試獲取鎖,消耗性能比較大,但是實現難度比較低。
Zookeeper如果獲取不到鎖,只需要添加一個監聽器就可以了,消耗性能比較小,但是實現難度比較高。
4、 zookeeper 是如何保證事務的順序一致性的?
- zookeeper采用了全局遞增的事務Id 來標識,所有的 proposal(提議)在被提出的時候都會加上 zxid,zxid實際上是一個64位的數字,高32位是epoch(時期; 紀元; 世; 新時代)用來標識leader是否發生改變,如果有新的leader產生出來,epoch會自增,然后低32位用來遞增計數。
當產生新的提議時,zookeeper會采用數據庫的二階段提交,首先會向其他的節點發出事務執行請求,如果超過半數的機器都能執行,那么就會提交事務。
5、 zookeeper主從同步機制:
- 1、當leader 接受到消息請求后,會給消息加上一個全局的事務Id (zxid);
- 2、leader將帶有zxid的消息作為一個提案(proposal)分發給所有的follower;
- 3、當follower接受到提案后,先把提案寫到磁盤,寫入成功以后再向leader回復一個ack;
- 4、當leader 接受到半數以上ack,就會向follower發送提交commit命令,同時自己也執行;
- 5、當follower接受到commit命令以后,就會提交該消息,從而實現數據同步。
6、分布式集群中為什么會有 Master?
在分布式環境中,有些業務邏輯只需要集群中的某一臺機器進行執行,其他的機器可以共享這個結果,這樣可以大大減少重復計算,提高性能。
7、 zk 節點宕機如何處理?
- 1、如果是一個 Follower 宕機,只要超過半數的節點正常,集群就能正常提供服務,否則集群就會失效;
- 2、如果是一個 Leader 宕機,Zookeeper會進入恢復模式,重新選舉出新的 Leader。
Zookeeper本身也是集群,推薦不少于 3 個服務器,而且最好奇數個。
8、說幾個 zookeeper 常用的命令?
常用命令:ls get set create delete 等。
9、ZK 如何投票實現Leader選舉?
如果 Leader 節點宕機了,為了保證集群繼續提供服務,Zookeeper 需要從剩下的 Follower 節點里面去選舉一個新的節點作為 Leader,也就是所謂的 Leader 選舉。具體的實現是:
- 1、每一個節點都會向集群里面的其他節點發送一個票據 Vote,這個票據包括三個屬性:epoch邏輯時鐘,zxid事務id,myid服務器id;然后第一輪投票都會投給自己。
- 2、每個節點把收到的票據和自己節點的票據做比較,根據 epoch、zxid、myid 的順序逐一比較,以值最大的一方獲勝,比較結束以后就把票投給獲勝的節點;
- 3、通過多輪投票以后,以少數服從多數的方式,最終獲得票數最多的節點成為Leader。
到這里,leader選舉就結束啦。
其中epoch,邏輯時鐘,用來標識當前票據是否過期;zxid,事務id,用來表示當前節點最新存儲的數據的事務編號; myid,服務器id,在 myid 文件里面填寫的;
MQ中間件
10、什么是 RocketMq?
- RocketMq是一個基于 AMQP 高級消息隊列協議的中間件,接受并轉發消息。它有4個核心組件,分別是:
- producer生產者:負責生產和發送消息到 Broker;
- Exchange交換機:按照一定的規則將消息路由轉發到某個隊列,對消息進行過慮;
- Queue隊列:用來存儲消息,并把消息轉發給指定的消費者;
- consumer消費者:負責從 Broker 中獲取消息,并進行相應處理;
它的工作原理是生產者把消息發送到Exchange交換機上。Exchange交換機把收到的消息根據路由規則,轉發給綁定的queue隊列,隊列再把消息投遞給訂閱了這個隊列的消費者,從而完成消息的異步通訊。
11、什么是消息隊列?
消息隊列 Message Queue,簡稱 MQ。
消息隊列有很多使用場景,比較常見的有3個:解耦、異步、削峰。
- 1、應用解耦:把相關聯的系統進行職責解耦,比如:生成訂單會調用倉庫管理系統或積分系統。
- 2、異步處理:不需要立即處理消息,提高系統的性能,比如:用戶注冊發送驗證碼、下單短信通知、發送優惠券等。
- 3、流量削峰:能夠有效的頂住瞬間高并發,防止服務器承受不住導致崩潰,比如秒殺、限時搶購優惠券等。
比如吞吐量低的中小型公司,一般用 ActiveMQ、RabbitMQ 較為合適,
大數據、吞吐量高的大型公司一般選用 Kafka 和 RocketMQ。
12、RocketMq的路由類型和發送消息的方式?
它的工作原理是生產者把消息發送到Exchange交換機上,Exchange交換機把收到的消息根據路由規則,轉發給綁定的queue隊列,最后再把消息投遞給訂閱了這個隊列的消費者,從而完成消息的異步通訊。
在RabbitMQ中,交換機常見的有3種類型,分別是Fanout、Direct 、Topic:
- Fanout(扇出交換機):類似于廣播機制,將消息轉發給到所有綁定的隊列上,與routingKey(路由鍵)無關;
- Direct(直連交換機):完整匹配方式,也就是Routing key和Binding Key完全一致,才把消息發給該隊列;
- Topic(主題交換機):就是Routing Key加了通配符,符合匹配規則的Queue隊列都會收到這個消息,#:代表0個或多個單詞,*:代表一個單詞。
13、死信消息的生命周期?
- 1、消費者消費業務消息時發生異常,就會返回nck或者reject操作;
- 2、那么這些消息就會被投遞到死信交換機中;
- 3、死信交換機將消息發送到相應的死信隊列;
- 4、死信隊列可以定時重新投遞到Broker中,也可以由死信消費者消費。
14、如何保證消息的順序性?
這個問題是由于不同的消息都發送到了同一個 queue隊列中,而這個queue隊列又被多個消費者消費。
解決這個問題,我們可以給 RabbitMQ 創建多個 queue隊列,每個隊列對應一個消費者。比如生產者發送消息的時候,同一個訂單號的消息發送到同一個 queue隊列中,由于同一個 queue隊列的消息是有序的,那么同一個訂單號的消息就只會被一個消費者順序消費,從而保證了消息的順序性。
15、如何防止消息丟失?
- 1)、使用事務消息
- 2)、使用消息確認機制
消息丟失的場景有下面幾種: - 1)、生產者發送消息到MQ的過程中丟失
解決方法: 在生產者端開啟comfirm 確認模式,每個消息會分配一個唯一的 id,如果MQ寫入了內存中, 就會返回一個ack,告訴你說這個消息ok了,如果MQ沒有寫入這個消息,會回調一個nack接口,告訴你這個消息失敗了,你可以進行重試或拋棄此條消息。 - 2)、MQ收到消息,寫入到內存中,還沒消費,服務掛掉了,數據都會丟失
解決方法:將消息持久化到磁盤,有兩個步驟:
第1步:把該消息設置為持久化,即deliveryMode設置為2,第3個參數設置如下:
channel.basicPublish(“exchange”, “routingKey”, MessageProperties.PERSISTENT_TEXT_PLAIN, " message".getBytes());
第2步:把該queue隊列設置為持久化,即durable=true,第2個參數設置為true,
channel.queueDeclare(QUEUE_NAME, true, false, false, null); - 3)、消費者剛拿到消息,還沒處理,服務掛掉了,MQ又以為消費者處理完了
解決方式:首先關閉 RabbitMQ 的自動 ack,然后消費者在處理完消息之后,再手動返回ack。這樣可以保證,如果你還沒處理完,就不會返回ack,那隊列就不會刪除該消息。
(關閉 RabbitMQ 的自動 ack,即autoAck=false,第2個參數設置為false
channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel){});)
16、如何保證消息不被重復消費?
我們為了消息的可靠性,采用了手動ack機制;如果消費者在處理完一個消息之后,還沒有手動調用ack,服務就掛了,MQ以為你還還沒處理,就把這個消息投遞給其他的消費者,就導致了消息被重復消費。
我覺得這個問題可以分為2種場景來對待:
- 第1種冪等性場景:就是說消息重復消費和消費一次產生的影響是一樣的,可以不用處理,比如Redis的set操作,它是天然冪等性的;
- 第2種非冪等性場景:把消息唯一id保存到 mysql 或者 redis 里面,在處理消息之前先去 mysql 或者 redis 里面,判斷一下是否已經消費過了。
17、MQ處理消息失敗了怎么辦?
一般生產環境中,都會在使用MQ的時候設計兩個隊列:一個是核心業務隊列,一個是死信隊列。核心業務隊列,就是比如專門用來給訂單系統發送訂單消息的,然后另外一個死信隊列就是用來處理異常情況的。
18、消息基于什么傳輸?
RabbitMQ 使用channel通道的方式來傳輸數據,通道是建立在真實的 TCP 連接內的虛擬連接,且通道數量沒有限制,大大提升了MQ的處理性能。
19、RocketMQ 底層原理?
RocketMQ 架構主要包含以下四個部分:
- 1、NameServer:是一個簡單的Topic路由注冊中心,支持Broker的注冊與發現,主要有兩個功能:Broker管理(提供心跳檢測機制)和路由信息管理(保存Broker的路由信息和用于客戶端查詢的隊列信息);
- 2、BrokerServer:RocketMQ 的核心組件,主要負責消息的
1、存儲:CommitLog(存儲消息的文件);
2、投遞:ConsumeQueue(消息消費隊列); - 3.查詢:IndexFile(索引文件);
- 4.以及維護消費者的Topic訂閱信息和保證服務高可用;
1、Producer:消息生產者,往Broker發送指定Topic的消息。
2、Consumer:消息消費者,主動拉取消息來消費,支持集群方式(同一Topic下的一條消息只會發送到同一消費組中的一個消費者)和廣播方式(所有的消息會廣播發送到所有的消費者)。
20、RocketMQ為什么速度快, 吞吐量?
因為使?了順序存儲、頁緩存(Page Cache)和異步刷盤;我們在寫?commitlog的時候是順序寫?的,這樣?隨機寫?的性能就會?很多,而且它不是直接寫?磁盤,?是先寫?頁緩存,開啟一個異步線程通過零拷貝機制,定時的將緩存中的數據刷到磁盤中,從而保證消息的快速讀寫。
21、什么是零拷貝?
- 傳統?件復制方式:需要對?件在內存中進?四次復制(內核態到用戶態的來回復制)。
- 零拷?:就是減少內核態到用戶態的來回復制,它是通過內存映射的機制,直接把內核態里的數據映射到用戶態,對文件的操作轉化為對內存地址的操作;
通常有兩種?式,mmap(RocketMq)和sendfile(kafka)。
22、死信隊列?
當一條消息消費失敗時,經過多次重試依然失敗,為了保證消息數據不丟失,需要將消息投入到死信隊列中。
死信的來源:
- 1、消息 TTL(存活時間)過期
- 2、隊列滿了,無法再添加數據到隊列中
- 3、消息被拒絕(消費方拒絕應答:basic.reject 或 basic.nack)并且不放回隊列中( requeue=false)
總結
都已經看到這里啦,趕緊收藏起來,祝您工作順心,生活愉快!