面試:據說你用過MQ,能具體說說嗎?

點擊上方Java學習指南關注公衆號
java

天天閱讀Java乾貨文章mysql


爲何使用消息隊列?消息隊列的優勢和缺點?kafka、activemq、rabbitmq、rocketmq都有什麼優缺點?web

面試官角度分析:面試

(1)你知不知道大家系統裏爲何要用消息隊列這個東西?redis

(2)既然用了消息隊列這個東西,你知不知道用了有什麼好處?spring

(3)既然你用了MQ,那麼當時爲何選用這一款MQ?sql

1. 爲何使用消息隊列?

面試官問這個問題的指望之一的回答是,大家公司有什麼業務場景,這個業務場景有什麼技術挑戰,若是不用MQ可能會很麻煩,可是再用了以後帶來了不少好處。數據庫

消息隊列的常見使用場景有不少可是核心的有三個:解耦、異步、削峯api

解耦

場景描述:A系統發送個數據到BCD三個系統,接口調用發送,那若是E系統也要這個數據呢?那若是C系統如今不須要了呢?如今A系統又要發送第二種數據了呢?A系統負責人崩潰中...再來點崩潰的事兒,A系統要時時刻刻考慮BCDE四個系統若是掛了怎麼辦?那我要不要重發?我要不要把消息存起來?頭髮都白了啊...性能優化

使用了MQ以後的解耦場景

面試技巧:你須要考慮下,你負責的系統中是否有相似的場景,就是一個系統或者一個模塊,調用了多個系統或者模塊,相互之間的調用很複雜,維護起來很麻煩。可是其實這個調用是不須要直接同步調用接口的,若是MQ給他異步化解耦也是能夠的,你就須要去考慮在你的項目裏是否是能夠運用這個MQ去進行系統解耦 。

異步

場景描述:系統A接受一個請求,須要在本身本地寫庫,還須要在系統BCD三個系統寫庫,本身本地寫庫須要3ms。BCD分別須要300ms、450ms、200ms。最終總好時長:953ms,接近1s。給用戶的體驗感受一點也很差。

不用MQ的同步高延時請求場景

使用MQ異步化以後的接口性能優化

削峯

場景描述:天天 0 點到 11 點,系統A風平浪靜,每秒併發請求數量就 100 個。結果每一一到11點到1點,每秒併發請求數量就會暴增大1萬條 。可是系統最大的處理能力就只能每秒鐘處理1000個請求。往期面試題彙總:001期~150期彙總

沒有用MQ的時候高峯期系統被打死的場景

使用MQ來進行削峯的場景

2. 消息隊列的有點和缺點?

優勢:特殊場景下解耦、異步、削峯。

缺點:

系統可用性下降:系統引入的外部依賴越多,越容易掛掉,原本你就是A系統調用BCD三個系統的接口就行了,人ABCD四個系統好好的沒什麼問題,你偏加個MQ進來,萬一MQ掛了怎麼辦,整套系統崩潰了,就完蛋了

系統複雜性提升:硬生生加個MQ進來,你怎麼保證消息沒有重複消費?怎麼處理消息丟失的狀況?怎麼保證消息傳遞的順序性?

一致性問題:系統A處理完了直接返回成功了,人家都認爲你這個請求成功了;但問題是,要是BCD三個系統哪裏BD系統成功了,結果C系統寫庫失敗了,咋整?數據就不一致了,

因此消息隊列是一種很是複雜的架構,引入它有不少好處,可是也得針對他帶來的壞處作各類額外的技術方案和架構來規避掉。作好以後你會發現系統複雜度提高了一個數量積,可是關鍵時刻,用,仍是要用的。

3. kafka、activemq、rabbitmq、rocketmq都有什麼優缺點?

特性

ActiveMQ

RabbitMQ

RocketMQ

Kafka

單機吞吐量

萬級,吞吐量比RocketMQ和Kafka要低了一個數量級

萬級,吞吐量比RocketMQ和Kafka要低了一個數量級

10萬級,RocketMQ也是能夠支撐高吞吐的一種MQ

10萬級別,這是kafka最大的優勢,就是吞吐量高。

 

通常配合大數據類的系統來進行實時數據計算、日誌採集等場景

topic數量對吞吐量的影響



topic能夠達到幾百,幾千個的級別,吞吐量會有較小幅度的降低

這是RocketMQ的一大優點,在同等機器下,能夠支撐大量的topic

topic從幾十個到幾百個的時候,吞吐量會大幅度降低

因此在同等機器下,kafka儘可能保證topic數量不要過多。若是要支撐大規模topic,須要增長更多的機器資源

時效性

ms級

微秒級,這是rabbitmq的一大特色,延遲是最低的

ms級

延遲在ms級之內

可用性

 

高,基於主從架構實現高可用性

高,基於主從架構實現高可用性

很是高,分佈式架構

很是高,kafka是分佈式的,一個數據多個副本,少數機器宕機,不會丟失數據,不會致使不可用

消息可靠性

有較低的機率丟失數據


通過參數優化配置,能夠作到0丟失

通過參數優化配置,消息能夠作到0丟失

功能支持

MQ領域的功能極其完備

基於erlang開發,因此併發能力很強,性能極其好,延時很低

MQ功能較爲完善,仍是分佈式的,擴展性好

功能較爲簡單,主要支持簡單的MQ功能,在大數據領域的實時計算以及日誌採集被大規模使用,是事實上的標準

優劣勢總結

很是成熟,功能強大,在業內大量的公司以及項目中都有應用

 

偶爾會有較低機率丟失消息

 

並且如今社區以及國內應用都愈來愈少,官方社區如今對ActiveMQ 5.x維護愈來愈少,幾個月才發佈一個版本

 

並且確實主要是基於解耦和異步來用的,較少在大規模吞吐的場景中使用

 

erlang語言開發,性能極其好,延時很低;

吞吐量到萬級,MQ功能比較完備

並且開源提供的管理界面很是棒,用起來很好用

社區相對比較活躍,幾乎每月都發布幾個版本分

在國內一些互聯網公司近幾年用rabbitmq也比較多一些

可是問題也是顯而易見的,RabbitMQ確實吞吐量會低一些,這是由於他作的實現機制比較重。

並且erlang開發,國內有幾個公司有實力作erlang源碼級別的研究和定製?若是說你沒這個實力的話,確實偶爾會有一些問題,你很難去看懂源碼,你公司對這個東西的掌控很弱,基本職能依賴於開源社區的快速維護和修復bug。

並且rabbitmq集羣動態擴展會很麻煩,不過這個我以爲還好。其實主要是erlang語言自己帶來的問題。很難讀源碼,很難定製和掌控。

接口簡單易用,並且畢竟在阿里大規模應用過,有阿里品牌保障

日處理消息上百億之多,能夠作到大規模吞吐,性能也很是好,分佈式擴展也很方便,社區維護還能夠,可靠性和可用性都是ok的,還能夠支撐大規模的topic數量,支持複雜MQ業務場景

並且一個很大的優點在於,阿里出品都是java系的,咱們能夠本身閱讀源碼,定製本身公司的MQ,能夠掌控

社區活躍度相對較爲通常,不過也還能夠,文檔相對來講簡單一些,而後接口這塊不是按照標準JMS規範走的有些系統要遷移須要修改大量代碼

還有就是阿里出臺的技術,你得作好這個技術萬一被拋棄,社區黃掉的風險,那若是大家公司有技術實力我以爲用RocketMQ挺好的

kafka的特色其實很明顯,就是僅僅提供較少的核心功能,可是提供超高的吞吐量,ms級的延遲,極高的可用性以及可靠性,並且分佈式能夠任意擴展

 

同時kafka最好是支撐較少的topic數量便可,保證其超高吞吐量

 

並且kafka惟一的一點劣勢是有可能消息重複消費,那麼對數據準確性會形成極其輕微的影響,在大數據領域中以及日誌採集中,這點輕微影響能夠忽略

 

這個特性自然適合大數據實時計算以及日誌收集

1. 引入消息隊列以後如何保證其高可用性?

(1)RabbitMQ的高可用性

RabbitMQ是比較有表明性的,由於是基於主從作高可用性的,咱們就以他爲例子講解第一種MQ的高可用性怎麼實現。

rabbitmq有三種模式:單機模式,普通集羣模式,鏡像集羣模式

(1.1) 單機模式

就是demo級別的,通常就是你本地啓動了玩玩兒的,沒人生產用單機模式

(1.2)普通集羣模式

意思就是在多臺機器上啓動多個rabbitmq實例,每一個機器啓動一個。可是你建立的queue,只會放在一個rabbtimq實例上,可是每一個實例都同步queue的元數據。完了你消費的時候,實際上若是鏈接到了另一個實例,那麼那個實例會從queue所在實例上拉取數據過來。

這種方式確實很麻煩,也不怎麼好,沒作到所謂的分佈式,就是個普通集羣。由於這致使你要麼消費者每次隨機鏈接一個實例而後拉取數據,要麼固定鏈接那個queue所在實例消費數據,前者有數據拉取的開銷,後者致使單實例性能瓶頸。

往期面試題彙總:001期~150期彙總

並且若是那個放queue的實例宕機了,會致使接下來其餘實例就沒法從那個實例拉取,若是你開啓了消息持久化,讓rabbitmq落地存儲消息的話,消息不必定會丟,得等這個實例恢復了,而後才能夠繼續從這個queue拉取數據。

因此這個事兒就比較尷尬了,這就沒有什麼所謂的高可用性可言了,這方案主要是提升吞吐量的,就是說讓集羣中多個節點來服務某個queue的讀寫操做。

(1.3)鏡像集羣模式

這種模式,纔是所謂的rabbitmq的高可用模式,跟普通集羣模式不同的是,你建立的queue,不管元數據仍是queue裏的消息都會存在於多個實例上,而後每次你寫消息到queue的時候,都會自動把消息到多個實例的queue裏進行消息同步。

這樣的話,好處在於,你任何一個機器宕機了,沒事兒,別的機器均可以用。壞處在於,第一,這個性能開銷也太大了吧,消息同步全部機器,致使網絡帶寬壓力和消耗很重!第二,這麼玩兒,就沒有擴展性可言了,若是某個queue負載很重,你加機器,新增的機器也包含了這個queue的全部數據,並無辦法線性擴展你的queue

那麼怎麼開啓這個鏡像集羣模式呢?我這裏簡單說一下,避免面試人家問你你不知道,其實很簡單rabbitmq有很好的管理控制檯,就是在後臺新增一個策略,這個策略是鏡像集羣模式的策略,指定的時候能夠要求數據同步到全部節點的,也能夠要求就同步到指定數量的節點,而後你再次建立queue的時候,應用這個策略,就會自動將數據同步到其餘的節點上去了。

(2)kafka的高可用性

kafka一個最基本的架構認識:多個broker組成,每一個broker是一個節點;你建立一個topic,這個topic能夠劃分爲多個partition,每一個partition能夠存在於不一樣的broker上,每一個partition就放一部分數據。

這就是自然的分佈式消息隊列,就是說一個topic的數據,是分散放在多個機器上的,每一個機器就放一部分數據。

實際上rabbitmq之類的,並非分佈式消息隊列,他就是傳統的消息隊列,只不過提供了一些集羣、HA的機制而已,由於不管怎麼玩兒,rabbitmq一個queue的數據都是放在一個節點裏的,鏡像集羣下,也是每一個節點都放這個queue的完整數據。

kafka 0.8之前,是沒有HA機制的,就是任何一個broker宕機了,那個broker上的partition就廢了,無法寫也無法讀,沒有什麼高可用性可言。

kafka 0.8之後,提供了HA機制,就是replica副本機制。每一個partition的數據都會同步到吉他機器上,造成本身的多個replica副本。而後全部replica會選舉一個leader出來,那麼生產和消費都跟這個leader打交道,而後其餘replica就是follower。寫的時候,leader會負責把數據同步到全部follower上去,讀的時候就直接讀leader上數據便可。只能讀寫leader?很簡單,要是你能夠隨意讀寫每一個follower,那麼就要care數據一致性的問題,系統複雜度過高,很容易出問題。kafka會均勻的將一個partition的全部replica分佈在不一樣的機器上,這樣才能夠提升容錯性。

這麼搞,就有所謂的高可用性了,由於若是某個broker宕機了,沒事兒,那個broker上面的partition在其餘機器上都有副本的,若是這上面有某個partition的leader,那麼此時會從新選舉一個新的leader出來,你們繼續讀寫那個新的leader便可。這就有所謂的高可用性了。

寫數據的時候,生產者就寫leader,而後leader將數據落地寫本地磁盤,接着其餘follower本身主動從leader來pull數據。一旦全部follower同步好數據了,就會發送ack給leader,leader收到全部follower的ack以後,就會返回寫成功的消息給生產者。(固然,這只是其中一種模式,還能夠適當調整這個行爲)

消費的時候,只會從leader去讀,可是隻有一個消息已經被全部follower都同步成功返回ack的時候,這個消息纔會被消費者讀到。

實際上這塊機制,講深了,是能夠很是之深刻的,可是我仍是回到咱們這個課程的主題和定位,聚焦面試,至少你聽到這裏大體明白了kafka是如何保證高可用機制的了,對吧?不至於一無所知,現場還能給面試官畫畫圖。要趕上面試官確實是kafka高手,深挖了問,那你只能說很差意思,太深刻的你沒研究過。

可是你們必定要明白,這個事情是要權衡的,你如今是要快速突擊常見面試題體系,而不是要深刻學習kafka,要深刻學習kafka,你是沒那麼多時間的。你只能確保,你以前也許壓根兒不知道這塊,可是如今你知道了,面試被問到,你大概能夠說一說。而後不少其餘的候選人,也許還不如你,沒看過這個,被問到了壓根兒答不出來,相比之下,你還能說點出來,大概就是這個意思了。

2. 如何保證消息不被重複消費(如何保證消息消費時的冪等性)?

其實這個很常見的一個問題,這倆問題基本能夠連起來問。既然是消費消息,那確定要考慮考慮會不會重複消費?能不能避免重複消費?或者重複消費了也別形成系統異常能夠嗎?這個是MQ領域的基本問題,其實本質上仍是問你使用消息隊列如何保證冪等性,這個是你架構裏要考慮的一個問題。

首先就是好比rabbitmq、rocketmq、kafka,都有可能會出現消費重複消費的問題,正常。由於這問題一般不是mq本身保證的,是給你保證的。而後咱們挑一個kafka來舉個例子,說說怎麼重複消費吧。

kafka實際上有個offset的概念,就是每一個消息寫進去,都有一個offset,表明他的序號,而後consumer消費了數據以後,每隔一段時間,會把本身消費過的消息的offset提交一下,表明我已經消費過了,下次我要是重啓啥的,你就讓我繼續從上次消費到的offset來繼續消費吧。

可是凡事總有意外,好比咱們以前生產常常遇到的,就是你有時候重啓系統,看你怎麼重啓了,若是碰到點着急的,直接kill進程了,再重啓。這會致使consumer有些消息處理了,可是沒來得及提交offset,尷尬了。重啓以後,少數消息會再次消費一次。

其實重複消費不可怕,可怕的是你沒考慮到重複消費以後,怎麼保證冪等性。

給你舉個例子吧。假設你有個系統,消費一條往數據庫裏插入一條,要是你一個消息重複兩次,你不就插入了兩條,這數據不就錯了?可是你要是消費到第二次的時候,本身判斷一下已經消費過了,直接扔了,不就保留了一條數據?

一條數據重複出現兩次,數據庫裏就只有一條數據,這就保證了系統的冪等性

冪等性,我通俗點說,就一個數據,或者一個請求,給你重複來屢次,你得確保對應的數據是不會改變的,不能出錯。

那因此第二個問題來了,怎麼保證消息隊列消費的冪等性?

其實仍是得結合業務來思考,我這裏給幾個思路:

(1)好比你拿個數據要寫庫,你先根據主鍵查一下,若是這數據都有了,你就別插入了,update一下好吧

(2)好比你是寫redis,那沒問題了,反正每次都是set,自然冪等性

(3)好比你不是上面兩個場景,那作的稍微複雜一點,你須要讓生產者發送每條數據的時候,裏面加一個全局惟一的id,相似訂單id之類的東西,而後你這裏消費到了以後,先根據這個id去好比redis裏查一下,以前消費過嗎?若是沒有消費過,你就處理,而後這個id寫redis。若是消費過了,那你就別處理了,保證別重複處理相同的消息便可。

還有好比基於數據庫的惟一鍵來保證重複數據不會重複插入多條,咱們以前線上系統就有這個問題,就是拿到數據的時候,每次重啓可能會有重複,由於kafka消費者還沒來得及提交offset,重複數據拿到了之後咱們插入的時候,由於有惟一鍵約束了,因此重複數據只會插入報錯,不會致使數據庫中出現髒數據

如何保證MQ的消費是冪等性的,須要結合具體的業務來看

如何保證消息的冪等性

3. 如何保證消息的可靠傳輸(如何處理消息丟失的問題)?

這個是確定的,用mq有個基本原則,就是數據不能多一條,也不能少一條,不能多,就是剛纔說的重複消費和冪等性問題。不能少,就是說這數據別搞丟了。那這個問題你必須得考慮一下。

若是說你這個是用mq來傳遞很是核心的消息,好比說計費,扣費的一些消息,由於我之前設計和研發過一個公司很是核心的廣告平臺,計費系統,計費系統是很重的一個業務,操做是很耗時的。因此說廣告系統總體的架構裏面,其實是將計費作成異步化的,而後中間就是加了一個MQ。

咱們當時爲了確保說這個MQ傳遞過程當中絕對不會把計費消息給弄丟,花了不少的精力。廣告主投放了一個廣告,明明說好了,用戶點擊一次扣費1塊錢。結果要是用戶動不動點擊了一次,扣費的時候搞的消息丟了,咱們公司就會不斷的少幾塊錢,幾塊錢,聚沙成塔,這個就對公司是一個很大的損失。

面試題剖析

這個丟數據,mq通常分爲兩種,要麼是mq本身弄丟了,要麼是咱們消費的時候弄丟了。我們從rabbitmq和kafka分別來分析一下

rabbitmq這種mq,通常來講都是承載公司的核心業務的,數據是絕對不能弄丟的

RabbitMQ可能存在的數據丟失問題

(1)rabbitmq

1)生產者弄丟了數據

生產者將數據發送到rabbitmq的時候,可能數據就在半路給搞丟了,由於網絡啥的問題,都有可能。

此時能夠選擇用rabbitmq提供的事務功能,就是生產者發送數據以前開啓rabbitmq事務(channel.txSelect),而後發送消息,若是消息沒有成功被rabbitmq接收到,那麼生產者會收到異常報錯,此時就能夠回滾事務(channel.txRollback),而後重試發送消息;若是收到了消息,那麼能夠提交事務(channel.txCommit)。可是問題是,rabbitmq事務機制一搞,基本上吞吐量會下來,由於太耗性能。

因此通常來講,若是你要確保說寫rabbitmq的消息別丟,能夠開啓confirm模式,在生產者那裏設置開啓confirm模式以後,你每次寫的消息都會分配一個惟一的id,而後若是寫入了rabbitmq中,rabbitmq會給你回傳一個ack消息,告訴你說這個消息ok了。若是rabbitmq沒能處理這個消息,會回調你一個nack接口,告訴你這個消息接收失敗,你能夠重試。並且你能夠結合這個機制本身在內存裏維護每一個消息id的狀態,若是超過必定時間還沒接收到這個消息的回調,那麼你能夠重發。

事務機制和cnofirm機制最大的不一樣在於,事務機制是同步的,你提交一個事務以後會阻塞在那兒,可是confirm機制是異步的,你發送個消息以後就能夠發送下一個消息,而後那個消息rabbitmq接收了以後會異步回調你一個接口通知你這個消息接收到了。

因此通常在生產者這塊避免數據丟失,都是用confirm機制的。

2)rabbitmq弄丟了數據

就是rabbitmq本身弄丟了數據,這個你必須開啓rabbitmq的持久化,就是消息寫入以後會持久化到磁盤,哪怕是rabbitmq本身掛了,恢復以後會自動讀取以前存儲的數據,通常數據不會丟。除非極其罕見的是,rabbitmq還沒持久化,本身就掛了,可能致使少許數據會丟失的,可是這個機率較小。

設置持久化有兩個步驟,第一個是建立queue的時候將其設置爲持久化的,這樣就能夠保證rabbitmq持久化queue的元數據,可是不會持久化queue裏的數據;第二個是發送消息的時候將消息的deliveryMode設置爲2,就是將消息設置爲持久化的,此時rabbitmq就會將消息持久化到磁盤上去。必需要同時設置這兩個持久化才行,rabbitmq哪怕是掛了,再次重啓,也會從磁盤上重啓恢復queue,恢復這個queue裏的數據。

並且持久化能夠跟生產者那邊的confirm機制配合起來,只有消息被持久化到磁盤以後,纔會通知生產者ack了,因此哪怕是在持久化到磁盤以前,rabbitmq掛了,數據丟了,生產者收不到ack,你也是能夠本身重發的。

哪怕是你給rabbitmq開啓了持久化機制,也有一種可能,就是這個消息寫到了rabbitmq中,可是還沒來得及持久化到磁盤上,結果不巧,此時rabbitmq掛了,就會致使內存裏的一點點數據會丟失。

3)消費端弄丟了數據

rabbitmq若是丟失了數據,主要是由於你消費的時候,剛消費到,還沒處理,結果進程掛了,好比重啓了,那麼就尷尬了,rabbitmq認爲你都消費了,這數據就丟了。

這個時候得用rabbitmq提供的ack機制,簡單來講,就是你關閉rabbitmq自動ack,能夠經過一個api來調用就行,而後每次你本身代碼裏確保處理完的時候,再程序裏ack一把。這樣的話,若是你還沒處理完,不就沒有ack?那rabbitmq就認爲你還沒處理完,這個時候rabbitmq會把這個消費分配給別的consumer去處理,消息是不會丟的。

往期面試題彙總:001期~150期彙總

(2)kafka

1)消費端弄丟了數據

惟一可能致使消費者弄丟數據的狀況,就是說,你那個消費到了這個消息,而後消費者那邊自動提交了offset,讓kafka覺得你已經消費好了這個消息,其實你剛準備處理這個消息,你還沒處理,你本身就掛了,此時這條消息就丟咯。

這不是同樣麼,你們都知道kafka會自動提交offset,那麼只要關閉自動提交offset,在處理完以後本身手動提交offset,就能夠保證數據不會丟。可是此時確實仍是會重複消費,好比你剛處理完,還沒提交offset,結果本身掛了,此時確定會重複消費一次,本身保證冪等性就行了。

生產環境碰到的一個問題,就是說咱們的kafka消費者消費到了數據以後是寫到一個內存的queue裏先緩衝一下,結果有的時候,你剛把消息寫入內存queue,而後消費者會自動提交offset。

而後此時咱們重啓了系統,就會致使內存queue裏還沒來得及處理的數據就丟失了

2)kafka弄丟了數據

這塊比較常見的一個場景,就是kafka某個broker宕機,而後從新選舉partiton的leader時。你們想一想,要是此時其餘的follower恰好還有些數據沒有同步,結果此時leader掛了,而後選舉某個follower成leader以後,他不就少了一些數據?這就丟了一些數據啊。

生產環境也遇到過,咱們也是,以前kafka的leader機器宕機了,將follower切換爲leader以後,就會發現說這個數據就丟了

因此此時通常是要求起碼設置以下4個參數:

  • 給這個topic設置replication.factor參數:這個值必須大於1,要求每一個partition必須有至少2個副本

  • 在kafka服務端設置min.insync.replicas參數:這個值必須大於1,這個是要求一個leader至少感知到有至少一個follower還跟本身保持聯繫,沒掉隊,這樣才能確保leader掛了還有一個follower吧

  • 在producer端設置acks=all:這個是要求每條數據,必須是寫入全部replica以後,才能認爲是寫成功了

  • 在producer端設置retries=MAX(很大很大很大的一個值,無限次重試的意思):這個是要求一旦寫入失敗,就無限重試,卡在這裏了

咱們生產環境就是按照上述要求配置的,這樣配置以後,至少在kafka broker端就能夠保證在leader所在broker發生故障,進行leader切換時,數據不會丟失

3)生產者會不會弄丟數據

若是按照上述的思路設置了ack=all,必定不會丟,要求是,你的leader接收到消息,全部的follower都同步到了消息以後,才認爲本次寫成功了。若是沒知足這個條件,生產者會自動不斷的重試,重試無限次。

1. 如何保證消息的順序性?

其實這個也是用MQ的時候必問的話題,第一看看你瞭解不瞭解順序這個事兒?第二看看你有沒有辦法保證消息是有順序的?這個生產系統中常見的問題。

我舉個例子,咱們之前作過一個mysql binlog同步的系統,壓力仍是很是大的,日同步數據要達到上億。mysql -> mysql,常見的一點在於說大數據team,就須要同步一個mysql庫過來,對公司的業務系統的數據作各類複雜的操做。

你在mysql裏增刪改一條數據,對應出來了增刪改3條binlog,接着這三條binlog發送到MQ裏面,到消費出來依次執行,起碼得保證人家是按照順序來的吧?否則原本是:增長、修改、刪除;你楞是換了順序給執行成刪除、修改、增長,不全錯了麼。

原本這個數據同步過來,應該最後這個數據被刪除了;結果你搞錯了這個順序,最後這個數據保留下來了,數據同步就出錯了。

先看看順序會錯亂的倆場景

(1)rabbitmq:一個queue,多個consumer,這不明顯亂了

(2)kafka:一個topic,一個partition,一個consumer,內部多線程,這不也明顯亂了

那如何保證消息的順序性呢?簡單簡單

(1)rabbitmq:拆分多個queue,每一個queue一個consumer,就是多一些queue而已,確實是麻煩點;

或者就一個queue可是對應一個consumer,而後這個consumer內部用內存隊列作排隊,而後分發給底層不一樣的worker來處理

(2)kafka:一個topic,一個partition,一個consumer,內部單線程消費,寫N個內存queue,而後N個線程分別消費一個內存queue便可

如何解決消息隊列的延時以及過時失效問題?消息隊列滿了之後該怎麼處理?有幾百萬消息持續積壓幾小時,說說怎麼解決?

你看這問法,其實本質針對的場景,都是說,可能你的消費端出了問題,不消費了,或者消費的極其極其慢。接着就坑爹了,可能你的消息隊列集羣的磁盤都快寫滿了,都沒人消費,這個時候怎麼辦?或者是整個這就積壓了幾個小時,你這個時候怎麼辦?或者是你積壓的時間太長了,致使好比rabbitmq設置了消息過時時間後就沒了怎麼辦?

因此就這事兒,其實線上挺常見的,通常不出,一出就是大case,通常常見於,舉個例子,消費端每次消費以後要寫mysql,結果mysql掛了,消費端hang那兒了,不動了。或者是消費端出了個什麼叉子,致使消費速度極其慢。

關於這個事兒,咱們一個一個來梳理吧,先假設一個場景,咱們如今消費端出故障了,而後大量消息在mq裏積壓,如今事故了,慌了


廣告插入:歡迎關注純乾貨技術公衆號:Java學習指南,天天閱讀技術文章。

(1)大量消息在mq裏積壓了幾個小時了還沒解決

幾千萬條數據在MQ裏積壓了七八個小時,從下午4點多,積壓到了晚上很晚,10點多,11點多

這個是咱們真實遇到過的一個場景,確實是線上故障了,這個時候要否則就是修復consumer的問題,讓他恢復消費速度,而後傻傻的等待幾個小時消費完畢。這個確定不能在面試的時候說吧。

一個消費者一秒是1000條,一秒3個消費者是3000條,一分鐘是18萬條,1000多萬條

因此若是你積壓了幾百萬到上千萬的數據,即便消費者恢復了,也須要大概1小時的時間才能恢復過來

通常這個時候,只能操做臨時緊急擴容了,具體操做步驟和思路以下:

1)先修復consumer的問題,確保其恢復消費速度,而後將現有cnosumer都停掉

2)新建一個topic,partition是原來的10倍,臨時創建好原先10倍或者20倍的queue數量

3)而後寫一個臨時的分發數據的consumer程序,這個程序部署上去消費積壓的數據,消費以後不作耗時的處理,直接均勻輪詢寫入臨時創建好的10倍數量的queue

4)接着臨時徵用10倍的機器來部署consumer,每一批consumer消費一個臨時queue的數據

5)這種作法至關因而臨時將queue資源和consumer資源擴大10倍,以正常的10倍速度來消費數據

6)等快速消費完積壓數據以後,得恢復原先部署架構,從新用原先的consumer機器來消費消息

(2)這裏咱們假設再來第二個坑

假設你用的是rabbitmq,rabbitmq是能夠設置過時時間的,就是TTL,若是消息在queue中積壓超過必定的時間就會被rabbitmq給清理掉,這個數據就沒了。那這就是第二個坑了。這就不是說數據會大量積壓在mq裏,而是大量的數據會直接搞丟。

這個狀況下,就不是說要增長consumer消費積壓的消息,由於實際上沒啥積壓,而是丟了大量的消息。咱們能夠採起一個方案,就是批量重導,這個咱們以前線上也有相似的場景幹過。就是大量積壓的時候,咱們當時就直接丟棄數據了,而後等過了高峯期之後,好比你們一塊兒喝咖啡熬夜到晚上12點之後,用戶都睡覺了。

這個時候咱們就開始寫程序,將丟失的那批數據,寫個臨時程序,一點一點的查出來,而後從新灌入mq裏面去,把白天丟的數據給他補回來。也只能是這樣了。

假設1萬個訂單積壓在mq裏面,沒有處理,其中1000個訂單都丟了,你只能手動寫程序把那1000個訂單給查出來,手動發到mq裏去再補一次

往期面試題彙總:001期~150期彙總

(3)而後咱們再來假設第三個坑

若是走的方式是消息積壓在mq裏,那麼若是你很長時間都沒處理掉,此時致使mq都快寫滿了,咋辦?這個還有別的辦法嗎?沒有,誰讓你第一個方案執行的太慢了,你臨時寫程序,接入數據來消費,消費一個丟棄一個,都不要了,快速消費掉全部的消息。而後走第二個方案,到了晚上再補數據吧。

1. 若是讓你寫一個消息隊列,該如何進行架構設計啊?說一下你的思路

其實聊到這個問題,通常面試官要考察兩塊:

(1)你有沒有對某一個消息隊列作過較爲深刻的原理的瞭解,或者從總體瞭解把握住一個mq的架構原理

(2)看看你的設計能力,給你一個常見的系統,就是消息隊列系統,看看你能不能從全局把握一下總體架構設計,給出一些關鍵點出來

其實回答這類問題,說白了,起碼不求你看過那技術的源碼,起碼你大概知道那個技術的基本原理,核心組成部分,基本架構構成,而後參照一些開源的技術把一個系統設計出來的思路說一下就好

好比說這個消息隊列系統,咱們來從如下幾個角度來考慮一下

說實話,我通常面相似問題的時候,大部分人基本都會蒙,由於平時歷來沒有思考過相似的問題,大多數人就是平時埋頭用,歷來不去思考背後的一些東西。相似的問題,我常常問的還有,若是讓你來設計一個spring框架你會怎麼作?若是讓你來設計一個dubbo框架你會怎麼作?若是讓你來設計一個mybatis框架你會怎麼作?

其實回答這類問題,說白了,起碼不求你看過那技術的源碼,起碼你大概知道那個技術的基本原理,核心組成部分,基本架構構成,而後參照一些開源的技術把一個系統設計出來的思路說一下就好

好比說這個消息隊列系統,咱們來從如下幾個角度來考慮一下

(1)首先這個mq得支持可伸縮性吧,就是須要的時候快速擴容,就能夠增長吞吐量和容量,那怎麼搞?設計個分佈式的系統唄,參照一下kafka的設計理念,broker -> topic -> partition,每一個partition放一個機器,就存一部分數據。若是如今資源不夠了,簡單啊,給topic增長partition,而後作數據遷移,增長機器,不就能夠存放更多數據,提供更高的吞吐量了?

(2)其次你得考慮一下這個mq的數據要不要落地磁盤吧?那確定要了,落磁盤,才能保證別進程掛了數據就丟了。那落磁盤的時候怎麼落啊?順序寫,這樣就沒有磁盤隨機讀寫的尋址開銷,磁盤順序讀寫的性能是很高的,這就是kafka的思路。

2.其次你考慮一下你的mq的可用性啊?

這個事兒,具體參考咱們以前可用性那個環節講解的kafka的高可用保障機制。多副本 -> leader & follower -> broker掛了從新選舉leader便可對外服務。

(4)能不能支持數據0丟失啊?能夠的,參考咱們以前說的那個kafka數據零丟失方案

其實一個mq確定是很複雜的,面試官問你這個問題,實際上是個開放題,他就是看看你有沒有從架構角度總體構思和設計的思惟以及能力。確實這個問題能夠刷掉一大批人,由於大部分人平時不思考這些東西。

< END >

來源:blog.csdn.net/qq_29676623/article/details/85108070


- 往期精彩 -


這多是你看過最好的微服務架構詳解文章


編碼之路,與君共勉。


硬核乾貨:一個核心繫統 3 萬多行代碼的重構之旅



全乾貨技術公衆號Java學習指南

👇👇

長按上方二維碼關注公衆號


本文分享自微信公衆號 - Java學習指南(gh_85b94beaede2)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索