面試官:你的項(xiàng)目為什么要用消息隊(duì)列?

今天我們探討一種廣泛使用的中間件:消息隊(duì)列。
消息隊(duì)列由來(lái)已久,通常用于不同系統(tǒng)之間的通信。下圖以星巴克的工作方式為例,來(lái)說(shuō)明消息隊(duì)列的概念。
在星巴克,收銀員接受訂單并收錢(qián),然后把顧客的名字寫(xiě)在咖啡杯上,交給下一個(gè)步驟。咖啡制作師拿起訂單和杯子制作咖啡。然后,顧客到柜臺(tái)領(lǐng)取咖啡。這三個(gè)步驟同步進(jìn)行。收銀員只需將訂單以咖啡杯的形式放入,無(wú)需等待訂單完成??Х戎谱鲙熤恍鑼⒅谱魍瓿傻目Х确旁诠衽_(tái)上,就可以去制作下一杯咖啡了,無(wú)需等待顧客取走。
這個(gè)過(guò)程的美妙之處在于每個(gè)步驟都是獨(dú)立運(yùn)行的,很像一個(gè)異步系統(tǒng)。
這種異步處理(每一步都無(wú)需等待前一步完成)大大提高了系統(tǒng)的吞吐量。例如,收銀員在接受另一份訂單之前,無(wú)需等待您的飲料制作完成。
01
一個(gè)秒殺的案例
我們來(lái)看一個(gè)真實(shí)系統(tǒng)的例子:電商中經(jīng)常出現(xiàn)的秒殺。由于秒殺期間用戶(hù)活躍度激增,會(huì)給系統(tǒng)帶來(lái)很大壓力。消息隊(duì)列通常在后端優(yōu)化中起著關(guān)鍵作用。
下圖列出了一個(gè)簡(jiǎn)化的電子商務(wù)秒殺架構(gòu)。這時(shí)我們還沒(méi)有對(duì)其進(jìn)行優(yōu)化,只是簡(jiǎn)單列出了數(shù)據(jù)流。
-
步驟 1 和 2:客戶(hù)向訂單服務(wù)下訂單。
-
步驟 3:在處理付款之前,訂單服務(wù)會(huì)鎖定該用戶(hù)占用的庫(kù)存。
-
步驟 4:訂單服務(wù)向支付服務(wù)發(fā)送支付指令。支付服務(wù)向 3 個(gè)服務(wù)發(fā)送消息:支付渠道、通知服務(wù)和數(shù)據(jù)分析。
-
步驟 5.1 和 6.1:支付服務(wù)向外部支付通道發(fā)送支付指令。支付通道與外部支付服務(wù)提供商(Payment Service Provider, PSP)對(duì)話(huà),最終完成交易。
-
步驟 5.2 和 6.2:支付服務(wù)向通知服務(wù)發(fā)送消息,然后通知服務(wù)通過(guò)電子郵件或短信向客戶(hù)發(fā)送通知。
-
步驟 5.3:支付服務(wù)向數(shù)據(jù)分析服務(wù)發(fā)送交易詳情。
在秒殺期間,無(wú)縫的用戶(hù)體驗(yàn)至關(guān)重要。為了在高流量情況下保持服務(wù)響應(yīng)速度,可以在系統(tǒng)多個(gè)階段集成消息隊(duì)列,以確保最佳性能。
02
使用消息隊(duì)列優(yōu)化秒殺系統(tǒng)
我們下面一步一步地優(yōu)化這個(gè)秒殺系統(tǒng),來(lái)看看消息隊(duì)列給系統(tǒng)帶來(lái)了什么好處。
扇出
扇出(fan out)這個(gè)概念來(lái)源于電子電路,是指由一個(gè)邏輯門(mén)的輸出驅(qū)動(dòng)幾個(gè)下游邏輯門(mén)的輸入,從而連接形成更復(fù)雜的電路。
支付服務(wù)將消息發(fā)送到三個(gè)下游服務(wù),分別用于不同的目的:支付渠道、通知服務(wù)和數(shù)據(jù)分析。生產(chǎn)者(支付服務(wù))只需將信息放到隊(duì)列中,不同的消費(fèi)者就可以按照自己的節(jié)奏處理信息。這種扇出簡(jiǎn)化了系統(tǒng)架構(gòu)。
異步處理
以星巴克為例,正如收銀員不會(huì)等待咖啡煮好一樣,訂單服務(wù)也不會(huì)等待付款最終完成。支付指令被置于隊(duì)列中,一旦最終完成,客戶(hù)就會(huì)收到通知。
流控
在秒殺活動(dòng)中,可能會(huì)有數(shù)以萬(wàn)計(jì)的用戶(hù)同時(shí)下訂單。如何在滿(mǎn)足客戶(hù)需求和保持系統(tǒng)穩(wěn)定之間取得平衡至關(guān)重要。
一種常見(jiàn)的方法是在特定時(shí)間段內(nèi)對(duì)收到的請(qǐng)求數(shù)量設(shè)置上限,使其與系統(tǒng)容量相匹配。過(guò)多的請(qǐng)求可能會(huì)被拒絕或被要求在短暫延遲后重試。這種方法可確保系統(tǒng)保持穩(wěn)定,不會(huì)不堪重負(fù)。對(duì)于成功通過(guò)的請(qǐng)求,消息隊(duì)列可確保它們得到高效、有序的處理。如果系統(tǒng)的某個(gè)部分暫時(shí)滯后,訂單也不會(huì)丟失。它將被保留在隊(duì)列中,直到可以處理為止。
服務(wù)解耦
我們的設(shè)計(jì)在多處使用了消息隊(duì)列。服務(wù)之間使用定義明確的消息接口進(jìn)行交互,而不是彼此緊密依賴(lài)。每個(gè)服務(wù)都可以獨(dú)立修改和部署。每個(gè)組件都可以用不同的編程語(yǔ)言開(kāi)發(fā)。這為架構(gòu)設(shè)計(jì)帶來(lái)了靈活性。
橫向可擴(kuò)展性
由于服務(wù)是解耦的,因此我們可以根據(jù)業(yè)務(wù)需求對(duì)它們進(jìn)行獨(dú)立擴(kuò)展。每個(gè)服務(wù)都能以不同的容量提供服務(wù),因此我們可以根據(jù)其計(jì)劃的 QPS(query per second)或 TPS(transaction per second)進(jìn)行擴(kuò)展。
消息持久性
消息隊(duì)列也可用作存儲(chǔ)消息的中間件。如果上游服務(wù)崩潰,下游服務(wù)總能從消息隊(duì)列中拾取消息進(jìn)行處理。這樣,恢復(fù)功能就從每個(gè)服務(wù)中轉(zhuǎn)移出來(lái),交由消息隊(duì)列負(fù)責(zé)。
批量處理
我們有時(shí)需要進(jìn)行批量數(shù)據(jù)的處理。例如,當(dāng)支付服務(wù)向數(shù)據(jù)分析服務(wù)發(fā)送消息時(shí),數(shù)據(jù)分析服務(wù)并不需要執(zhí)行實(shí)時(shí)更新,而是設(shè)置一個(gè)滾動(dòng)窗口來(lái)批量處理窗口內(nèi)的數(shù)據(jù)。批量處理是下游服務(wù)的要求,因此支付服務(wù)無(wú)需知道,只需將消息放入隊(duì)列即可。消息定序 秒殺活動(dòng)的庫(kù)存商品數(shù)量是有限的。例如,秒殺只提供 10 部 iPhone,但下單的用戶(hù)超過(guò) 10,000 人。我們?cè)撊绾螞Q定訂單順序呢?用消息隊(duì)列來(lái)保存所有訂單會(huì)有一個(gè)自然的順序:隊(duì)列中的前 10 位將獲得 iPhone。
下圖中顯示了所有的優(yōu)化點(diǎn)。服務(wù)通過(guò)消息隊(duì)列連接并解耦。這樣,架構(gòu)就能實(shí)現(xiàn)更高的吞吐量。
歷史好文:
