消息隊列つ登場
大家好,我是老三,是一個電商公司的程式員,負責訂單系統的開發。
掉了不少頭發之後,我完成了使用者訂單支付的開發。
訂單支付的業務是這樣的。使用者支付完成之後,我需要更新訂單狀态,這一部分是在本系統完成的。接下來,我要調用庫存系統,減庫存,好了,剩下的就是庫存系統的事情了。
開發、聯調、測試、上線,我的小日子變得清閑起來,每天就是在群裡吹牛打屁。
可是沒過兩天,産品妹子,找過來了,她說,她想加個功能,使用者完成訂單支付以後,要增加使用者的積分。
沒問題,so easy,噼裡啪啦,我兩天就做完了,無非是調用一下會員系統。
這天,正和沙雕群友鬥圖的時候,産品妹子過來,他說要接入消息系統,好,搞!
又過兩天,她說要添加營銷系統,行吧,幹!
又過兩天,她說要搞推薦系統,嗯……,來吧!
又過兩天……
于是系統就變成了這個樣子:
就這樣,我過上了暗無天日的日子,我要維護和若幹個系統的對接,每次他們釋出新版本,我都要跟着值班。
我要疊代,也要改和幾個系統的對接代碼。
周一,營銷系統;
周二,庫存系統;
……
這天,眼圈發黑的我正在和下遊服務撕巴的時候,突然忍不住兩腿戰戰,她來了,産品妹(女)子(王)來了——她是我不能拒絕的女人。
脆弱的眼淚流了滿面,我的猿生一片灰暗……
沒想到,代救星出現了,我的好朋友傲天過來了,拿鼻孔看着我。
“你個Loser,竟不知道用消息隊列,怪不得天天被人欺負,哼!”
一語驚醒夢中人,為什麼不用消息隊列啊?
于是我引入消息隊列,對系統進行了重構。
這下好了,我隻管
更新訂單狀态
,剩下的丢給消息隊列,你們這些下遊自己去消息隊列消費消息就好了,别來纏着我了。
引入消息隊列之後,又是一個閑适的下午。
我沒有在群聊裡扯扯,因為我退群了。
前幾天,我受到了前所未有的傷害——
我在群裡嘲諷一個老哥,技術真菜,連消息隊列都不會!
老哥反手就發出他和女朋友的合照,“單身狗,技術好又怎麼樣,連個女朋友都沒有!”
我瞬間san值狂掉!
“程式員單身,不算單身……new個對象的事,能算單身麼?”接連着便是什麼難懂的話,什麼“沒有妹子”,什麼“哲學”之類,引得衆人都哄笑起來,群裡充滿了快活的空氣。
于是,這個下午我盯着空空如也的需求單發呆,公司真的沒有妹子麼?……
好了,冗長的前奏結束了,接下來該進入正文了😂。
消息隊列つ用途
在上面的前言中,我們已經了解了消息隊列最重要的一個用途:
- 解耦
通過消息隊列,降低系統間的耦合,避免過多的調用。
就好像公司的行政小姐姐要通知一件事情,她通常會是在群裡發一個公告,然後我們扣1就行了。要是一個個通知,她要通知幾十上百次。
- 異步
還是上面我們提到的訂單支付,支付之後,我們要扣減庫存、增加積分、發送消息等等,這樣一來這個鍊路就長了,鍊路一長,響應時間就變長了。引入消息隊列,除了
更新訂單狀态
,其它的都可以異步去做,這樣一來就來,就能更快地響應我們的上遊。
為什麼不用多線程之類的方式做異步呢?——
嗨,隻用多線程做異步,不是還得寫代碼去調那一堆下遊嗎,是以這又回到了解耦這個問題上。
- 削峰
消息隊列同樣可以用來削峰。
用一個比喻,一條河流,假如它的下遊能容納的水量是有限的,為了防止洪水沖垮堤壩,我們應該怎麼辦呢?
我們可以在上遊修建一個水庫,洪峰來的時候,我們先把水給蓄起來,閘口裡隻放出下遊能承受地住的水量。
放在我們的系統,也是一個道理。比如秒殺系統,平時流量很低,但是要做秒殺活動,秒殺的時候流量瘋狂怼進來,你的伺服器,Redis,MySQL各自的承受能力都不一樣,直接全部流量照單全收肯定有問題啊,嚴重點可能直接打挂了。
是以一樣,我們可以把請求放到隊列裡面,隻放出我們下遊服務能處理的流量,這樣就能抗住短時間的大流量了。
除了這三大用途之外,還可以利用隊列本身的順序性,來滿足消息必須按順序投遞的場景;利用隊列 + 定時任務來實作消息的延時消費 ……
消息隊列つ本質
過氣老北鼻馬老師有三招——
接
、
化
發
。
消息隊列核心有三闆斧:
發
存
消費
生産者先将消息投遞一個叫做「隊列」的容器中,然後再從這個容器中取出消息,最後再轉發給消費者[1]。
上面這個圖便是消息隊列最原始的模型,它包含了消息隊列中的一兩個關鍵詞
消息
和隊列
隊列
:
- 消息:就是要傳輸的資料,可以是最簡單的文本字元串,也可以是自定義的複雜格式。
- 隊列:大家應該再熟悉不過了,是一種先進先出資料結構。它是存放消息的容器,消息從隊尾入隊,從隊頭出隊,入隊即發消息的過程,出隊即收消息的過程。
是以消息隊列的本質就是把要傳輸的資料放在隊列中。
圍繞着這個本質:
- 把資料放到消息隊列的角色就是
生産者
- 把資料從隊列中取出的就是
消費者
消息隊列つ模型
[1]我們上面已經了解了消息隊列模型的本質,随着應用場景的變化,消息隊列的模型逐漸發生了一些演進。
就好像我們的文字通訊,最開始單對單地發消息,後來可以群發,再後來,可以拉一個群聊。
- 隊列模型
最初的消息隊列就是上一節講的原始模型,它是一個嚴格意義上的隊列(Queue)。消息按照什麼順序寫進去,就按照什麼順序讀出來。不過,隊列沒有 “讀” 這個操作,讀就是出隊,從隊頭中 “删除” 這個消息。
如果有多個生産者往同一個隊列裡面發送消息,這個隊列中可以消費到的消息,就是這些生産者生産的所有消息的合集。消息的順序就是這些生産者發送消息的自然順序。
如果有多個消費者接收同一個隊列的消息,這些消費者之間實際上是競争的關系,每個消費者隻能收到隊列中的一部分消息,也就是說任何一條消息隻能被其中的一個消費者收到。
- 釋出 - 訂閱模型
如果需要将一份消息資料分發給多個消費者,并且每個消費者都要求收到全量的消息。很顯然,隊列模型無法滿足這個需求。
一個可行的方案是:為每個消費者建立一個單獨的隊列,讓生産者發送多份。這種做法比較笨,而且同一份資料會被複制多份,也很浪費空間。
為了解決這個問題,就演化出了另外一種消息模型:釋出-訂閱模型。
在釋出 - 訂閱模型中,消息的發送方稱為釋出者(Publisher),消息的接收方稱為訂閱者(Subscriber),服務端存放消息的容器稱為主題(Topic)。釋出者将消息發送到主題中,訂閱者在接收消息之前需要先“訂閱主題”。“訂閱”在這裡既是一個動作,同時還可以認為是主題在消費時的一個邏輯副本,每份訂閱中,訂閱者都可以接收到主題的所有消息。
仔細對比下它和 “隊列模式” 的異同:生産者就是釋出者,隊列就是主題,消費者就是訂閱者,無本質差別。唯一的不同點在于:一份消息資料是否可以被多次消費。
消息隊列つ代價
天下沒有白吃的午餐——這句話放在系統設計中同樣适用。引入了消息隊列,解決了一些問題,但同時也增加了系統的複雜性和維護成本。[5]
- 高可用
前面說,我們引入了消息隊列,降低了系統間的耦合,但是,我們對消息隊列的依賴也變重了,想想,要是消息隊列挂了,那我們下遊就全涼了。
是以,我們消息隊列的部署必須保證高可用,至少是
主/從
,一般都得
叢集/分布式
- 消息丢失問題
我們将資料寫到消息隊列上,下遊服務沒來得及取消息隊列的資料,突然挂了。如果沒有做任何的措施,我們的資料就丢了。
那可怎麼辦呢?
消息隊列中的資料得想辦法存在别的地方,這樣才盡可能減少資料的丢失。
問題又來了,存在哪呢?
- 磁盤?
- 資料庫?
- Redis?
- 分布式檔案系統?
同步存儲還是異步存儲?
- 重複消費問題
要是我們網絡延遲或者什麼原因,導緻下遊重複消費怎麼辦?
我們這個問題是在消息隊列解決?還是在下遊服務解決?
還有其它的順序消息等等問題。
這些問題都是選型時候需要充分考慮的。
消息隊列つ選擇
目前在市面上比較主流的主要有四大消息中間件:Kafka、ActiveMQ、RabbitMQ、RocketMQ 。
它們的對比我整理了一張圖:
消息隊列つ終焉
好了,到這我們就了解了消息隊列的一些基礎的知識。
門外漢,這個門,你入了嗎?我反正是在裡面打滾了。
“簡單的事情重複做,重複的事情認真做,認真的事情有創造性地做!”——
我是三分惡,一個能文能武的全棧開發。
點贊
不迷路,咱們下期見!
關注