发布/订阅

在软件架构中，发布/订阅（Publish–subscribe pattern）是一种消息范式，消息的发送者（称为发布者）不会将消息直接发送给特定的接收者（称为订阅者）。而是将发布的消息分为不同的类别，无需了解哪些订阅者（如果有的话）可能存在。同样的，订阅者可以表达对一个或多个类别的兴趣，只接收感兴趣的消息，无需了解哪些发布者（如果有的话）存在。

发布/订阅是消息队列范式的兄弟，通常是更大的系统的一部分。大多数消息系统在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

Category:软件设计模式
Category:中间件