一、爲何要使用消息隊列?前端
分析:一個用消息隊列的人,不知道爲啥用,這就有點尷尬。沒有複習這點,很容易被問蒙,而後就開始胡扯了。程序員
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:854630135,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。web
回答:這個問題,咱只答三個最主要的應用場景(不能否認還有其餘的,可是隻答三個主要的),即如下六個字:解耦、異步、削峯面試
(1)解耦redis
傳統模式:算法
傳統模式的缺點:數據庫
中間件模式:服務器
中間件模式的的優勢:網絡
(2)異步架構
傳統模式:
傳統模式的缺點:
中間件模式:
中間件模式的的優勢:
(3)削峯
傳統模式
傳統模式的缺點:
中間件模式:
中間件模式的的優勢:
二、使用了消息隊列會有什麼缺點?
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:854630135,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。
分析:一個使用了MQ的項目,若是連這個問題都沒有考慮過,就把MQ引進去了,那就給本身的項目帶來了風險。咱們引入一個技術,要對這個技術的弊端有充分的認識,才能作好預防。要記住,不要給公司挖坑!
回答:回答也很容易,從如下兩個個角度來答
可是,咱們該用仍是要用的。
三、消息隊列如何選型?
先說一下,博主只會ActiveMQ,RabbitMQ,RocketMQ,Kafka,對什麼ZeroMQ等其餘MQ沒啥理解,所以只能基於這四種MQ給出回答。
分析:既然在項目中用了MQ,確定事先要對業界流行的MQ進行調研,若是連每種MQ的優缺點都沒了解清楚,就拍腦殼依據喜愛,用了某種MQ,仍是給項目挖坑。若是面試官問:"你爲何用這種MQ?。"你直接回答"領導決定的。"這種回答就很LOW了。仍是那句話,不要給公司挖坑。
回答:首先,咱先上ActiveMQ的社區,看看該MQ的更新頻率:
Apache ActiveMQ 5.15.3 Release Christopher L. Shannon posted on Feb 12, 2018 Apache ActiveMQ 5.15.2 Released Christopher L. Shannon posted on Oct 23, 2017 Apache ActiveMQ 5.15.0 Released Christopher L. Shannon posted on Jul 06, 2017 省略如下記錄 ...
咱們能夠看出,ActiveMq幾個月才發一次版本,聽說研究重心在他們的下一代產品Apollo。
接下來,咱們再去RabbitMQ的社區去看一下,RabbitMQ的更新頻率
RabbitMQ 3.7.3 release 30 January 2018 RabbitMQ 3.6.15 release 17 January 2018 RabbitMQ 3.7.2 release23 December 2017 RabbitMQ 3.7.1 release21 December 2017 省略如下記錄 ...
咱們能夠看出,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了。具體該選哪一個,看使用場景。
四、如何保證消息隊列是高可用的?
分析:在第二點說過了,引入消息隊列後,系統的可用性降低。在生產中,沒人使用單機模式的消息隊列。所以,做爲一個合格的程序員,應該對消息隊列的高可用有很深入的瞭解。若是面試的時候,面試官問,大家的消息中間件如何保證高可用的?你的回答只是代表本身只會訂閱和發佈消息,面試官就會懷疑你是否是隻是本身搭着玩,壓根沒在生產用過。請作一個愛思考,會思考,懂思考的程序員。
回答:這問題,其實要對消息隊列的集羣模式要有深入瞭解,纔好回答。
以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集羣架構或清晰的敘述出來。
五、如何保證消息不被重複消費?
分析:這個問題其實換一種問法就是,如何保證消息隊列的冪等性?這個問題能夠認爲是消息隊列領域的基本問題。換句話來講,是在考察你的設計能力,這個問題的回答能夠根據具體的業務場景來答,沒有固定的答案。
回答:先來講一下爲何會形成重複消費?
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:854630135,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。
其實不管是那種消息隊列,形成重複消費緣由其實都是相似的。正常狀況下,消費者在消費消息時候,消費完畢後,會發送一個確認信息給消息隊列,消息隊列就知道該消息被消費了,就會將該消息從消息隊列中刪除。只是不一樣的消息隊列發送的確認信息形式不一樣,例如RabbitMQ是發送一個ACK確認消息,RocketMQ是返回一個CONSUME_SUCCESS成功標誌,kafka實際上有個offset的概念,簡單說一下(若是還不懂,出門找一個kafka入門到精通教程),就是每個消息都有一個offset,kafka消費過消息後,須要提交offset,讓消息隊列知道本身已經消費過了。那形成重複消費的緣由?,就是由於網絡傳輸等等故障,確認信息沒有傳送到消息隊列,致使消息隊列不知道本身已經消費過該消息了,再次將該消息分發給其餘的消費者。
如何解決?這個問題針對業務場景來答分如下幾點
(1)好比,你拿到這個消息作數據庫的insert操做。那就容易了,給這個消息作一個惟一主鍵,那麼就算出現重複消費的狀況,就會致使主鍵衝突,避免數據庫出現髒數據。
(2)再好比,你拿到這個消息作redis的set的操做,那就容易了,不用解決,由於你不管set幾回結果都是同樣的,set操做原本就算冪等操做。
(3)若是上面兩種狀況還不行,上大招。準備一個第三方介質,來作消費記錄。以redis爲例,給消息分配一個全局id,只要消費過該消息,將<id,message>以K-V形式寫入redis。那消費者開始消費前,先去redis中查詢有沒消費記錄便可。
六、如何保證消費的可靠性傳輸?
分析:咱們在使用消息隊列的過程當中,應該作到消息不能多消費,也不能少消費。若是沒法作到可靠性傳輸,可能給公司帶來千萬級別的財產損失。一樣的,若是可靠性傳輸在使用過程當中,沒有考慮到,這不是給公司挖坑麼,你能夠拍拍屁股走了,公司損失的錢,誰承擔。仍是那句話,認真對待每個項目,不要給公司挖坑。
回答:其實這個可靠性傳輸,每種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信號,生產者會自動重發。
那麼如何持久化呢,這裏順便說一下吧,其實也很容易,就下面兩步
一、將queue的持久化標識durable設置爲true,則表明是一個持久的隊列
二、發送消息的時候將deliveryMode=2
這樣設置之後,rabbitMQ就算掛了,重啓後也能恢復數據
(3)消費者丟數據
消費者丟數據通常是由於採用了自動確認消息模式。這種模式下,消費者會自動確認收到信息。這時rahbitMQ會當即將消息刪除,這種狀況下若是消費者出現異常而沒能處理該消息,就會丟失該消息。
至於解決方案,採用手動確認消息便可。
kafka
這裏先引一張kafka Replication的數據流向圖
Producer在發佈消息到某個Partition時,先經過ZooKeeper找到該Partition的Leader,而後不管該Topic的Replication Factor爲多少(也即該Partition有多少個Replica),Producer只將該消息發送到該Partition的Leader。Leader會將該消息寫入其本地Log。每一個Follower都從Leader中pull數據。
針對上述狀況,得出以下分析
(1)生產者丟數據
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:854630135,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。
在kafka生產中,基本都有一個leader和多個follwer。follwer會去同步leader的信息。所以,爲了不生產者丟數據,作以下兩點配置
(2)消息隊列丟數據
針對消息隊列丟數據的狀況,無外乎就是,數據還沒同步,leader就掛了,這時zookpeer會將其餘的follwer切換爲leader,那數據就丟失了。針對這種狀況,應該作兩個配置。
這兩個配置加上上面生產者的配置聯合起來用,基本可確保kafka不丟數據
(3)消費者丟數據
這種狀況通常是自動提交了offset,而後你處理程序過程當中掛了。kafka覺得你處理好了。再強調一次offset是幹嗎的
offset:指的是kafka的topic中的每一個消費組消費的下標。簡單的來講就是一條消息對應一個offset下標,每次消費數據的時候若是提交offset,那麼下次消費就會從提交的offset加一那裏開始消費。
好比一個topic中有100條數據,我消費了50條而且提交了,那麼此時的kafka服務端記錄提交的offset就是49(offset從0開始),那麼下次消費的時候offset就從50開始消費。
解決方案也很簡單,改爲手動提交便可。
ActiveMQ和RocketMQ
你們自行查閱吧
七、如何保證消息的順序性?
分析:其實並不是全部的公司都有這種業務需求,可是仍是對這個問題要有所複習。
回答:針對這個問題,經過某種算法,將須要保持前後順序的消息放到同一個消息隊列中(kafka中就是partition,rabbitMq中就是queue)。而後只用一個消費者去消費該隊列。
有的人會問:那若是爲了吞吐量,有多個消費者去消費怎麼辦?
這個問題,沒有固定回答的套路。好比咱們有一個微博的操做,發微博、寫評論、刪除微博,這三個異步操做。若是是這樣一個業務場景,那隻要重試就行。好比你一個消費者先執行了寫評論的操做,可是這時候,微博都還沒發,寫評論必定是失敗的,等一段時間。等另外一個消費者,先執行寫評論的操做後,再執行,就能夠成功。
總之,針對這個問題,個人觀點是保證入隊有序就行,出隊之後的順序交給消費者本身去保證,沒有固定套路。
總結
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:854630135,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。
寫到這裏,但願讀者把本文提出的這幾個問題,通過深入的準備後,通常來講,能囊括大部分的消息隊列的知識點。若是面試官不問這幾個問題怎麼辦,簡單,本身把幾個問題講清楚,突出如下本身考慮的全面性。