MQ消息中間件,面試能問些什么?
為什么使用消息隊列?消息隊列的優點和缺點?
kafka、activemq、rabbitmq、rocketmq都有什么優缺點?
面試官角度分析:
(1)你知不知道你們系統里為什么要用消息隊列這個東西?
(2)既然用了消息隊列這個東西,你知不知道用了有什么好處?
(3)既然你用了MQ,那么當時為什么選用這一款MQ?
1. 為什么使用消息隊列?
面試官問這個問題的期望之一的回答是,你們公司有什么業務場景,這個業務場景有什么技術挑戰,如果不用MQ可能會很麻煩,但是再用了之后帶來了很多好處。
消息隊列的常見使用場景有很多但是核心的有三個:解耦、異步、削峰
解耦
場景描述: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個請求。
沒有用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所在實例消費數據,前者有數據拉取的開銷,后者導致單實例性能瓶頸。
而且如果那個放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去處理,消息是不會丟的。
(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里積壓,現在事故了,慌了
(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里去再補一次
(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肯定是很復雜的,面試官問你這個問題,其實是個開放題,他就是看看你有沒有從架構角度整體構思和設計的思維以及能力。確實這個問題可以刷掉一大批人,因為大部分人平時不思考這些東西。
來源:blog.csdn.net/qq_29676623/article/details/85108070
歡迎關注公眾號 【碼農開花】一起學習成長
我會一直分享Java干貨,也會分享免費的學習資料課程和面試寶典
回復:【計算機】【設計模式】有驚喜哦