发布-订阅模式

在軟體架構中，**發布 - 訂閱**是一種訊息規範，訊息的傳送者（稱為發布者）不會將訊息直接傳送給特定的接收者（稱為訂閱者）。而是將發布的訊息分為不同的類別，無需了解哪些訂閱者（如果有的話）可能存在。同樣的，訂閱者可以表達對一個或多個類別的興趣，只接收感興趣的訊息，無需了解哪些發布者（如果有的話）存在。

發布 / 訂閱是訊息佇列規範的兄弟，通常是更大的訊息導向中介軟體系統的一部分。大多數訊息系統在 API 中同時支援訊息佇列模型和發布 / 訂閱模型，例如 Java 訊息服務（JMS）。

這種模式提供了更大的網路可延伸性和更動態的網路拓撲，同時也降低了對發布者和發布資料的結構修改的靈活性。

## 訊息過濾

在發布 / 訂閱模型中，訂閱者通常接收所有發布的訊息的一個子集。選擇接受和處理的訊息的過程被稱作過濾。有兩種常用的過濾形式：基於主題的和基於內容的。

在**基於主題**的系統中，訊息被發布到主題或命名通道上。訂閱者將收到其訂閱的主題上的所有訊息，並且所有訂閱同一主題的訂閱者將接收到同樣的訊息。發布者負責定義訂閱者所訂閱的訊息類別。

在**基於內容**的系統中，訂閱者定義其感興趣的訊息的條件，只有當訊息的屬性或內容滿足訂閱者定義的條件時，訊息才會被投遞到該訂閱者。發布者需要負責對訊息進行分類。

一些系統支援兩者的混合：發布者發布訊息到主題上，而訂閱者將基於內容的訂閱註冊到一個或多個主題上。

## 拓撲

在許多發布 / 訂閱系統中，發布者發布訊息到一個中間的訊息代理，然後訂閱者向該訊息代理註冊訂閱，由訊息代理來進行過濾。訊息代理通常執行儲存轉發的功能將訊息從發布者傳送到訂閱者。

## 歷史

最早公開描述發布 / 訂閱系統之一的是 Isis Toolkit 的「新聞」子系統，1987 年，在電腦協會 (ACM) 的作業系統原理的研討會上，, 在論文《在分散式系統中利用虛同步》中。該文描述的發布 / 訂閱技術是由 Frank Schmuck 發明的。

## 優點

### 鬆耦合

　
: 發布者與訂閱者鬆耦合，甚至不需要知道它們的存在。由於主題才是關注的焦點，發布者和訂閱者可以對系統拓撲結構保持一無所知。各自繼續正常操作而無需顧及對方。在傳統的緊耦合的客戶端 - 伺服器模式中，當伺服器行程不執行時，客戶端無法傳送訊息給伺服器，伺服器也無法在客戶端不執行時接收訊息。許多發布 / 訂閱系統不但將發布者和訂閱者從位置上解耦，還從時間上解耦他們。中介軟體分析師對這種發布 / 訂閱使用的常用策略，是拆卸一個發布者來讓訂閱者處理完積壓的工作（頻寬限制的一種形式）。

### 可延伸性

　
: 通過並列操作，訊息快取，基於樹或基於網路的路由等技術，發布 / 訂閱提供了比傳統的客戶端–伺服器更好的可延伸性。然而，在某些類型的緊耦合、高容量的企業環境中，隨著系統規模上升到由上千台伺服器組成的資料中心所共享的發布 / 訂閱基礎架構，現有的供應商系統經常失去這項好處；在這些高負載環境下，發布 / 訂閱產品的擴充性是一個研究課題。

　
: 另一方面，在企業環境之外，發布 / 訂閱規範已經證明了它的可延伸性遠超過一個單一的資料中心，通過網路聚合協定如 RSS 和 Atom 提供網際網路範圍內分發的訊息。在互動時，為了能夠即便是用低檔 Web 伺服器也能將訊息播出到 (可能) 數以百萬計的獨立使用者節點，這些聚合協定接受更高的延遲和無保障交付。

## 缺點

發布 / 訂閱系統最嚴重的問題是其主要優點的副作用：發布者解耦訂閱者。

### 訊息交付問題

發布 / 訂閱系統必須仔細設計，才能提供特定的應用程式可能需要的更強大的系統效能，例如有保障的交付。

* 發布 / 訂閱系統的中介 (broker) 可能設計為在指定時間傳送訊息，隨後便停止嘗試傳送，無論是否已收到所有使用者成功接收訊息的確認回覆。這樣設計的發布 / 訂閱系統不能保證訊息能夠傳遞到所有需要這種有保障交付的應用程式。要達成有保障交付，必須在發布 / 訂閱架構之外強制執行這種發布者和訂閱者之間在設計上更緊密的耦合（例如，通過要求訂閱者宣布訊息已接收）。
* 發布 / 訂閱系統中的發布者會「假定」訂閱者正在監聽，而實際上可能沒有。一個工廠可能會使用發布 / 訂閱系統來允許裝置發布問題和故障，訂閱者將問題顯示並記錄。如果記錄器失敗（崩潰了），那麼裝置故障發布者不一定收到記錄器失敗的通知，發布 / 訂閱系統的任何裝置都不會顯示和記錄錯誤訊息。應當指出的是，對於其它訊息架構這也是一個設計上的挑戰，例如客戶端 / 伺服器系統。在客戶端 / 伺服器系統中，當一個錯誤記錄器失效，系統將收到跡象。但是，客戶端 / 伺服器系統要處理這個失效，就必須擁有一個線上的冗餘紀錄檔伺服器，或者動態生成回退紀錄檔伺服器。這就增加了伺服器端和客戶端以及整個客戶端 / 伺服器架構設計的複雜度。然而，發布 / 訂閱系統中，在不影響任何其它裝置的情況下，精確複製現有紀錄檔器的冗餘紀錄檔記錄訂閱者可以添加到系統，來增加紀錄檔記錄的可靠性。在發布 / 訂閱系統中，有保障的錯誤訊息紀錄檔功能可以逐步添加，隨後實現裝置故障資訊記錄的簡單基本功能。

在有少量發布者和訂閱節點的小型網路和低資訊量時發布 / 訂閱能夠自如伸縮。然而，隨著節點和訊息量的增長，不穩定性隨之增長，限制了發布 / 訂閱網路的最大可延伸性。大規模時吞吐量不穩定的例子包括：

* 負載激增 - 訂閱請求使網路流量飽和，隨後進入低資訊量（未充分利用網路頻寬）
* 速度變慢 - 越來越多的應用程式使用該系統（即使它們是在不同的發布 / 訂閱頻道通信）訊息量流入單個訂閱者的速度緩慢

使用中介（伺服器）的發布 / 訂閱系統，同意中介傳送訊息給帶內訂閱者，會引發安全問題。中介可能被愚弄，從而將通知傳送給錯誤的客戶端，增大了針對客戶端的服務請求被拒絕的可能性。中介本身可能超載，因為他們分配資源來跟蹤建立的訂閱。

即使不依賴中介的系統，訂閱者也可能可以接收未被授權的資料。未經授權的發布者可能將不正確或損壞的訊息引入到發布 / 訂閱系統。對於廣播或多播訊息的系統，這是尤其真實的。加密（例如傳輸層安全性協定（SSL/TLS）），可以防止未經授權的存取，但不能防止損壞的訊息被授權的發布者引入。除了發布 / 訂閱架構，例如客戶端 / 伺服器系統，也經常碰到授權的訊息傳送者有惡意行為。

## 注釋

## 參見

* 事件驅動程式設計
* 觀察者模式
* 資料分布服務（DDS）
* 推技術（推播技術）
* Usenet

## 外部連結

* XMPP XEP-0060: Publish-Subscribe Protocol（頁面存檔備份，存於網際網路檔案館）
* For an open source example which is in production on MSN.com and Microsoft.com, see Distributed Publish/Subscribe Event System（頁面存檔備份，存於網際網路檔案館）
* A REconfigurable Dispatching System (REDS)
* PyPubSub a Python Publish-Subscribe broker for messages \*within\* an application (NOT network)
* Another open source example written in python: see Python Message Service（頁面存檔備份，存於網際網路檔案館）
* A Ruby open source example: Faye