我們從三個方面了解消息中間件
what why how
1. 什么是消息中間件?
2. 為什么要使用消息中間件?
3. 怎么用消息中間件?
1. 什么是消息中間件?
消息中間件是指一種在需要進行網絡通信的系統進行通道的建立,數據或文件發送的中間件。消息中間件的一個重要作用是可以跨平台操作,為不同操作系統上的應用軟件集成提供便利。
現在越來越多的分布式應用系統采用消息中間件方式來構建,人們通過使用消息中間件把應用擴展到不同的操作系統和不同的網絡環境。基於消息的機制更適用於由事件驅動的應用,當一個事件發生時,消息中間件通知服務方應該進行如何操作。
使用消息中間件編程可以很好地擴展到不同的操作系統和硬件平台上。可以將消息中間件的核心安裝在需要進行消息傳遞的系統上,並在它們之間建立邏輯通道,由消息中間件實現消息發送。消息中間件既可以支持同步方式通訊,又可以支持異步方式通訊,實際上它是一種點到點的機制,因而可以很好地適用於面向對象的編程方式。
當今市面上有很多主流的消息中間件,如老牌的ActiveMQ、RabbitMQ,炙手可熱的Kafka,阿里巴巴自主開發RocketMQ等。
2.為什么要使用消息中間件?
回答這個問題,其實就是讓你先說說消息中間件的常見使用場景,然后結合你們自身系統對應的使用場景,說一下在你們系統中引入消息中間件是解決了什么問題。
1) 系統解耦
假設你有個系統A,這個系統A會產出一個核心數據,現在下游有系統B和系統C需要這個數據。那簡單,系統A就是直接調用系統B和系統C的接口發送數據給他們就好了。
但是現在要是來了系統D、系統E、系統F、系統G,等等,十來個其他系統慢慢的都需要這份核心數據呢?如下圖所示:
一個大規模系統,往往會拆分為幾十個甚至上百個子系統,每個子系統又對應N多個服務,這些系統與系統之間有着錯綜復雜的關系網絡。
如果某個系統產出一份核心數據,可能下游無數的其他系統都需要這份數據來實現各種業務邏輯。
此時如果你要是采取上面那種模式來設計系統架構,那么絕對你負責系統A的同學要被煩死了。
先是來一個人找他要求發送數據給一個新的系統H,系統A的同學要修改代碼然后在那個代碼里加入調用新系統H的流程。
一會那個系統B是個陳舊老系統要下線了,告訴系統A的同學:別給我發送數據了,接着系統A再次修改代碼不再給這個系統B。
然后如果要是某個下游系統突然宕機了呢?
系統A的調用代碼里是不是會拋異常?那系統A的同學會收到報警說異常了,結果他還要去關注是下游哪個系統宕機了。
所以在實際的系統架構設計中,如果全部采取這種系統耦合的方式,在某些場景下絕對是不合適的,系統耦合度太嚴重。
並且互相耦合起來並不是核心鏈路的調用,而是一些非核心的場景(比如上述的數據消費,日志等)導致了系統耦合,這樣會嚴重的影響上下游系統的開發和維護效率。
因此在上述系統架構中,就可以采用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里,然后高峰期過了,后台系統在一定時間內消費完畢不再積壓的話,那就很適合用這種技術方案。
3. 怎么用消息中間件?
當前使用較多的 消息隊列 有 RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMQ 等,而部分 數據庫 如 Redis、MySQL也可實現消息隊列的功能。
消息中間件組成:
Broker:消息服務器,作為server提供消息核心服務
Producer:消息生產者,業務的發起方,負責生產消息傳輸給broker,
Consumer:消息消費者,業務的處理方,負責從broker獲取消息並進行業務邏輯處理
Topic:主題,發布訂閱模式下的消息統一匯集地,不同生產者向topic發送消息,由MQ服務器分發到不同的訂閱者,實現消息的廣播
Queue:隊列,點對點模式下,特定生產者向特定queue發送消息,消費者訂閱特定的queue完成指定消息的接收
Message:消息體,根據不同通信協議定義的固定格式進行編碼的數據包,來封裝業務數據,實現消息的傳輸
消息中間件模式分類:
1)點對點:使用queue作為通信載體
說明:
消息生產者生產消息發送到queue中,然后消息消費者從queue中取出並且消費消息。
消息被消費以后,queue中不再存儲,所以消息消費者不可能消費到已經被消費的消息。 Queue支持存在多個消費者,但是對一個消息而言,只會有一個消費者可以消費
2) 發布/訂閱
說明:
消息生產者(發布)將消息發布到topic中,同時有多個消息消費者(訂閱)消費該消息。和點對點方式不同,發布到topic的消息會被所有訂閱者消費。
queue實現了負載均衡,將producer生產的消息發送到消息隊列中,由多個消費者消費。但一個消息只能被一個消費者接受,當沒有消費者可用時,這個消息會被保存直到有一個可用的消費者。
topic實現了發布和訂閱,當你發布一個消息,所有訂閱這個topic的服務都能得到這個消息,所以從1到N個訂閱者都能得到一個消息的拷貝。