1. 程式人生 > 其它 >一個用訊息佇列的人,孫鑫Java視訊教程百度網盤

一個用訊息佇列的人,孫鑫Java視訊教程百度網盤

一個用訊息佇列的人,孫鑫Java視訊教程百度網盤

先說一下,博主只會ActiveMQ,RabbitMQ,RocketMQ,Kafka,對什麼ZeroMQ等其他MQ沒啥理解,因此只能基於這四種MQ給出回答。

分析:既然在專案中用了MQ,肯定事先要對業界流行的MQ進行調研,如果連每種MQ的優缺點都沒了解清楚,就拍腦袋依據喜好,用了某種MQ,還是給專案挖坑。

如果面試官問:"你為什麼用這種MQ?。"你直接回答"領導決定的。"這種回答就很LOW了。

還是那句話,不要給公司挖坑。

我們可以看出,RabbitMQ版本釋出比ActiveMq頻繁很多。至於RocketMQ和kafka就不帶大家看了,總之也比ActiveMQ活躍的多。詳情,可自行查閱。

再來一個效能對比表

綜合上面的材料得出以下兩點:

(1)中小型軟體公司,建議選RabbitMQ.

一方面,erlang語言天生具備高併發的特性,而且他的管理介面用起來十分方便。

正所謂,成也蕭何,敗也蕭何!他的弊端也在這裡,雖然RabbitMQ是開源的,然而國內有幾個能定製化開發erlang的程式設計師呢?

所幸,RabbitMQ的社群十分活躍,可以解決開發過程中遇到的bug,這點對於中小型公司來說十分重要。

不考慮rocketmq和kafka的原因是,一方面中小型軟體公司不如網際網路公司,資料量沒那麼大,選訊息中介軟體,應首選功能比較完備的,所以kafka排除。

不考慮rocketmq的原因是,rocketmq是阿里出品,如果阿里放棄維護rocketmq,中小型公司一般抽不出人來進行rocketmq的定製化開發,因此不推薦。

(2)大型軟體公司,根據具體使用在rocketMq和kafka之間二選一

一方面,大型軟體公司,具備足夠的資金搭建分散式環境,也具備足夠大的資料量。

針對rocketMQ,大型軟體公司也可以抽出人手對rocketMQ進行定製化開發,畢竟國內有能力改JAVA原始碼的人,還是相當多的。

至於kafka,根據業務場景選擇,如果有日誌採集功能,肯定是首選kafka了。具體該選哪個,看使用場景。

4、如何保證訊息佇列是高可用的?

分析:在第二點說過了,引入訊息佇列後,系統的可用性下降。在生產中,沒人使用單機模式的訊息佇列。

因此,作為一個合格的程式設計師,應該對訊息佇列的高可用有很深刻的瞭解。

如果面試的時候,面試官問,你們的訊息中介軟體如何保證高可用的?

如果你的回答只是表明自己只會訂閱和釋出訊息,面試官就會懷疑你是不是隻是自己搭著玩,壓根沒在生產用過。

因此,請做一個愛思考,會思考,懂思考的程式設計師。

回答:這問題,其實要對訊息佇列的叢集模式要有深刻了解,才好回答。

以rcoketMQ為例,他的叢集就有多master 模式、多master多slave非同步複製模式、多 master多slave同步雙寫模式。

多master多slave模式部署架構圖(網上找的,偷個懶,懶得畫):

其實博主第一眼看到這個圖,就覺得和kafka好像,只是NameServer叢集,在kafka中是用zookeeper代替,都是用來儲存和發現master和slave用的。

通訊過程如下:

Producer 與 NameServer叢集中的其中一個節點(隨機選擇)建立長連線,定期從 NameServer 獲取 Topic 路由資訊,並向提供 Topic 服務的 Broker Master 建立長連線,且定時向 Broker 傳送心跳。

Producer 只能將訊息傳送到 Broker master,但是 Consumer 則不一樣,它同時和提供 Topic 服務的 Master 和 Slave建立長連線,既可以從 Broker Master 訂閱訊息,也可以從 Broker Slave 訂閱訊息。

那麼kafka呢,為了對比說明直接上kafka的拓補架構圖(也是找的,懶得畫)

如上圖所示,一個典型的Kafka叢集中包含若干Producer(可以是web前端產生的Page View,或者是伺服器日誌,系統CPU、Memory等),若干broker(Kafka支援水平擴充套件,一般broker數量越多,叢集吞吐率越高),若干Consumer Group,以及一個Zookeeper叢集。

Kafka通過Zookeeper管理叢集配置,選舉leader,以及在Consumer Group發生變化時進行rebalance。

Producer使用push模式將訊息釋出到broker,Consumer使用pull模式從broker訂閱並消費訊息。

至於rabbitMQ,也有普通叢集和映象叢集模式,自行去了解,比較簡單,兩小時即懂。

要求,在回答高可用的問題時,應該能邏輯清晰的畫出自己的MQ叢集架構或清晰的敘述出來。

5、如何保證訊息不被重複消費?

分析:這個問題其實換一種問法就是,如何保證訊息佇列的冪等性?

這個問題可以認為是訊息佇列領域的基本問題。換句話來說,是在考察你的設計能力,這個問題的回答可以根據具體的業務場景來答,沒有固定的答案。

回答:先來說一下為什麼會造成重複消費?

其實無論是那種訊息佇列,造成重複消費原因其實都是類似的。

正常情況下,消費者在消費訊息時候,消費完畢後,會發送一個確認資訊給訊息佇列,訊息佇列就知道該訊息被消費了,就會將該訊息從訊息佇列中刪除。只是不同的訊息佇列傳送的確認資訊形式不同

例如RabbitMQ是傳送一個ACK確認訊息,RocketMQ是返回一個CONSUME_SUCCESS成功標誌,kafka實際上有個offset的概念

簡單說一下(如果還不懂,出門找一個kafka入門到精通教程),就是每一個訊息都有一個offset,kafka消費過訊息後,需要提交offset,讓訊息佇列知道自己已經消費過了。

那造成重複消費的原因?

就是因為網路傳輸等等故障,確認資訊沒有傳送到訊息佇列,導致訊息佇列不知道自己已經消費過該訊息了,再次將該訊息分發給其他的消費者。

如何解決?這個問題針對業務場景來答分以下幾點

(1)比如,你拿到這個訊息做資料庫的insert操作。

那就容易了,給這個訊息做一個唯一主鍵,那麼就算出現重複消費的情況,就會導致主鍵衝突,避免資料庫出現髒資料。

(2)再比如,你拿到這個訊息做redis的set的操作

那就容易了,不用解決。因為你無論set幾次結果都是一樣的,set操作本來就算冪等操作。

(3)如果上面兩種情況還不行,上大招。

準備一個第三方介質,來做消費記錄。以redis為例,給訊息分配一個全域性id,只要消費過該訊息,將以K-V形式寫入redis。那消費者開始消費前,先去redis中查詢有沒消費記錄即可。

6、如何保證消費的可靠性傳輸?

分析:我們在使用訊息佇列的過程中,應該做到訊息不能多消費,也不能少消費。如果無法做到可靠性傳輸,可能給公司帶來千萬級別的財產損失。

同樣的,如果可靠性傳輸在使用過程中,沒有考慮到,這不是給公司挖坑麼,你可以拍拍屁股走了,公司損失的錢,誰承擔。

還是那句話,認真對待每一個專案,不要給公司挖坑

回答:其實這個可靠性傳輸,每種MQ都要從三個角度來分析:生產者弄丟資料、訊息佇列弄丟資料、消費者弄丟資料

RabbitMQ

(1)生產者丟資料

從生產者弄丟資料這個角度來看,RabbitMQ提供transaction和confirm模式來確保生產者不丟訊息。

transaction機制就是說,傳送訊息前,開啟事物(channel.txSelect()),然後傳送訊息,如果傳送過程中出現什麼異常,事物就會回滾(channel.txRollback()),如果傳送成功則提交事物(channel.txCommit())。

然而缺點就是吞吐量下降了。因此,按照博主的經驗,生產上用confirm模式的居多。

一旦channel進入confirm模式,所有在該通道上面釋出的訊息都將會被指派一個唯一的ID(從1開始)

一旦訊息被投遞到所有匹配的佇列之後,rabbitMQ就會發送一個Ack給生產者(包含訊息的唯一ID)

這就使得生產者知道訊息已經正確到達目的隊列了.如果rabiitMQ沒能處理該訊息,則會發送一個Nack訊息給你,你可以進行重試操作。

處理Ack和Nack的程式碼如下所示(說好不上程式碼的,偷偷上了):

channel.addConfirmListener(new ConfirmListener() {
    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("nack: deliveryTag = "+deliveryTag+" multiple: "+multiple);
    }
    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("ack: deliveryTag = "+deliveryTag+" multiple: "+multiple);
    }
});

(2)訊息佇列丟資料

處理訊息佇列丟資料的情況,一般是開啟持久化磁碟的配置。

這個持久化配置可以和confirm機制配合使用,你可以在訊息持久化磁碟後,再給生產者傳送一個Ack訊號。

這樣,如果訊息持久化磁碟之前,rabbitMQ陣亡了,那麼生產者收不到Ack訊號,生產者會自動重發。

那麼如何持久化呢,這裡順便說一下吧,其實也很容易,就下面兩步

1、將queue的持久化標識durable設定為true,則代表是一個持久的佇列

2、傳送訊息的時候將deliveryMode=2

這樣設定以後,rabbitMQ就算掛了,重啟後也能恢復資料

(3)消費者丟資料

消費者丟資料一般是因為採用了自動確認訊息模式。

這種模式下,消費者會自動確認收到資訊。這時rahbitMQ會立即將訊息刪除,這種情況下如果消費者出現異常而沒能處理該訊息,就會丟失該訊息。

至於解決方案,採用手動確認訊息即可。

kafka

Producer在釋出訊息到某個Partition時,先通過ZooKeeper找到該Partition的Leader

然後無論該Topic的Replication Factor為多少(也即該Partition有多少個Replica),Producer只將該訊息傳送到該Partition的Leader。

Leader會將該訊息寫入其本地Log。每個Follower都從Leader中pull資料。
針對上述情況,得出如下分析

(1)生產者丟資料

在kafka生產中,基本都有一個leader和多個follwer。follwer會去同步leader的資訊。

因此,為了避免生產者丟資料,做如下兩點配置

  1. 第一個配置要在producer端設定acks=all。這個配置保證了,follwer同步完成後,才認為訊息傳送成功。

  2. 在producer端設定retries=MAX,一旦寫入失敗,這無限重試

(2)訊息佇列丟資料

總結

雖然面試套路眾多,但對於技術面試來說,主要還是考察一個人的技術能力和溝通能力。不同型別的面試官根據自身的理解問的問題也不盡相同,沒有規律可循。

上面提到的關於這些JAVA基礎、三大框架、專案經驗、併發程式設計、JVM及調優、網路、設計模式、spring+mybatis原始碼解讀、Mysql調優、分散式監控、訊息佇列、分散式儲存等等面試題筆記及資料都是免費分享的,有需要學習的小夥伴可以直接點點點點點這裡免費下載

有些面試官喜歡問自己擅長的問題,比如在實際程式設計中遇到的或者他自己一直在琢磨的這方面的問題,還有些面試官,尤其是大廠的比如 BAT 的面試官喜歡問面試者認為自己擅長的,然後通過提問的方式深挖細節,刨根到底。