发布/订阅(Pub/Sub 的简称)是一种消息传递技术,它促进分布式系统中不同组件之间的通信。这种通信模式与传统的点对点消息传递不同,后者是一个应用直接向另一个应用发送消息。相反,它是一种异步且可扩展的消息传递服务,它将负责生成消息的服务与负责处理消息的服务分离开来。
Redis Streams 还是 Redis Pub/Sub 是您下一个应用的最佳选择? Redis Streams 和 Pub/Sub 入门
发布/订阅 是一种消息传递模型,它允许分布式系统中的不同组件相互通信。发布者将消息发送到一个主题,订阅者则从该主题接收消息,这使得发布者可以在匿名的情况下向订阅者发送消息,当然如果他们在消息负载中包含识别信息,订阅者也可以识别他们。发布/订阅 系统确保消息到达所有对该主题感兴趣的订阅者。如果配置得当,它是一个高度可扩展且可靠的消息传递系统,可以处理大量数据。此外,在适当的消息大小、网络条件和订阅者处理时间下,发布/订阅 允许服务以 1 毫秒的延迟异步通信,这使得它对于快速且现代的分布式应用非常理想。
发布/订阅 本质上是一个简单的通信模型,其中一个 消息代理 接收来自发布者的消息,并将其分发给一个或多个订阅者。然后,消息被传递给订阅者,订阅者根据其特定用例的需求解释这些消息。
根据通信中涉及的发布者和订阅者的数量,它们通常被分为四种模型,包括一对一(one-to-one)、一对多(one-to-many)、多对一(many-to-one)和多对多(many-to-many)。
发布/订阅 模型类型 | 描述 |
一对一(One-to-one) | 该模型包含一个发布者和一个订阅者。直接消息从发布者发送给订阅者。 |
一对多(One-to-many) | 该模型包含一个发布者和多个订阅者。发布者将消息发送到主题;所有感兴趣的订阅者都会收到该消息。 |
多对一(Many-to-one) | 该模型包含多个发布者和一个订阅者。订阅者从多个发布者那里接收特定主题的消息。 |
多对多(Many-to-many) | 该模型包含多个发布者和订阅者。发布者将消息发送到一个主题,所有订阅者都会收到该消息。 |
发布/订阅 系统由多个组件组成;下面表格描述了一些主要组件
组件 | 描述 |
1. 发布者 | 发布者是发送消息的应用或服务。 |
2. 订阅者 | 订阅者是接收消息的应用或服务。 |
3. 主题 | 主题是消息的主题或信息流。发布者可以将消息推送到主题,该主题会将消息广播给订阅者。 |
4. 消息 | 消息包含系统中接收或传输的数据。 |
5. 消息代理 | 消息代理 负责在整个系统中引导消息。它充当中间人,建立发布者和订阅者之间的通信并进行交换。它可以维护主题及其各自订阅者的列表,这有助于它将从发布者接收到的消息路由发送给适当的订阅者。 |
6. 路由 | 路由 是消息在系统中从发布者流向订阅者的过程,并根据特定订阅确保消息被传递给正确的订阅者。 |
发布/订阅 提供的异步集成增强了系统的整体灵活性和鲁棒性,从而支持多种用例,包括
发布/订阅 服务可大致分为云端、自托管和实时服务。
云端发布/订阅 服务提供完全由云提供商(如 Google Cloud、AWS 和 Microsoft Azure)管理的消息传递基础设施。云端发布/订阅 系统提供高消息持久性和可用性,这使得它适用于构建需要实时不断更新的云原生应用,例如检查可用机票的航空公司应用或检查外币汇率的银行应用。此外,借助这些主要的云提供商,您可以轻松构建高度可扩展的解耦消息传递系统。
自托管发布/订阅 服务提供可定制的消息传递系统,以满足组织内部应用的特定需求,例如简化数据传输或实现异步工作流。这为企业提供了对其系统的更大灵活性和控制权,这些系统可以部署在本地或云环境中。流行的自托管发布/订阅 服务示例包括 RabbitMQ、Apache Kafka 和 Apache Pulsar。
实时发布/订阅 服务促进实时发送和接收消息,而无需频繁轮询消息队列。这减少了传输延迟并提高了整体用户体验。该模型还包括安全功能,如通道加密、用于系统响应性的在线状态检测、消息历史记录和备份。实时发布/订阅 服务系统最适合社交媒体聊天应用、在线游戏和直播等用例。实时发布/订阅 服务系统的示例包括 Pusher 和 PubNub。
除了 发布/订阅 之外,还有各种可用的消息传递技术,包括点对点消息传递和消息队列。消息传递技术的选择取决于个人或组织对应用的期望。通常,在根据其预期用例决定使用哪种技术之前,会评估系统的优缺点。
点对点消息传递 是一种基本的消息传递模型,其中发送者将消息发送给特定的接收者。接收者随后可以读取消息并进行响应。这项技术最适合发送者和接收者有直接连接,并且接收者可以实时处理传入消息的情况。点对点消息传递的一些示例包括 HTTP 和 TCP/IP。
消息队列 是一种消息传递模型,其中消息被排队并由消费者处理。这项技术非常适用于有多个生产者和消费者,且消息需要按顺序传递的场景。消息队列技术的示例包括 RabbitMQ 和 Apache ActiveMQ。
与点对点消息传递和消息队列相比,发布/订阅 消息传递提供了几个优点。首先,发布/订阅 消息传递实现了发布者和订阅者的解耦,消除了发布者需要了解其订阅者的必要性。这使得系统能够更有效地扩展,因为可以在不影响发布者的情况下添加新的订阅者。其次,借助 发布/订阅 消息传递对异步处理的支持,订阅者可以按照自己的速度读取消息。这通过减轻系统负载来帮助提高性能。此外,发布/订阅 消息传递支持使用事件驱动架构,其中事件触发系统中的操作。
现在很明显,发布/订阅 消息传递是构建分布式系统和实现事件驱动架构的强大工具。这是因为它提供了与许多其他技术和服务的集成。下面列出了一些最常用的集成类型
云平台集成:发布/订阅 消息传递是大多数云计算平台(包括 Amazon Web Services (AWS)、Google Cloud Platform (GCP) 和 Microsoft Azure)提供的服务,提供消息过滤和备份等功能。
编程语言集成:发布/订阅 消息传递支持多种编程语言及其库,例如 Java、Python 和 Node.js,从而使应用与 发布/订阅 消息传递服务之间的通信更加便捷。
API 集成:发布/订阅 消息传递允许使用针对多种语言的客户端库以及标准的 gRPC 和 REST 服务 API 技术,通过标准 API 调用来发送和接收消息。
数据库集成:发布/订阅 消息传递可以通过事件驱动架构触发数据库更新和数据处理。例如,当收到新消息时,发布/订阅 消息传递可以导致执行数据库更新。
第三方服务集成:发布/订阅 消息系统允许集成多种第三方服务,例如 Apache Kafka 和 Amazon SNS,以丰富应用功能并实现无缝数据交换。
自定义集成:发布/订阅 还允许根据组织的用例和需求进行自定义集成,以支持其内部应用。
安全和身份验证集成:发布/订阅 系统与身份和访问管理系统(例如 LDAP、OAuth 和 SAML)集成,以确保只有授权用户才能访问敏感数据,并通过通知系统以采取适当措施来帮助防止安全漏洞。
监控和日志记录集成:发布/订阅 系统受到 Prometheus 和 Grafana 等监控、警报和日志记录产品的支持。这使企业能够跟踪和监控其系统性能,并分析数据以获取见解和优化。
CI/CD 集成:与 CI/CD 工具(例如 Jenkins 和 GitLab)的集成实现了构建、测试和应用部署的自动化。此外,这也使得更容易跟踪应用的进度。
Python 是一种编程语言,它提供了一个 Redis 客户端库,允许 Python 开发者与 Redis(一种内存数据结构存储)交互。Redis 提供了一个发布/订阅(pub/sub)消息系统,允许客户端订阅通道并在消息发布到这些通道时接收消息。
Python 可以通过发送 PUBLISH 命令发布消息和发送 SUBSCRIBE 命令订阅通道来使用 Redis 实现 发布/订阅 功能。适用于 Python 的 Redis 客户端库提供了一种便捷的方式来在 Python 应用中处理 发布/订阅 消息传递。
命令 | 使用示例和描述 |
---|---|
SUBSCRIBE | SUBSCRIBE channel [channel …] – 订阅给定的通道 |
UNSUBSCRIBE | UNSUBSCRIBE [channel [channel …]] – 取消订阅提供的通道,如果未指定通道则取消订阅所有通道 |
PUBLISH | PUBLISH channel message – 发布消息到给定的通道 |
PSUBSCRIBE | PSUBSCRIBE pattern [pattern …] – 订阅广播到与给定模式匹配的通道的消息 |
PUNSUBSCRIBE | PUNSUBSCRIBE [pattern [pattern …]] – 取消订阅提供的模式,如果未指定模式则取消订阅所有已订阅的模式 |
为了有效展示该功能,由于 PUBLISH 和 SUBSCRIBE 命令在 Python 端的实现方式,使用辅助线程处理 PUBLISH 操作会更方便。
在 发布/订阅 模式中,发布者可以在一个通道上向任意数量的订阅者发送消息。这些消息是即发即忘的(fire-and-forget),也就是说,如果消息被发布时没有任何订阅者存在,该消息就会消失,无法恢复。
一旦订阅了通道,客户端将进入订阅模式,客户端无法发送任何命令。这样,客户端就变成了只读状态。发布操作没有这种限制。
可以同时订阅多个通道。让我们首先使用 SUBSCRIBE 命令订阅两个通道 weather 和 sports 。
> SUBSCRIBE weather sports Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "weather" 3) (integer) 1 1) "subscribe" 2) "sports" 3) (integer) 2
在另一个客户端(在我们的示例中是另一个终端)中,我们可以向这两个通道中的任何一个发布内容。我们通过运行 PUBLISH 命令来完成此操作
> PUBLISH sports oilers/7:leafs/1 (integer) 1
第一个参数是通道,第二个参数是消息。消息可以是任何内容,在本例中,它是一个编码的体育比分。它返回将收到消息的客户端数量。回到订阅模式客户端,我们将立即看到消息
1) "message" 2) "sports" 3) "oilers/7:leafs/1"
响应包含三个元素:通知(表示它是一条消息)、通道以及最后的消息本身。客户端在收到消息后立即返回监听状态。切换回另一个客户端,我们可以发布另一条消息
> PUBLISH weather snow/-4c (integer) 1
在另一个客户端中,我们将看到相同的格式,但带有另一个通道和事件
1) "message" 2) "weather" 3) "snow/-4c"
让我们向一个没有人订阅的通道发布消息
> PUBLISH currency CADUSD/0.787 (integer) 0
由于没有任何客户端正在监听 currency 通道,返回值为 0。这条消息现在已经消失,随后订阅 currency 通道的客户端将不会收到此消息通知——消息被发出后即被遗忘。
除了订阅单个通道外,Redis 还允许基于模式的订阅。glob 风格的模式由 PSUBSCRIBE 命令启用
> PSUBSCRIBE sports:*
这将获取所有以“sports:*”开头的通道的消息。在另一个客户端中,执行以下命令
> PUBLISH sports:hockey oilers/7:leafs/1 (integer) 1 > PUBLISH sports:basketball raptors/33:pacers/7 (integer) 1 > PUBLISH weather:edmonton snow/-4c (integer) 0
注意前两个命令返回 1,而最后一个命令返回 0。尽管我们没有 sports:hockey 或 sports:basketball 的直接订阅者,但它们仍然被模式订阅接收。回到我们的订阅客户端,我们可以看到只有匹配模式的通道返回了结果。
1) "pmessage" 2) "sports:*" 3) "sports:hockey" 4) "oilers/7:leafs/1" 1) "pmessage" 2) "sports:*" 3) "sports:basketball" 4) "raptors/33:pacers/7"
此响应与直接的 SUBSCRIBE 响应略有不同,因为它同时包含匹配的模式 (2) 和实际的通道名称 (3)。