開篇思考
- MQ 為什麼在系統中使用?一定要在分布式系統中使用嗎?
- MQ 有哪些中間件?他們有哪些特點?
- MQ 給系統帶來好處的同時有沒有帶來什麼問題?如何解決?
在阿裡的面試中,面試官問到關于 MQ 的幾個問題:
- 你的項目中 MQ 的作用?
- 為什麼選擇這款 MQ 作為消息中間件?
- 重複消費怎麼辦?
- 如何確定消息被消費?
-
有遇到其他問題嗎?
那麼接下來帶着問題先思考下,有好的想法可以在評論區留言,大家一起分享。
消息中間件在系統中的使用
我之前寫過一篇關于 rocketMQ 實作分布式鎖的文章,主要介紹如何使用 RocketMQ 實作分布式鎖,
《Springcloud + RocketMQ 解決分布式事務》但是這個功能并不是 MQ 基本功能,也不是所有 MQ 都有的功能。
MQ 在系統中到底有哪些作用呢?抛開基本的消息釋出訂閱不說,還有以下幾點:
- 分布式系統解耦
- 不需要立即傳回的業務異步處理
- 削峰填谷,不直接通路服務,緩解服務壓力,增加性能
- 日志記錄
在分布式系統中,要麼是通過 rest 調用,要麼是通過 dubbo 等 RPC 調用,但是有些場景需要解耦設計,不能直接調用。
比如消息驅動的系統中,消息發送者完成本地業務,發送消息,多平台的消息消費者服務需要收到推送的消息,然後繼續處理其他業務。
看這兩個架構圖,第一種 BC 都直接依賴 A 服務,那麼如果 A 中的接口修改,BC 都要跟着做修改,耦合度高。
第二種,通過 MQ 來作為中間件收發消息,BC 隻依賴收到的消息而不是具體的接口,這樣即使 A 服務修改或者增加其他服務,都隻要訂閱MQ就行。
不要求實時的業務異步處理
使用者注冊業務流程為例,
- 使用者注冊入庫
- 使用者驗證郵件發送
- 使用者驗證短信發送
原來的系統設計,這樣的服務流程會串行處理,即先 1-2-3 ;但是這裡可以思考下,如果單個服務單台機器的情況下,注冊使用者特别多,系統能不能抗住?
這裡假設各個階段的時間 1 = 50ms , 2 = 50ms , 3 = 50ms,那麼一個請求下來就是 all = 150ms;
這裡再假設,這個伺服器 CPU = 1 , 且隻能處理單線程,那麼以這種單台伺服器單線程的 QPS 來算;QPS = 1000/150 ≈ 7
現在我要讓這個 QPS * 3 提升三倍,這個時候引入 MQ 服務作為中間件

如圖可見,我在 A 服務使用者注冊完成後,就直接傳回了,這個時候 MQ 用來發送異步處理消息,B,C 服務分别處理。
A 不用等待 B、C 的傳回結果 ,這樣使用者體驗就是隻有 50ms 等待時間。而在郵件、短信這個階段,因為網絡延遲原因,
使用者可以接受一定時間的等待。
削峰填谷
一般的服務,我們的請求通路到系統都是直接請求,這樣的模式在使用者通路量不大的情況下,問題不是很大。
但是如果使用者請求達到了一定的瓶頸或者産生了一些問題,我們就需要考慮優化我們的架構設計,MQ 中間件正是解決辦法之一。
下面以秒殺系統為例分析問題
秒殺系統瞬間百萬并發,怎麼處理?一般秒殺系統會進行請求過濾,無效、重複都會被過濾一遍,剩下的才真正進入到秒殺服務、訂單服務。
但即使這樣并發仍然很高,如果網關把全部請求都轉發到下遊訂單服務,一樣會壓垮下遊系統,造成服務不可用甚至雪崩。
真實的秒殺系統更複雜 ,包含 Nginx 、網關、注冊中心、redis 緩存、mysql 叢集、消息隊列叢集
解決方式就是将上遊處理的較快的任務,加入到隊列處理,下遊逐一消費隊列,直到所有隊列消費完成。
假如秒殺服務處理請求數:1000/s,
下遊訂單服務處理請求書:10/s,
為了不給下遊訂單服務造成壓力,秒殺後的資訊發送到隊列,訂單服務就可以從容淡定的每秒處理十個,而不是直接塞 1000 個請求
也不管人家願意不願意。
到這裡,可以總結下秒殺系統的過濾方式:
- 頁面按鈕點選一次置灰
- 每秒透過請求數限制,例如 100/s,可以使用 Nginx ,sentinel
- 過濾同一使用者的重複請求,通過使用者唯一辨別、商品資訊,
- 通過消息隊列存儲成功的秒殺資訊,下遊訂單系統處理
日志
所有服務都将日志發送到 MQ 服務用來作為日志存儲。
MQ 作為中間件對日志進行持久化、轉發
大資料服務對 MQ 讀取和進行日志分析
MQ 怎麼選
有人上來就是一通性能比較,然後說 RabbitMQ 是世界上最好的 MQ...
你把挑選 MQ 比作挑老婆吧,上來就要全套,膚白貌美、前凸後翹、性感火辣、勤勞能幹。。。
真是缺乏社會的教育啊,兄弟
養得起嗎?動不動一套保養套餐,1W/月
守得住嗎?隔壁老王經常來你家吃飯吧,瘋狂腦補。。。
吃的消嗎?紅棗+枸杞+腎寶片,怕是心有餘力不足吧
言歸正傳,其實我覺得這是一個思考題,首先我們要看的應該是條件是哪些?
- 用途?是用來做日志、解耦、還是異步處理
- 公司情況?人員是否充足,現有人員技術棧情況,人員的技術棧實力
- 項目情況?項目周期,人員,使用者量,架構設計,是否老項目
- 主流 MQ 現狀?穩定可靠度,社群活躍度,文檔全面性,雲服務支援情況
上圖的例子日志消息就是使用的 kafka,為什麼是kafka?
Kafka是LinkedIn開源的分布式釋出-訂閱消息系統,屬于 Apache 頂級項目,社群活躍。
Kafka主要特點是基于Pull的模式來處理消息消費,追求高吞吐量,一開始的目的就是用于日志收集和傳輸。
後來版本開始支援複制,不支援事務,對消息的重複、丢失、錯誤沒有嚴格要求,适合産生大量資料的網際網路服務的資料收集業務。
但是 kafka 相對來說很重,需要依賴 zookeeper,大公司裡使用沒問題,也少不了專人維護。
RocketMQ 是阿裡開源的一套可靠消息系統,已經捐贈 Apache 成為頂級項目。剛開始定位于非日志的可靠消息傳輸,其實在日志處理方面性能也不錯。
目前支援的用戶端包括 java,c++,GO ,社群比較活躍,文檔還算全面。但是涉及到核心的要修改還是有難度的,畢竟阿裡雲靠賣這個服務賺錢呢。
是以如果公司實力不自信還是慎重選擇吧,實在不行可以直接購買雲服務,省心省力,還是那句話,看實際情況。
主流 MQ 的特點
下圖是來源網絡的圖檔,部分描述已經過時,但是基本不差,僅供參考:
如何確定消息不被重複消費
這裡簡單說說,後面專門針對這個問題進行書寫招供。
大緻就是一些特殊原因例如網絡原因,服務重新開機造成消息消費未被記錄,造成重複消費的可能。
一般的處理方式就是保證接口設計的幂等性,主旨通過唯一辨別判斷是否存在。
- redis 緩存使用,唯一性 token 儲存redis,每次消費後删除 token
- 唯一主鍵判斷,資料庫判斷是否存在該主鍵記錄,存在則更新,不存在則插入
喜歡文章請關注我
程式領域
點選關注+轉發,私信發送【面試】或者【資料】可以收獲更多資源