消息隊列優(yōu)點、缺點、為什么要用消息隊列

一個用消息隊列的人,不知道為啥用,有點尷尬。沒有復習這點,很容易被問蒙,然后就開始胡扯了。

回答:這個問題,咱只答三個最主要的應用場景,不可否認還有其他的,但是只答三個主要的,即以下六個字:

解耦、異步、削峰

1、解耦

傳統(tǒng)模式:

傳統(tǒng)模式的缺點:

系統(tǒng)間耦合性太強,如上圖所示,系統(tǒng)A在代碼中直接調(diào)用系統(tǒng)B和系統(tǒng)C的代碼,如果將來D系統(tǒng)接入,系統(tǒng)A還需要修改代碼,過于麻煩!

中間件模式:

中間件模式的的優(yōu)點:

將消息寫入消息隊列,需要消息的系統(tǒng)自己從消息隊列中訂閱,從而系統(tǒng)A不需要做任何修改。

2、異步

傳統(tǒng)模式:

傳統(tǒng)模式的缺點:

一些非必要的業(yè)務(wù)邏輯以同步的方式運行,太耗費時間。

中間件模式:

中間件模式的的優(yōu)點:

將消息寫入消息隊列,非必要的業(yè)務(wù)邏輯以異步的方式運行,加快響應速度

3、削峰

傳統(tǒng)模式

傳統(tǒng)模式的缺點:

并發(fā)量大的時候,所有的請求直接懟到數(shù)據(jù)庫,造成數(shù)據(jù)庫連接異常

中間件模式:

中間件模式的的優(yōu)點:

系統(tǒng)A慢慢的按照數(shù)據(jù)庫能處理的并發(fā)量,從消息隊列中慢慢拉取消息。在生產(chǎn)中,這個短暫的高峰期積壓是允許的。

使用了消息隊列會有什么缺點?

分析:一個使用了MQ的項目,如果連這個問題都沒有考慮過,就把MQ引進去了,那就給自己的項目帶來了風險。

我們引入一個技術(shù),要對這個技術(shù)的弊端有充分的認識,才能做好預防。要記住,不要給公司挖坑!

回答:回答也很容易,從以下兩個個角度來答

系統(tǒng)可用性降低:

你想啊,本來其他系統(tǒng)只要運行好好的,那你的系統(tǒng)就是正常的。

現(xiàn)在你非要加個消息隊列進去,那消息隊列掛了,你的系統(tǒng)不是呵呵了。因此,系統(tǒng)可用性降低

系統(tǒng)復雜性增加:

要多考慮很多方面的問題,比如一致性問題、如何保證消息不被重復消費,如何保證保證消息可靠傳輸。

因此,需要考慮的東西更多,系統(tǒng)復雜性增大。

但是,我們該用還是要用的。

消息隊列如何選型?

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

分析:既然在項目中用了MQ,肯定事先要對業(yè)界流行的MQ進行調(diào)研,如果連每種MQ的優(yōu)缺點都沒了解清楚,就拍腦袋依據(jù)喜好,用了某種MQ,還是給項目挖坑。

如果面試官問:"你為什么用這種MQ?。"

你直接回答"領(lǐng)導決定的。"

這種回答就很LOW了。

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

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

再來一個性能對比表

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

1、中小型軟件公司,建議選RabbitMQ.

一方面,erlang語言天生具備高并發(fā)的特性,而且他的管理界面用起來十分方便。

正所謂,成也蕭何,敗也蕭何!他的弊端也在這里,雖然RabbitMQ是開源的,然而國內(nèi)有幾個能定制化開發(fā)erlang的程序員呢?

所幸,RabbitMQ的社區(qū)十分活躍,可以解決開發(fā)過程中遇到的bug,這點對于中小型公司來說十分重要。

不考慮rocketmq和kafka的原因是,一方面中小型軟件公司不如互聯(lián)網(wǎng)公司,數(shù)據(jù)量沒那么大,選消息中間件,應首選功能比較完備的,所以kafka排除。

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

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

一方面,大型軟件公司,具備足夠的資金搭建分布式環(huán)境,也具備足夠大的數(shù)據(jù)量。

針對rocketMQ,大型軟件公司也可以抽出人手對rocketMQ進行定制化開發(fā),畢竟國內(nèi)有能力改JAVA源碼的人,還是相當多的。

至于kafka,根據(jù)業(yè)務(wù)場景選擇,如果有日志采集功能,肯定是首選kafka了。具體該選哪個,看使用場景。

如何保證消息隊列是高可用的?

在第二點說過了,引入消息隊列后,系統(tǒng)的可用性下降。

在生產(chǎn)中,沒人使用單機模式的消息隊列。

因此,作為一個合格的程序員,應該對消息隊列的高可用有很深刻的了解。

如果面試官問:"你們的消息中間件如何保證高可用的?"

如果你的回答只是表明自己只會訂閱和發(fā)布消息,面試官就會懷疑你是不是只是自己搭著玩,壓根沒在生產(chǎn)用過。

因此,請做一個愛思考,會思考,懂思考的程序員。

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

以rcoketMQ為例,他的集群就有多master 模式、多master多slave異步復制模式、多 master多slave同步雙寫模式。

多master多slave模式部署架構(gòu)圖(網(wǎng)上找的,偷個懶,懶得畫):

其實博主第一眼看到這個圖,就覺得和kafka好像,只是NameServer集群,在kafka中是用zookeeper代替,都是用來保存和發(fā)現(xiàn)master和slave用的。

通信過程如下:

Producer 與 NameServer集群中的其中一個節(jié)點(隨機選擇)建立長連接,定期從 NameServer 獲取 Topic 路由信息,并向提供 Topic 服務(wù)的 Broker Master 建立長連接,且定時向 Broker 發(fā)送心跳。

Producer 只能將消息發(fā)送到 Broker master,但是 Consumer 則不一樣,它同時和提供 Topic 服務(wù)的 Master 和 Slave建立長連接,既可以從 Broker Master 訂閱消息,也可以從 Broker Slave 訂閱消息。

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

如上圖所示,一個典型的Kafka集群中包含若干Producer(可以是web前端產(chǎn)生的Page View,或者是服務(wù)器日志,系統(tǒng)CPU、Memory等),若干broker(Kafka支持水平擴展,一般broker數(shù)量越多,集群吞吐率越高),若干Consumer Group,以及一個Zookeeper集群。

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

Producer使用push模式將消息發(fā)布到broker,Consumer使用pull模式從broker訂閱并消費消息。

至于rabbitMQ,也有普通集群和鏡像集群模式,自行去了解,比較簡單,兩小時即懂。

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

如何保證消息不被重復消費?

這個問題其實換一種問法就是,如何保證消息隊列的冪等性?

這個問題可以認為是消息隊列領(lǐng)域的基本問題。換句話來說,是在考察你的設(shè)計能力,這個問題的回答可以根據(jù)具體的業(yè)務(wù)場景來答,沒有固定的答案。

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

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

正常情況下,消費者在消費消息時候,消費完畢后,會發(fā)送一個確認信息給消息隊列,消息隊列就知道該消息被消費了,就會將該消息從消息隊列中刪除。只是不同的消息隊列發(fā)送的確認信息形式不同

例如RabbitMQ是發(fā)送一個ACK確認消息,RocketMQ是返回一個CONSUME_SUCCESS成功標志,kafka實際上有個offset的概念

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

那造成重復消費的原因?

就是因為網(wǎng)絡(luò)傳輸?shù)鹊裙收希_認信息沒有傳送到消息隊列,導致消息隊列不知道自己已經(jīng)消費過該消息了,再次將該消息分發(fā)給其他的消費者。

如何解決?這個問題針對業(yè)務(wù)場景來答分以下幾點

1、比如,你拿到這個消息做數(shù)據(jù)庫的insert操作。

那就容易了,給這個消息做一個唯一主鍵,那么就算出現(xiàn)重復消費的情況,就會導致主鍵沖突,避免數(shù)據(jù)庫出現(xiàn)臟數(shù)據(jù)。

2、再比如,你拿到這個消息做redis的set的操作

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

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

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

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

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

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

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

回答:其實這個可靠性傳輸,每種MQ都要從三個角度來分析:生產(chǎn)者弄丟數(shù)據(jù)、消息隊列弄丟數(shù)據(jù)、消費者弄丟數(shù)據(jù)

RabbitMQ

1、生產(chǎn)者丟數(shù)據(jù)

從生產(chǎn)者弄丟數(shù)據(jù)這個角度來看,RabbitMQ提供transaction和confirm模式來確保生產(chǎn)者不丟消息。

transaction機制就是說,發(fā)送消息前,開啟事物(channel.txSelect()),然后發(fā)送消息,如果發(fā)送過程中出現(xiàn)什么異常,事物就會回滾(channel.txRollback()),如果發(fā)送成功則提交事物(channel.txCommit())。

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

一旦channel進入confirm模式,所有在該信道上面發(fā)布的消息都將會被指派一個唯一的ID(從1開始)

一旦消息被投遞到所有匹配的隊列之后,rabbitMQ就會發(fā)送一個Ack給生產(chǎn)者(包含消息的唯一ID)

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

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

2、消息隊列丟數(shù)據(jù)

處理消息隊列丟數(shù)據(jù)的情況,一般是開啟持久化磁盤的配置。

這個持久化配置可以和confirm機制配合使用,你可以在消息持久化磁盤后,再給生產(chǎn)者發(fā)送一個Ack信號。

這樣,如果消息持久化磁盤之前,rabbitMQ陣亡了,那么生產(chǎn)者收不到Ack信號,生產(chǎn)者會自動重發(fā)。

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

1、將queue的持久化標識durable設(shè)置為true,則代表是一個持久的隊列

2、發(fā)送消息的時候?qū)eliveryMode=2

這樣設(shè)置以后,rabbitMQ就算掛了,重啟后也能恢復數(shù)據(jù)

3、消費者丟數(shù)據(jù)

消費者丟數(shù)據(jù)一般是因為采用了自動確認消息模式。

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

至于解決方案,采用手動確認消息即可。

kafka

Producer在發(fā)布消息到某個Partition時,先通過ZooKeeper找到該Partition的Leader

然后無論該Topic的Replication Factor為多少(也即該Partition有多少個Replica),Producer只將該消息發(fā)送到該Partition的Leader。

Leader會將該消息寫入其本地Log。每個Follower都從Leader中pull數(shù)據(jù)。

針對上述情況,得出如下分析

1、生產(chǎn)者丟數(shù)據(jù)

在kafka生產(chǎn)中,基本都有一個leader和多個follwer。follwer會去同步leader的信息。

因此,為了避免生產(chǎn)者丟數(shù)據(jù),做如下兩點配置

第一個配置要在producer端設(shè)置acks=all。這個配置保證了,follwer同步完成后,才認為消息發(fā)送成功。

在producer端設(shè)置retries=MAX,一旦寫入失敗,這無限重試

2、消息隊列丟數(shù)據(jù)

針對消息隊列丟數(shù)據(jù)的情況,無外乎就是,數(shù)據(jù)還沒同步,leader就掛了,這時zookpeer會將其他的follwer切換為leader,那數(shù)據(jù)就丟失了。

針對這種情況,應該做兩個配置。

replication.factor參數(shù),這個值必須大于1,即要求每個partition必須有至少2個副本

min.insync.replicas參數(shù),這個值必須大于1,這個是要求一個leader至少感知到有至少一個follower還跟自己保持聯(lián)系

這兩個配置加上上面生產(chǎn)者的配置聯(lián)合起來用,基本可確保kafka不丟數(shù)據(jù)

3、消費者丟數(shù)據(jù)

這種情況一般是自動提交了offset,然后你處理程序過程中掛了。kafka以為你處理好了。

再強調(diào)一次offset是干嘛的

offset:指的是kafka的topic中的每個消費組消費的下標。

簡單的來說就是一條消息對應一個offset下標,每次消費數(shù)據(jù)的時候如果提交offset,那么下次消費就會從提交的offset加一那里開始消費。

比如一個topic中有100條數(shù)據(jù),我消費了50條并且提交了,那么此時的kafka服務(wù)端記錄提交的offset就是49(offset從0開始),那么下次消費的時候offset就從50開始消費。

解決方案也很簡單,改成手動提交即可。

如何保證消息的順序性?

分析:其實并非所有的公司都有這種業(yè)務(wù)需求,但是還是對這個問題要有所復習。

回答:針對這個問題,通過某種算法,將需要保持先后順序的消息放到同一個消息隊列中(kafka中就是partition,rabbitMq中就是queue)。然后只用一個消費者去消費該隊列。

有的人會問:那如果為了吞吐量,有多個消費者去消費怎么辦?

這個問題,沒有固定回答的套路。比如我們有一個微博的操作,發(fā)微博、寫評論、刪除微博,這三個異步操作。如果是這樣一個業(yè)務(wù)場景,那只要重試就行。

比如你一個消費者先執(zhí)行了寫評論的操作,但是這時候,微博都還沒發(fā),寫評論一定是失敗的,等一段時間。等另一個消費者,先執(zhí)行寫評論的操作后,再執(zhí)行,就可以成功。

總之,針對這個問題,我的觀點是保證入隊有序就行,出隊以后的順序交給消費者自己去保證,沒有固定套路。

總結(jié)

寫到這里,希望讀者把本文提出的這幾個問題,經(jīng)過深刻的準備后,一般來說,能囊括大部分的消息隊列的知識點。

如果面試官不問這幾個問題怎么辦,簡單,自己把幾個問題講清楚,突出以下自己考慮的全面性。

最后,其實我不太提倡這樣突擊復習,希望大家打好基本功,做一個愛思考,懂思考,會思考的程序員。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內(nèi)容

  • 近來研究消息隊列,首先就要知道消息隊列是什么,為什么要用消息隊列。下面是轉(zhuǎn)載內(nèi)容,原文鏈接 點我 引言 為什么寫這...
    super_pcm閱讀 295評論 0 1
  • 引言 復習要點 本文大概圍繞如下幾點進行闡述: 為什么使用消息隊列?使用消息隊列有什么缺點?消息隊列如何選型?如何...
    消失er閱讀 719評論 0 9
  • 作者:孤獨煙 出處: http://rjzheng.cnblogs.com/ 一、復習要點本文大概圍繞如下幾點進行...
    蝦米愛螃蟹閱讀 712評論 0 6
  • 下過雨之后的天氣真冷,給女兒穿上厚一點的衣服,就怕孩子感冒了。 今天的我真是好脾氣,昨晚女兒沒寫...
    李烽熠媽閱讀 196評論 0 2
  • 逃離臨海,前往寧波,在2018即將結(jié)束日子,見到2018的第一場雪。 動車上,站票,一個小時的車程,就盯著窗外的風...
    膽子很小的哥斯拉閱讀 154評論 0 0