歡迎光臨
每天分享高質量文章

哥們,你們的系統架構中為什麼要引入訊息中間件?

這篇文章開始,我們把訊息中間件這塊高頻的面試題給大家說一下,也會涵蓋一些MQ中間件常見的技術問題。

假如面試官看你簡歷里寫了MQ中間件的使用經驗,很可能會有如下問題:

  • 你們公司生產環境用的是什麼訊息中間件?

  • 為什麼要在系統里引入訊息中間件?

  • 引入訊息中間件之後會有什麼好處以及壞處?

好,我們一個個的來分析!

你們公司生產環境用的是什麼訊息中間件?

     

這個首先你可以說下你們公司選用的是什麼訊息中間件,比如用的是RabbitMQ,然後可以初步給一些你對不同MQ中間件技術的選型分析。

舉個例子:比如說ActiveMQ是老牌的訊息中間件,國內很多公司過去運用的還是非常廣泛的,功能很強大。

但是問題在於沒法確認ActiveMQ可以支撐互聯網公司的高併發、高負載以及高吞吐的複雜場景,在國內互聯網公司落地較少。而且使用較多的是一些傳統企業,用ActiveMQ做異步呼叫和系統解耦。

然後你可以說說RabbitMQ,他的好處在於可以支撐高併發、高吞吐、性能很高,同時有非常完善便捷的後臺管理界面可以使用。

另外,他還支持集群化、高可用部署架構、訊息高可靠支持,功能較為完善。

而且經過調研,國內各大互聯網公司落地大規模RabbitMQ集群支撐自身業務的case較多,國內各種中小型互聯網公司使用RabbitMQ的實踐也比較多。

除此之外,RabbitMQ的開源社區很活躍,較高頻率的迭代版本,來修複發現的bug以及進行各種優化,因此綜合考慮過後,公司採取了RabbitMQ。

但是RabbitMQ也有一點缺陷,就是他自身是基於erlang語言開發的,所以導致較為難以分析裡面的原始碼,也較難進行深層次的原始碼定製和改造,畢竟需要較為扎實的erlang語言功底才可以。

然後可以聊聊RocketMQ,是阿裡開源的,經過阿裡的生產環境的超高併發、高吞吐的考驗,性能卓越,同時還支持分佈式事務等特殊場景。

而且RocketMQ是基於Java語言開發的,適合深入閱讀原始碼,有需要可以站在原始碼層面解決線上生產問題,包括原始碼的二次開發和改造。

另外就是Kafka。Kafka提供的訊息中間件的功能明顯較少一些,相對上述幾款MQ中間件要少很多。

但是Kafka的優勢在於專為超高吞吐量的實時日誌採集、實時資料同步、實時資料計算等場景來設計。

因此Kafka在大資料領域中配合實時計算技術(比如Spark Streaming、Storm、Flink)使用的較多。但是在傳統的MQ中間件使用場景中較少採用。

PS:如果大家對上述一些MQ技術還沒在自己電腦部署過,沒寫幾個helloworld體驗一下的話,建議先上各個技術的官網找到helloworld demo,自己跑一遍玩玩

為什麼在你們系統架構中要引入訊息中間件?

         

回答這個問題,其實就是讓你先說說訊息中間件的常見使用場景。

然後結合你們自身系統對應的使用場景,說一下在你們系統中引入訊息中間件是解決了什麼問題。解耦                

1)系統解耦

假設你有個系統A,這個系統A會產出一個核心資料,現在下游有系統B和系統C需要這個資料。

那簡單,系統A就是直接呼叫系統B和系統C的接口發送資料給他們就好了。

整個過程,如下圖所示。

但是現在要是來了系統D、系統E、系統F、系統G,等等,十來個其他系統慢慢的都需要這份核心資料呢?如下圖所示。

大家可別以為這是開玩笑,一個大規模系統,往往會拆分為幾十個甚至上百個子系統,每個子系統又對應N多個服務,這些系統與系統之間有著錯綜複雜的關係網絡。

如果某個系統產出一份核心資料,可能下游無數的其他系統都需要這份資料來實現各種業務邏輯。

此時如果你要是採取上面那種樣式來設計系統架構,那麼絕對你負責系統A的同學要被煩死了。

先是來一個人找他要求發送資料給一個新的系統H,系統A的同學要修改代碼然後在那個代碼裡加入呼叫新系統H的流程。

一會那個系統B是個陳舊老系統要下線了,告訴系統A的同學:別給我發送資料了,接著系統A再次修改代碼不再給這個系統B。

然後如果要是某個下游系統突然宕機了呢?系統A的呼叫代碼里是不是會拋異常?那系統A的同學會收到報警說異常了,結果他還要去care是下游哪個系統宕機了。

所以在實際的系統架構設計中,如果全部採取這種系統耦合的方式,在某些場景下絕對是不合適的,系統耦合度太嚴重。

並且互相耦合起來並不是核心鏈路的呼叫,而是一些非核心的場景(比如上述的資料消費)導致了系統耦合,這樣會嚴重的影響上下游系統的開發和維護效率。

因此在上述系統架構中,就可以採用MQ中間件來實現系統解耦

系統A就把自己的一份核心資料發到MQ里,下游哪個系統感興趣自己去消費即可,不需要了就取消資料的消費,如下圖所示。

2)異步呼叫

假設你有一個系統呼叫鏈路,是系統A呼叫系統B,一般耗時20ms;系統B呼叫系統C,一般耗時200ms;系統C呼叫系統D,一般耗時2s,如下圖所示。

現在最大的問題就是:用戶一個請求過來巨慢無比,因為走完一個鏈路,需要耗費20ms + 200ms + 2000ms(2s) = 2220ms,也就是2秒多的時間。

但是實際上,鏈路中的系統A呼叫系統B,系統B呼叫系統C,這兩個步驟起來也就220ms。

就因為引入了系統C呼叫系統D這個步驟,導致最終鏈路執行時間是2秒多,直接將鏈路呼叫性能降低了10倍,這就是導致鏈路執行過慢的罪魁禍首。

那此時我們可以思考一下,是不是可以將系統D從鏈路中抽離出去做成異步呼叫呢?其實很多的業務場景是可以允許異步呼叫的。

舉個例子,你平時點個外賣,咔嚓一下子下訂單然後付款了,此時賬戶扣款、創建訂單、通知商家給你準備菜品。

接著,是不是需要找個騎手給你送餐?那這個找騎手的過程,是需要一套複雜演算法來實現調度的,比較耗時。

但是其實稍微晚個幾十秒完成騎手的調度都是ok的,因為實際並不需要在你支付的一瞬間立馬給你找好騎手,也沒那個必要。

那麼我們是不是就可以把找騎手給你送餐的這個步驟從鏈路中抽離出去,做成異步化的,哪怕延遲個幾十秒,但是只要在一定時間範圍內給你找到一個騎手去送餐就可以了。

這樣是不是就可以讓你下訂單點外賣的速度變得超快?支付成功之後,直接創建好訂單、賬戶扣款、通知商家立馬給你準備做菜就ok了,這個過程可能就幾百毫秒。

然後後臺異步化的耗費可能幾十秒通過調度演算法給你找到一個騎手去送餐,但是這個步驟不影響我們快速下訂單。

當然我們不是說那些大家熟悉的外賣平臺的技術架構就一定是這麼實現的,只不過是用一個生活中常見的例子給大家舉例說明而已。

所以上面的鏈路也是同理,如果業務流程支持異步化的話,是不是就可以考慮把系統C對系統D的呼叫抽離出去做成異步化的,不要放在鏈路中同步依次呼叫。

這樣,實現思路就是系統A -> 系統B -> 系統C,直接就耗費220ms後直接成功了。

然後系統C就是發送個訊息到MQ中間件里,由系統D消費到訊息之後慢慢的異步來執行這個耗時2s的業務處理。通過這種方式直接將核心鏈路的執行性能提升了10倍。

整個過程,如下圖所示。

3)流量削峰

假設你有一個系統,平時正常的時候每秒可能就幾百個請求,系統部署在8核16G的機器的上,正常處理都是ok的,每秒幾百請求是可以輕鬆抗住的。

但是如下圖所示,在高峰期一下子來了每秒鐘幾千請求,瞬時出現了流量高峰,此時你的選擇是要搞10台機器,抗住每秒幾千請求的瞬時高峰嗎?

那如果瞬時高峰每天就那麼半個小時,接著直接就降低為了每秒就幾百請求,如果你線上部署了很多台機器,那麼每台機器就處理每秒幾十個請求就可以了,這不是有點浪費機器資源嗎?

大部分時候,每秒幾百請求,一臺機器就足夠了,但是為了抗那每天瞬時的高峰,硬是部署了10台機器,每天就那半個小時有用,別的時候都是浪費資源的。

但是如果你就部署一臺機器,那會導致瞬時高峰時,一下子壓垮你的系統,因為絕對無法抗住每秒幾千的請求高峰。

此時我們就可以用MQ中間件來進行流量削峰。所有機器前面部署一層MQ,平時每秒幾百請求大家都可以輕鬆接收訊息。

一旦到了瞬時高峰期,一下涌入每秒幾千的請求,就可以積壓在MQ裡面,然後那一臺機器慢慢的處理和消費。

等高峰期過了,再消費一段時間,MQ里積壓的資料就消費完畢了。

這個就是很典型的一個MQ的用法,用有限的機器資源承載高併發請求,如果業務場景允許異步削峰,高峰期積壓一些請求在MQ里,然後高峰期過了,後臺系統在一定時間內消費完畢不再積壓的話,那就很適合用這種技術方案。

下集預告:

關於第三個問題:引入訊息中間件之後會有什麼好處以及壞處。

我們將會在下一篇文章:《兄弟,那你說說系統架構引入訊息中間件有什麼缺點呢?》詳細闡述,敬請關註。

END

 

本文作者:

中華石杉,十餘年BAT架構經驗傾囊相授。個人微信公眾號:石杉的架構筆記(ID: shishan100)

赞(0)

分享創造快樂