做了幾年開發(fā),你知道自己的系統(tǒng)為什么要用消息中間件嗎?
這篇文章開始,我們把消息中間件這塊高頻的面試題給大家說一下,也會涵蓋一些MQ中間件常見的技術(shù)問題。
假如面試官看你簡歷里寫了MQ中間件的使用經(jīng)驗,很可能會有如下問題:
- 你們公司生產(chǎn)環(huán)境用的是什么消息中間件?
- 為什么要在系統(tǒng)里引入消息中間件?
- 引入消息中間件之后會有什么好處以及壞處?
好,我們一個個的來分析!
一、 你們公司生產(chǎn)環(huán)境用的是什么消息中間件?
這個首先你可以說下你們公司選用的是什么消息中間件,比如用的是RabbitMQ,然后可以初步給一些你對不同MQ中間件技術(shù)的選型分析。
舉個例子:比如說ActiveMQ是老牌的消息中間件,國內(nèi)很多公司過去運用的還是非常廣泛的,功能很強大。
但是問題在于沒法確認ActiveMQ可以支撐互聯(lián)網(wǎng)公司的高并發(fā)、高負載以及高吞吐的復(fù)雜場景,在國內(nèi)互聯(lián)網(wǎng)公司落地較少。而且使用較多的是一些傳統(tǒng)企業(yè),用ActiveMQ做異步調(diào)用和系統(tǒng)解耦。
然后你可以說說RabbitMQ,他的好處在于可以支撐高并發(fā)、高吞吐、性能很高,同時有非常完善便捷的后臺管理界面可以使用。
另外,他還支持集群化、高可用部署架構(gòu)、消息高可靠支持,功能較為完善。
而且經(jīng)過調(diào)研,國內(nèi)各大互聯(lián)網(wǎng)公司落地大規(guī)模RabbitMQ集群支撐自身業(yè)務(wù)的case較多,國內(nèi)各種中小型互聯(lián)網(wǎng)公司使用RabbitMQ的實踐也比較多。
除此之外,RabbitMQ的開源社區(qū)很活躍,較高頻率的迭代版本,來修復(fù)發(fā)現(xiàn)的bug以及進行各種優(yōu)化,因此綜合考慮過后,公司采取了RabbitMQ。
但是RabbitMQ也有一點缺陷,就是他自身是基于erlang語言開發(fā)的,所以導(dǎo)致較為難以分析里面的源碼,也較難進行深層次的源碼定制和改造,畢竟需要較為扎實的erlang語言功底才可以。
然后可以聊聊RocketMQ,是阿里開源的,經(jīng)過阿里的生產(chǎn)環(huán)境的超高并發(fā)、高吞吐的考驗,性能卓越,同時還支持分布式事務(wù)等特殊場景。
而且RocketMQ是基于Java語言開發(fā)的,適合深入閱讀源碼,有需要可以站在源碼層面解決線上生產(chǎn)問題,包括源碼的二次開發(fā)和改造。
另外就是Kafka。Kafka提供的消息中間件的功能明顯較少一些,相對上述幾款MQ中間件要少很多。
但是Kafka的優(yōu)勢在于專為超高吞吐量的實時日志采集、實時數(shù)據(jù)同步、實時數(shù)據(jù)計算等場景來設(shè)計。
因此Kafka在大數(shù)據(jù)領(lǐng)域中配合實時計算技術(shù)(比如Spark Streaming、Storm、Flink)使用的較多。但是在傳統(tǒng)的MQ中間件使用場景中較少采用。
PS:如果大家對上述一些MQ技術(shù)還沒在自己電腦部署過,沒寫幾個helloworld體驗一下的話,建議先上各個技術(shù)的官網(wǎng)找到helloworld demo,自己跑一遍玩玩。
二、為什么在你們系統(tǒng)架構(gòu)中要引入消息中間件?
回答這個問題,其實就是讓你先說說消息中間件的常見使用場景。
然后結(jié)合你們自身系統(tǒng)對應(yīng)的使用場景,說一下在你們系統(tǒng)中引入消息中間件是解決了什么問題。
1、系統(tǒng)解耦
假設(shè)你有個系統(tǒng)A,這個系統(tǒng)A會產(chǎn)出一個核心數(shù)據(jù),現(xiàn)在下游有系統(tǒng)B和系統(tǒng)C需要這個數(shù)據(jù)。
那簡單,系統(tǒng)A就是直接調(diào)用系統(tǒng)B和系統(tǒng)C的接口發(fā)送數(shù)據(jù)給他們就好了。
整個過程,如下圖所示。
但是現(xiàn)在要是來了系統(tǒng)D、系統(tǒng)E、系統(tǒng)F、系統(tǒng)G,等等,十來個其他系統(tǒng)慢慢的都需要這份核心數(shù)據(jù)呢?如下圖所示。
大家可別以為這是開玩笑,一個大規(guī)模系統(tǒng),往往會拆分為幾十個甚至上百個子系統(tǒng),每個子系統(tǒng)又對應(yīng)N多個服務(wù),這些系統(tǒng)與系統(tǒng)之間有著錯綜復(fù)雜的關(guān)系網(wǎng)絡(luò)。
如果某個系統(tǒng)產(chǎn)出一份核心數(shù)據(jù),可能下游無數(shù)的其他系統(tǒng)都需要這份數(shù)據(jù)來實現(xiàn)各種業(yè)務(wù)邏輯。
此時如果你要是采取上面那種模式來設(shè)計系統(tǒng)架構(gòu),那么絕對你負責(zé)系統(tǒng)A的同學(xué)要被煩死了。
先是來一個人找他要求發(fā)送數(shù)據(jù)給一個新的系統(tǒng)H,系統(tǒng)A的同學(xué)要修改代碼然后在那個代碼里加入調(diào)用新系統(tǒng)H的流程。
一會那個系統(tǒng)B是個陳舊老系統(tǒng)要下線了,告訴系統(tǒng)A的同學(xué):別給我發(fā)送數(shù)據(jù)了,接著系統(tǒng)A再次修改代碼不再給這個系統(tǒng)B。
然后如果要是某個下游系統(tǒng)突然宕機了呢?系統(tǒng)A的調(diào)用代碼里是不是會拋異常?那系統(tǒng)A的同學(xué)會收到報警說異常了,結(jié)果他還要去care是下游哪個系統(tǒng)宕機了。
所以在實際的系統(tǒng)架構(gòu)設(shè)計中,如果全部采取這種系統(tǒng)耦合的方式,在某些場景下絕對是不合適的,系統(tǒng)耦合度太嚴重。
并且互相耦合起來并不是核心鏈路的調(diào)用,而是一些非核心的場景(比如上述的數(shù)據(jù)消費)導(dǎo)致了系統(tǒng)耦合,這樣會嚴重的影響上下游系統(tǒng)的開發(fā)和維護效率。
因此在上述系統(tǒng)架構(gòu)中,就可以采用MQ中間件來實現(xiàn)系統(tǒng)解耦。
系統(tǒng)A就把自己的一份核心數(shù)據(jù)發(fā)到MQ里,下游哪個系統(tǒng)感興趣自己去消費即可,不需要了就取消數(shù)據(jù)的消費,如下圖所示。
2、異步調(diào)用
假設(shè)你有一個系統(tǒng)調(diào)用鏈路,是系統(tǒng)A調(diào)用系統(tǒng)B,一般耗時20ms;系統(tǒng)B調(diào)用系統(tǒng)C,一般耗時200ms;系統(tǒng)C調(diào)用系統(tǒng)D,一般耗時2s,如下圖所示。
?現(xiàn)在最大的問題就是:用戶一個請求過來巨慢無比,因為走完一個鏈路,需要耗費20ms + 200ms + 2000ms(2s) = 2220ms,也就是2秒多的時間。
但是實際上,鏈路中的系統(tǒng)A調(diào)用系統(tǒng)B,系統(tǒng)B調(diào)用系統(tǒng)C,這兩個步驟起來也就220ms。
就因為引入了系統(tǒng)C調(diào)用系統(tǒng)D這個步驟,導(dǎo)致最終鏈路執(zhí)行時間是2秒多,直接將鏈路調(diào)用性能降低了10倍,這就是導(dǎo)致鏈路執(zhí)行過慢的罪魁禍?zhǔn)住?/p>
那此時我們可以思考一下,是不是可以將系統(tǒng)D從鏈路中抽離出去做成異步調(diào)用呢?其實很多的業(yè)務(wù)場景是可以允許異步調(diào)用的。
舉個例子,你平時點個外賣,咔嚓一下子下訂單然后付款了,此時賬戶扣款、創(chuàng)建訂單、通知商家給你準(zhǔn)備菜品。
接著,是不是需要找個騎手給你送餐?那這個找騎手的過程,是需要一套復(fù)雜算法來實現(xiàn)調(diào)度的,比較耗時。
但是其實稍微晚個幾十秒完成騎手的調(diào)度都是ok的,因為實際并不需要在你支付的一瞬間立馬給你找好騎手,也沒那個必要。
那么我們是不是就可以把找騎手給你送餐的這個步驟從鏈路中抽離出去,做成異步化的,哪怕延遲個幾十秒,但是只要在一定時間范圍內(nèi)給你找到一個騎手去送餐就可以了。
這樣是不是就可以讓你下訂單點外賣的速度變得超快?支付成功之后,直接創(chuàng)建好訂單、賬戶扣款、通知商家立馬給你準(zhǔn)備做菜就ok了,這個過程可能就幾百毫秒。
然后后臺異步化的耗費可能幾十秒通過調(diào)度算法給你找到一個騎手去送餐,但是這個步驟不影響我們快速下訂單。
當(dāng)然我們不是說那些大家熟悉的外賣平臺的技術(shù)架構(gòu)就一定是這么實現(xiàn)的,只不過是用一個生活中常見的例子給大家舉例說明而已。
所以上面的鏈路也是同理,如果業(yè)務(wù)流程支持異步化的話,是不是就可以考慮把系統(tǒng)C對系統(tǒng)D的調(diào)用抽離出去做成異步化的,不要放在鏈路中同步依次調(diào)用。
這樣,實現(xiàn)思路就是系統(tǒng)A -> 系統(tǒng)B -> 系統(tǒng)C,直接就耗費220ms后直接成功了。
然后系統(tǒng)C就是發(fā)送個消息到MQ中間?件里,由系統(tǒng)D消費到消息之后慢慢的異步來執(zhí)行這個耗時2s的業(yè)務(wù)處理。通過這種方式直接將核心鏈路的執(zhí)行性能提升了10倍。
整個過程,如下圖所示。
3、流量削峰
假設(shè)你有一個系統(tǒng),平時正常的時候每秒可能就幾百個請求,系統(tǒng)部署在8核16G的機器的上,正常處理都是ok的,每秒幾百請求是可以輕松抗住的。
但是如下圖所示,在高峰期一下子來了每秒鐘幾千請求,瞬時出現(xiàn)了流量高峰,此時你的選擇是要搞10臺機器,抗住每秒幾千請求的瞬時高峰嗎?
那如果瞬時高峰每天就那么半個小時,接著直接就降低為了每秒就幾百請求,如果你線上部署了很多臺機器,那么每臺機器就處理每秒幾十個請求就可以了,這不是有點浪費機器資源嗎?
大部分時候,每秒幾百請求,一臺機器就足夠了,但是為了抗那每天瞬時的高峰,硬是部署了10臺機器,每天就那半個小時有用,別的時候都是浪費資源的。
但是如果你就部署一臺機器,那會導(dǎo)致瞬時高峰時,一下子壓垮你的系統(tǒng),因為絕對無法抗住每秒幾千的請求高峰。
此時我們就可以用MQ中間件來進行流量削峰。所有機器前面部署一層MQ,平時每秒幾百請求大家都可以輕松接收消息。
一旦到了瞬時高峰期,一下涌入每秒幾千的請求,就可以積壓在MQ里面,然后那一臺機器慢慢的處理和消費。
等高峰期過了,再消費一段時間,MQ里積壓的數(shù)據(jù)就消費完畢了。
這個就是很典型的一個MQ的用法,用有限的機器資源承載高并發(fā)請求,如果業(yè)務(wù)場景允許異步削峰,高峰期積壓一些請求在MQ里,然后高峰期過了,后臺系統(tǒng)在一定時間內(nèi)消費完畢不再積壓的話,那就很適合用這種技術(shù)方案。