本文將說明內部部署的訊息佇列驅動架構,與在 Pub/Sub 上實作的雲端事件驅動架構之間的差異。如果嘗試將內部部署模式直接套用至雲端技術,可能會錯失讓雲端技術具備吸引力的獨特價值。
本文件適用於將設計從內部架構遷移至雲端架構的系統架構師。本文假設您對訊息系統有初步的瞭解。
下圖概略說明訊息佇列模型和 Pub/Sub 模型。
在上圖中,我們比較了訊息佇列模型和 Pub/Sub 事件串流模型。在訊息佇列模型中,發布者會將訊息推送至佇列,每位訂閱者都可以聆聽特定佇列。在使用 Pub/Sub 的事件串流模型中,發布端會將訊息推送至多個訂閱端可聽取的主題。我們將在以下各節說明這些模型之間的差異。
比較事件串流和佇列式訊息
如果您使用的是內部部署系統,那麼您應該已經熟悉企業服務總線 (ESB) 和訊息佇列。事件串流是一種全新的模式,與現代即時系統的具體優勢有重大差異。
本文件將討論事件驅動架構中傳輸機制和酬載資料的主要差異。
訊息傳輸
在這些模型中移動資料的系統稱為訊息中介服務,其中實作了各種架構。其中一個基本概念是,將訊息從發布者傳送至接收者的基礎機制。在內部訊息架構中,原始系統會使用訊息佇列做為傳輸工具,向下游處理系統發出明確、遠端、解耦的訊息。
下圖顯示訊息佇列模型:
在上圖中,訊息會透過訊息佇列從上游發布者程序流向下游訂閱者程序。
系統 A (發布端) 會將訊息傳送至訊息中介軟體的佇列,該佇列專門用於系統 B (訂閱端)。雖然佇列的訂閱者可以包含多個用戶端,但這些用戶端都是為了擴充和可用性而部署的系統 B 重複執行個體。如果其他下游程序 (例如系統 C) 需要從產生者 (系統 A) 取用相同的訊息,就需要建立新的佇列。您需要更新產生器,才能將訊息發布至新佇列。這個模型通常稱為「訊息傳遞」。
這些佇列的訊息傳輸層可能會提供訊息順序保證,也可能不會。通常,訊息佇列會提供有序保證模型,並以嚴格的先進先出 (FIFO) 存取模型排序資料,類似於工作佇列。這種模式一開始很容易實作,但最終會出現資源調度和作業方面的問題。如要實作有序訊息,系統需要集中式程序來整理資料。這個程序會限制擴充功能,並降低服務可用性,因為這是單一故障點。
這些架構中的訊息中介軟體通常會實作額外邏輯,例如追蹤哪些訂閱者收到哪些訊息,以及監控訂閱者負載。訂閱者通常只會回應,不瞭解整體系統,只會在收到訊息時執行函式。這類架構稱為「智慧管道」(訊息佇列系統) 和「無腦端點」 (訂閱者)。
Pub/Sub 傳輸
與以訊息為導向的系統類似,事件串流系統也會將訊息從來源系統傳送至解耦的目的地系統。不過,事件導向系統通常會將訊息發布至共用主題,而不是將每則訊息傳送至以程序為目標的佇列,然後一或多個接收器訂閱該主題,以便監聽相關訊息。
下圖顯示上游發布端如何將各種訊息傳送至單一主題,然後將其路由至相關的下游訂閱端:
這就是「pub/sub」 一詞的由來。這也是 Google Cloud Pub/Sub 產品的基礎。在本文件中,「pubsub」是指模式,「Pub/Sub」則是指產品。
在 pubsub 模型中,訊息系統不需要瞭解任何訂閱者。它不會追蹤已收到的訊息,也不會管理使用程序的負載。相反地,訂閱者會追蹤已收到哪些訊息,並負責自行管理負載層級和調整。
其中一個重大優點是,如果您在 pubsub 模型中發現資料有新用途,就不需要更新原始系統,以便發布至新佇列或複製資料。您可以將新消費者連結至新訂閱項目,而不會影響現有系統。
事件串流系統中的呼叫幾乎都是非同步的,也就是說,這些呼叫會傳送事件,但不會等待任何回應。非同步事件可讓生產者和消費者有更多擴充選項。不過,如果您希望能保證 FIFO 訊息順序,這種非同步模式可能會造成挑戰。
訊息佇列資料
在訊息佇列系統和 Pub/Sub 系統中,系統之間傳遞的資料通常會在兩種情況下稱為「訊息」。不過,呈現資料的模型不同。在訊息佇列系統中,訊息會反映指令,該指令旨在變更下游資料的狀態。如果您查看內部部署訊息佇列系統的資料,發布者可能會明確指出消費者應採取的動作。舉例來說,商品目錄訊息可能會指出下列事項:
<m:SetInventoryLevel>
<inventoryValue>3001</inventoryValue>
</m: SetInventoryLevel>
在這個範例中,生產者會告知消費者,需要將商品目錄層級設為 3001。這種做法可能會很困難,因為產生器需要瞭解每個使用者的商業邏輯,並為不同的用途建立個別的訊息結構。這套訊息佇列系統是大多數企業實作大型單體架構的常見做法。不過,如果您想加快速度、擴大規模,並進行更多創新,這些集中式系統可能會成為瓶頸,因為變更的風險和速度都很高。
這種模式也會帶來營運上的挑戰。當發生錯誤資料、重複記錄或其他問題,需要進行修正時,這種訊息傳遞模型就會帶來重大挑戰。舉例來說,如果您需要回復先前範例中使用的訊息,由於沒有先前的狀態參照,因此不知道要將修正值設為何值。您無法得知在傳送該訊息之前,廣告空間價值是 3000 還是 4000。
Pub/Sub 資料
事件是另一種傳送訊息資料的方式。特別的是,事件驅動系統著重於發生的事件,而非應發生的結果。這類資料不會傳送指出消費者應採取哪些動作的資料,而是著重於實際產生的事件詳細資料。您可以在各種平台上實作事件驅動系統,但這類系統通常會出現在以 pubsub 為基礎的系統中。
舉例來說,商品目錄事件可能會像這樣:
{ "inventory":-1 }
先前的事件資料指出,發生了某個事件,導致廣告空間減少 1。這些訊息著重於過去發生的事件,而非日後要變更的狀態。發布者可以以非同步方式傳送訊息,因此事件導向系統比訊息佇列模型更容易擴充。在發布/訂閱模型中,您可以將業務邏輯分離,讓生產端只需瞭解對其執行的動作,而不需要瞭解下游程序。資料訂閱者可以選擇如何處理收到的資料。由於這些訊息並非必要指令,因此訊息的順序就沒那麼重要。
使用這種模式,您就能更輕鬆地復原變更。在本範例中,您可以將商品目錄值設為負值,以便將其移至相反方向,因此不需要額外資訊。您也不必再擔心訊息延遲或順序錯亂的問題。
模式比較
在這個情境中,你的商品目錄中含有四件相同產品。一位顧客退回一項產品,下一位顧客購買了三項相同產品。在這個情境中,假設退回產品的訊息延遲傳送。
下表比較了在正確順序中接收商品數量的訊息佇列模型,與接收商品數量順序不正確的相同模型的商品層級:
訊息佇列 (正確順序) | 訊息佇列 (順序錯亂) |
---|---|
初始商品目錄:4 |
初始商品目錄:4 |
訊息 1:setInventory(5) |
訊息 2:setInventory(2) |
訊息 2:setInventory(2) |
訊息 1:setInventory(5) |
廣告空間層級: 2 |
廣告空間層級:5 |
在訊息佇列模型中,訊息的接收順序非常重要,因為訊息包含預先計算的值。在這個範例中,如果訊息依正確順序傳送,則廣告空間等級為 2。不過,如果訊息傳送順序不正確,則商品目錄等級為 5,這不正確。
下表比較了以 pubsub 為基礎的系統,在正確順序接收廣告空間計數的廣告空間層級,以及同一個系統在錯誤順序接收廣告空間計數的廣告空間層級:
Pubsub (正確順序) | Pubsub (順序錯誤) |
---|---|
初始商品目錄:4 | 初始商品目錄:4 |
訊息 2:"inventory":-3 |
訊息 1:"inventory":+1 |
訊息 1:"inventory":+1 |
訊息 2:"inventory":-3 |
廣告空間層級:2 |
廣告空間層級:2 |
在以 Pub/Sub 為基礎的系統中,訊息的順序並不重要,因為系統會根據產生事件的服務來通知訊息。無論訊息傳送的順序為何,廣告空間層級都會正確。
下圖顯示在訊息佇列模型中,佇列如何執行指令,告知訂閱端應如何變更狀態;而在 pubsub 模型中,訂閱端會回應事件資料,說明發布端發生了什麼事:
導入事件導向架構
實作事件驅動架構時,需要考量各種概念。以下各節將介紹其中幾個主題。
關於通知訊息的注意事項
在系統討論中,有一個概念是訊息傳送保證的可靠性。不同供應商和系統提供的可靠度可能不同,因此請務必瞭解這些差異。
第一類保證會提出一個簡單的問題:如果傳送訊息,是否保證會送達?這就是所謂的「至少一次」傳送。系統保證至少會傳送一次訊息,但可能會傳送多次。
另一種保證類型是「最多傳送一次」。使用「最多一次」傳送模式時,系統只會傳送一次訊息,但無法保證訊息會實際傳送。
最終的放送保證變化版本是「確切一次放送」。在這個模型中,系統會傳送一封且只有一封郵件,且保證會送達。
訂單和重複項目
在內部部署架構中,訊息通常會遵循先進先出 (FIFO) 模式。為了實現這個模型,集中處理系統會管理訊息的排序,確保正確的排序。有序訊息會造成挑戰,因為對於任何失敗的訊息,都必須依序重新傳送所有訊息。任何集中式系統都可能會影響可用性和可擴充性。如要擴充管理訂單的中央系統,通常只能在現有機器上新增更多資源。當單一系統管理訂單時,任何可靠性問題都會影響整個系統,而非僅限於該機器。
可高度擴充且可用性高的訊息服務通常會使用多個處理系統,確保至少傳送一次訊息。在許多系統中,我們無法保證訊息的順序。
以事件為核心的架構不依賴訊息順序,因此可以容許重複訊息。如果需要排序,子系統可以實作匯總和視窗技術;不過,這種做法會犧牲該元件的可擴充性和可用性。
篩選和分支技巧
由於事件串流可能包含每個訂閱者可能或不需的資料,因此通常需要限制特定訂閱者收到的資料。管理這項需求有兩種模式:事件篩選器和事件分散。
下圖顯示事件驅動系統,其中事件篩選器會為訂閱者篩選訊息:
在上圖中,事件篩選器會使用篩選機制,限制傳送至訂閱者的事件。在這個模型中,單一主題包含訊息的所有變化版本。訊息系統中的篩選邏輯會評估訊息,而非由訂閱者讀取每則訊息並驗證是否適用。如果訊息不符合條件,系統就會將其從其他訂閱者中移除。
下圖顯示事件篩選器模式的變化版本,稱為事件分支,可使用多個主題:
在上圖中,主要主題包含訊息的所有變化版本,但事件分支機制會重新發布與該子集訂閱者相關的主題訊息。
未處理的訊息佇列
即使是最佳系統,也可能發生故障。未處理的訊息佇列是處理這類失敗情況的一種方法。在大多數事件驅動架構中,訊息系統會持續向訂閱者提供訊息,直到訂閱者確認為止。
如果訊息發生問題 (例如訊息內文含有無效字元),訂閱者可能無法確認訊息。系統可能無法處理情境,甚至會終止程序。
系統通常會重試未確認或發生錯誤的訊息。如果無效訊息在預定時間內未收到確認,就會逾時,並從主題中移除。從作業角度來看,最好是查看訊息,而不是讓訊息消失。這時,未處理訊息佇列就派上用場。系統不會從主題中移除訊息,而是將訊息移至其他主題,以便重新處理或查看訊息,瞭解訊息為何發生錯誤。
串流記錄和重播
事件串流是持續流動的資料。存取這類歷來資料很有用。您可能想瞭解系統如何達到特定狀態。您可能會遇到需要稽核資料的安全性相關問題。在事件驅動系統的長期運作中,記錄事件的歷史記錄至關重要。
歷史事件資料的常見用途之一,就是與重播系統搭配使用。重播功能僅供測試之用。您可以在其他環境 (例如階段和測試) 中重播實際工作環境的事件資料,藉此驗證新功能是否符合實際資料集。您也可以重播歷來資料,從失敗狀態復原。如果系統發生故障或資料遺失,團隊可以從已知的良好時間點重播事件記錄,服務就能重新建構遺失的狀態。
當訂閱者需要在不同時間存取一系列事件時,在以記錄為基礎的佇列或記錄串流中擷取這些事件也很實用。在具備離線功能的系統中,您可以查看記錄串流。您可以使用串流記錄,從「上次讀取」指標開始讀取串流,藉此處理最新的項目。
資料檢視:即時和近乎即時
所有資料都會透過系統傳送,因此您必須能夠使用資料。您可以透過多種方式存取及使用這些事件串流,但常見的用途是瞭解特定時刻的資料整體狀態。這些通常是計算導向的問題,例如「有多少」或「目前等級」,可供其他系統或人類使用。以下是可回答這些問題的多種實作方式:
- 即時系統可以持續運作,並追蹤目前狀態;不過,由於系統僅有記憶體內計算,因此任何停機時間都會將計算設為零。
- 系統可以為每項要求計算歷史資料表中的值,但這可能會造成問題,因為在資料量增加的情況下,嘗試為每項要求計算值最終可能會變得不可行。
- 系統可在特定間隔時間內建立計算作業的快照,但僅使用快照無法反映即時資料。
實用的實作模式是Lambda 架構,可同時提供近乎即時和即時功能。舉例來說,電子商務網站上的產品頁面可以使用近乎即時的庫存資料檢視畫面。當消費者下單時,系統會使用即時服務,確保庫存資料的狀態能即時更新。為實作此模式,服務會回應快照資料表的近乎即時要求,該資料表包含特定間隔的計算值。即時要求會同時使用快照資料表和記錄資料表中的值,自上次快照起,以取得確切的目前狀態。這些事件串流的具體檢視畫面可提供可供採取行動的資料,以推動實際的業務流程。
後續步驟
- 請參閱這篇文章,瞭解如何使用 Pub/Sub 或 Cloud Tasks 傳遞訊息及執行非同步整合作業。
- 請試試 Pub/Sub 快速入門導覽課程。
- 請參閱 Pub/Sub 架構總覽
- 探索 Google Cloud 的參考架構、圖表和最佳做法。歡迎瀏覽我們的雲端架構中心。