為什么我們需要消息隊(duì)列?
在這期中,我們將深入探討一種廣泛使用的中間件:消息隊(duì)列。
消息隊(duì)列有著悠久的歷史,它們經(jīng)常用于不同系統(tǒng)之間的通信。圖1通過(guò)將其與星巴克的工作方式進(jìn)行比較,闡述了消息隊(duì)列的概念。
在星巴克,收銀員接受訂單并收取款項(xiàng),然后在咖啡杯上寫下顧客的名字,交給下一個(gè)步驟。制作咖啡的人拿起訂單和杯子,然后制作咖啡。然后顧客在柜臺(tái)上取走咖啡。這三個(gè)步驟是異步進(jìn)行的。收銀員只是將訂單以咖啡杯的形式放下,并不等待完成。制作咖啡的人只是將完成的咖啡放在柜臺(tái)上,并不等待顧客來(lái)取。
當(dāng)您在星巴克下訂單時(shí),收銀員接受訂單并在杯子上寫下您的名字,然后轉(zhuǎn)向下一個(gè)顧客。然后咖啡師拿起杯子,準(zhǔn)備您的飲品,然后留下供您取走。這個(gè)過(guò)程之所以美妙,是因?yàn)槊總€(gè)步驟都是獨(dú)立運(yùn)作的。這很像一個(gè)異步系統(tǒng)。
圖1 星巴克作為消息隊(duì)列的類比
這種異步處理,每個(gè)步驟都不必等待前一個(gè)步驟完成,顯著增加了系統(tǒng)的吞吐量。例如,收銀員不必等待您的飲品制作完成后才接受另一個(gè)訂單。
消息隊(duì)列的一個(gè)示例
現(xiàn)在,讓我們將焦點(diǎn)轉(zhuǎn)向一個(gè)真實(shí)世界的例子:電子商務(wù)中的限時(shí)搶購(gòu)。由于用戶活動(dòng)激增,限時(shí)搶購(gòu)可能會(huì)給系統(tǒng)帶來(lái)壓力。為了應(yīng)對(duì)這種需求,采用了許多策略,而消息隊(duì)列通常在后端優(yōu)化中發(fā)揮關(guān)鍵作用。
一個(gè)簡(jiǎn)化的電子商務(wù)限時(shí)搶購(gòu)架構(gòu)如圖2所示。
- 步驟1和2:顧客向訂單服務(wù)下訂單。
- 步驟3:在處理付款之前,訂單服務(wù)保留所選的庫(kù)存。
- 步驟4:然后訂單服務(wù)將支付指令發(fā)送到支付服務(wù)。支付服務(wù)會(huì)扇出到3個(gè)服務(wù):支付渠道、通知和分析。
- 步驟5.1和6.1:支付服務(wù)將支付指令發(fā)送到支付渠道服務(wù)。支付渠道服務(wù)與外部PSP(支付服務(wù)提供商)進(jìn)行通信,以完成交易。
- 步驟5.2和6.2:支付服務(wù)向通知服務(wù)發(fā)送通知,通知服務(wù)然后通過(guò)電子郵件或短信向顧客發(fā)送通知。
- 步驟5.3:支付服務(wù)向分析服務(wù)發(fā)送交易詳細(xì)信息。
圖2 一個(gè)簡(jiǎn)化的電子商務(wù)限時(shí)搶購(gòu)架構(gòu)
這里的一個(gè)關(guān)鍵點(diǎn)是,在限時(shí)搶購(gòu)活動(dòng)中,無(wú)縫的用戶體驗(yàn)至關(guān)重要。為了在高流量情況下保持服務(wù)的響應(yīng)能力,可以在多個(gè)階段集成消息隊(duì)列,以確保性能最佳。
消息隊(duì)列的優(yōu)勢(shì) 扇出
支付服務(wù)將數(shù)據(jù)發(fā)送到三個(gè)下游服務(wù),用于不同的目的:支付渠道、通知和分析。這種扇出方法就像有人在房間里大聲喊話;誰(shuí)需要聽(tīng)到,就聽(tīng)到了。生產(chǎn)者只需將消息放在隊(duì)列中,而消費(fèi)者可以按照自己的節(jié)奏處理消息。
(1) 異步處理
借用星巴克的類比,就像收銀員不必等待咖啡制作完成一樣,訂單服務(wù)也不必等待支付完成。支付指令被放置在隊(duì)列中,一旦完成,顧客就會(huì)收到通知。
(2) 速率限制
在限時(shí)搶購(gòu)活動(dòng)中,可能會(huì)有數(shù)以萬(wàn)計(jì)的并發(fā)用戶同時(shí)下訂單。在滿足渴望購(gòu)買的顧客和保持系統(tǒng)穩(wěn)定之間取得平衡非常重要。一種常見(jiàn)的方法是在特定的時(shí)間范圍內(nèi)限制進(jìn)入的請(qǐng)求數(shù)量,以匹配系統(tǒng)的容量。多余的請(qǐng)求可能會(huì)被拒絕或要求在短時(shí)間延遲后重試。這種方法確保系統(tǒng)保持穩(wěn)定,不會(huì)被壓垮。對(duì)于成功通過(guò)的請(qǐng)求,消息隊(duì)列確保它們被高效有序地處理。如果系統(tǒng)的某個(gè)部分暫時(shí)滯后,訂單不會(huì)丟失。它會(huì)在隊(duì)列中保持,直到可以處理為止。這確保了即使在壓力下也能保持流暢的流程。
(3) 解耦
我們的設(shè)計(jì)在多個(gè)地方使用了消息隊(duì)列。總體架構(gòu)與圖2中呈現(xiàn)的簡(jiǎn)化版本不同。服務(wù)之間通過(guò)定義良好的消息接口進(jìn)行交互,而不是緊密依賴彼此。每個(gè)服務(wù)都可以獨(dú)立進(jìn)行修改和部署。每個(gè)組件可以使用不同的編程語(yǔ)言進(jìn)行開發(fā)。這為架構(gòu)設(shè)計(jì)帶來(lái)了靈活性。
(4) 橫向擴(kuò)展
由于服務(wù)被解耦,我們可以根據(jù)需求獨(dú)立地進(jìn)行擴(kuò)展。每個(gè)服務(wù)可以在不同的能力范圍內(nèi)提供服務(wù),因此我們可以根據(jù)其計(jì)劃的每秒查詢數(shù)(QPS)或每秒事務(wù)數(shù)(TPS)進(jìn)行擴(kuò)展。
(5) 消息持久性
消息隊(duì)列還可以用作存儲(chǔ)消息的中間件。如果上游服務(wù)崩潰,下游服務(wù)始終可以從消息隊(duì)列中獲取消息進(jìn)行處理。通過(guò)這種方式,恢復(fù)功能從每個(gè)服務(wù)中移出,并成為消息隊(duì)列的責(zé)任。
(6) 批處理
在處理流程中,有時(shí)我們需要對(duì)數(shù)據(jù)進(jìn)行批處理以獲得摘要。例如,當(dāng)支付服務(wù)向分析服務(wù)發(fā)送更新時(shí),分析服務(wù)不需要執(zhí)行實(shí)時(shí)更新,而是設(shè)置一個(gè)滾動(dòng)窗口以批處理處理。批處理是下游服務(wù)的要求,因此支付服務(wù)不需要知道它,只需將消息放入隊(duì)列中。
(7) 消息排序
在限時(shí)搶購(gòu)中,庫(kù)存數(shù)量有限。例如,限時(shí)搶購(gòu)只提供10部iPhone,但有超過(guò)10,000名下訂單的用戶。我們?nèi)绾螞Q定訂單的順序呢?通過(guò)使用消息隊(duì)列來(lái)保留所有訂單,將會(huì)自然形成一個(gè)順序:隊(duì)列中的前10個(gè)訂單將獲得iPhone。
在圖3中,我們將所有內(nèi)容整合在一起,服務(wù)通過(guò)消息隊(duì)列連接并解耦。這樣,架構(gòu)可以實(shí)現(xiàn)更高的吞吐量。
圖3 在限時(shí)搶購(gòu)架構(gòu)中使用消息隊(duì)列