目錄
熟悉訊息中介軟體的同學應該對釋出/訂閱模式(Publish Subscribe Pattern)並不陌生。即使你不瞭解訊息中介軟體,那麼在平時生活中釋出/訂閱模式也是非常常見的場景。
比如你開啟你的微信訂閱號,你訂閱的作者釋出的文章,會廣播給每個訂閱者。在這個場景裡,微信公眾號就是一個Pulisher,而你就是一個Subscriber,你收到的文章就是一個Message。
下面我們就一起了解一下釋出/訂閱模式
,如果你要了解並在自己的專案中使用這個模式,或者你對訊息佇列(MQ)等中介軟體的原理感興趣,那麼這個系列的文章就是最高效地深入淺出寶典。
模式介紹
釋出/訂閱模式(Publish Subscribe Pattern)屬於設計模式中的行為(Behavioral Patterns)。
在軟體架構中,釋出/訂閱是一種訊息正規化,訊息的傳送者(稱為釋出者)不會將訊息直接傳送給特定的接收者(稱為訂閱者),而是通過訊息通道廣播出去,讓訂閱改訊息主題的訂閱者消費到。
釋出/訂閱者模式
最大的特點就是實現了鬆耦合,也就是說你可以讓釋出者釋出訊息、訂閱者接受訊息,而不是尋找一種方式把兩個分離的系統連線在一起。當然這種鬆耦合也是釋出/訂閱者模式
最大的缺點,因為需要中間的代理,增加了系統的複雜度。而且釋出者無法實時知道釋出的訊息是否被每個訂閱者接收到了,增加了系統的不確定性。
釋出/訂閱者模式
的優點
釋出/訂閱者模式
的優點可以歸納為:
- 鬆耦合/Independence
釋出/訂閱者模式
可以將眾多需要通訊的子系統(Subsystem)解耦,每個子系統都可以獨立管理。而且即使部分子系統下線了,也不會影響系統訊息的整體管理。
釋出/訂閱者模式
為應用程式提供了關注點分離。每個應用程式都可以專注於其核心功能,而訊息傳遞基礎結構負責將訊息路由到每個消費者手裡。
- 高伸縮性/Scalability
釋出/訂閱者模式
增加了系統的可伸縮性,並提高了傳送者的響應能力。原因是傳送方(Publisher)可以快速地向輸入通道傳送一條訊息,然後返回到其核心處理職責,而不必等待子系統處理完成。然後訊息傳遞的基礎結構負責確保把訊息傳遞到每個訂閱者(Subscriber)手裡。
- 高可靠性/Reliability
釋出/訂閱者模式
提高了可靠性。非同步的訊息傳遞有助於應用程式在增加的負載下繼續平穩執行,並且可以更有效地處理間歇性故障。
- 靈活性/Flexibility
你不需要關心不同的元件是如何組合在一起的,只要他們共同遵守一份協議即可。
釋出/訂閱者模式
允許延遲處理或者按計劃的處理。例如當系統負載大的時候,訂閱者可以等到非高峰時間才接收訊息,或者根據特定的計劃處理訊息。
- 可測試性/Testability
釋出/訂閱者模式
提高了可測試性。通道可以被監視,訊息可以作為整體整合測試策略的一部分而被檢查或記錄。
實現釋出/訂閱者模式
需要考慮的點
- 訂閱處理
訂閱者可以在訊息通道中訂閱或者取消訂閱某個話題。
- 安全
連線到任何訊息通道必須受到安全策略的限制,以防止未經授權的使用者或應用程式竊聽。
- 內容篩選
根據每條訊息的內容檢查和分發訊息。每個訂戶都可以指定其感興趣的內容。
訂閱者通常只對釋出者分發的訊息的子集感興趣。訊息服務通常允許訂戶縮小以下使用者接收到的訊息集。
考慮允許訂戶通過萬用字元訂閱多個主題。每個主題都有一個專用的輸出通道,每個使用者都可以訂閱所有相關主題。
- 雙向通訊
釋出訂閱系統中的通道被視為單向的。
如果特定訂戶需要向釋出伺服器傳送確認或通訊狀態,請考慮使用請求/回覆模式。此模式使用一個通道向訂閱伺服器傳送訊息,以及一個單獨的回覆通道向釋出伺服器進行通訊。
- 訊息排序
使用者例項接收訊息的順序不一定得到保證,也不一定反映訊息的建立順序。
設計該系統以確保訊息處理是等量的,以幫助消除對訊息處理順序的任何依賴。
- 訊息優先順序
有些解決方案可能需要按特定順序處理訊息。優先順序佇列模式提供了一種確保特定訊息先於其他訊息傳遞的機制。
- 有毒資訊
格式錯誤的訊息或需要訪問不可用資源的任務可能會導致服務例項失敗。系統應防止此類訊息返回到佇列,否則可能導致系統故障。
- 訊息重複
同一訊息可能會傳送多次。例如,傳送者可能在釋出訊息後出現了異常,沒有記錄自己已經成功傳送了訊息,然後,傳送者的新例項可能會啟動並重復該訊息。
訊息基礎結構應基於訊息ID實現重複訊息檢測和刪除(也稱為重複資料消除),以便最多提供一次訊息傳遞。
- 訊息過期
訊息的生命週期可能有限。如果在這段時間內沒有處理,它可能不再有價值,應該丟棄。傳送方可以指定過期時間作為訊息中資料的一部分。在決定是否執行與訊息關聯的業務邏輯之前,接收者可以檢查此資訊,以確保訊息沒有過期。
- 訊息排程
例如,訊息可能會被暫時禁止,直到特定的日期和時間才被處理。
何時應使用釋出/訂閱者模式
如果你的程式只有很少的訂閱者,或者需要與子系統進行實時的互動,那麼釋出/訂閱者模式
是不適合的。
在以下情況下可以考慮使用此模式:
-
應用程式需要向大量消費者廣播資訊。例如微信訂閱號就是一個消費者量龐大的廣播平臺。
-
應用程式需要與一個或多個獨立開發的應用程式或服務通訊,這些應用程式或服務可能使用不同的平臺、程式語言和通訊協議。
-
應用程式可以向消費者傳送資訊,而不需要消費者的實時響應。
-
被整合的系統被設計為支援其資料的最終一致性模型。
-
應用程式需要將資訊傳遞給多個消費者,這些消費者可能具有與傳送者不同的可用性要求或正常執行時間計劃。例如你訊息在上午釋出了出去,消費者計劃在下午才去處理這些訊息。
釋出/訂閱者模式
與觀察者模式
釋出/訂閱者模式
與觀察者模式
是我們經常混淆的兩種設計模式,可以說兩種設計模式在行為上有一定的相似性,但卻是兩種不同的設計模式。或者說釋出/訂閱者模式
是觀察者模式
的一種變體。
通過下圖可以清晰地看到兩種設計模式的不同點。
釋出/訂閱者模式
與觀察者模式
主要有以下幾個不同點:
-
在觀察者模式中,主體維護觀察者列表,因此主體知道當狀態發生變化時如何通知觀察者。然而,在釋出者/訂閱者中,釋出者和訂閱者不需要相互瞭解。它們只需在中間層訊息代理(或訊息佇列)的幫助下進行通訊。
-
在釋出者/訂閱者模式中,元件與觀察者模式完全分離。在觀察者模式中,主題和觀察者鬆散耦合。
-
觀察者模式主要是以同步方式實現的,即當發生某些事件時,主題呼叫其所有觀察者的適當方法。釋出伺服器/訂閱伺服器模式主要以非同步方式實現(使用訊息佇列)。
-
釋出者/訂閱者模式更像是一種跨應用程式模式。釋出伺服器和訂閱伺服器可以駐留在兩個不同的應用程式中。它們中的每一個都通過訊息代理或訊息佇列進行通訊。
本文介紹了釋出者/訂閱者模式
的相關概念,後面幾篇會詳細介紹具體實現。