发布/订阅模式(也称为发布/订阅)提供了传统客户端-服务器架构的替代方案。在传统的客户端-服务器模型中,发布信息的终端和请求信息的终端是直接相连的。发布/订阅模型则是将发送消息的客户端(发布者)与接收消息的客户端(订阅者)分离。发布者和订阅者从不直接相互联系。事实上,他们甚至不知道对方的存在。它们之间的连接由第三个组件(代理)处理。代理的工作是过滤所有传入的消息并将它们正确地分发给订阅者。因此,让我们更深入地研究一下 pub/sub 的一些一般方面(稍后我们将讨论 MQTT 的细节)。
pub/sub 最重要的方面是消息的发布者与接收者(订阅者)的解耦。这种解耦有几个维度:
空间解耦:发布者和订阅者不需要相互了解(例如不交换IP地址和端口)。 时间解耦:发布者和订阅者不需要同时运行。 同步解耦:在发布或接收过程中不需要中断两个组件的操作。
总之,发布/订阅模型消除了消息发布者和接收者/订阅者之间的直接通信。代理的过滤活动可以控制哪个客户端/订阅者接收哪个消息。解耦具有三个维度:空间、时间和同步。
Pub/Sub 的扩展性比传统的客户端-服务器方法更好。这是因为代理上的操作可以高度并行化,并且可以以事件驱动的方式处理消息。消息缓存和消息的智能路由通常是提高可扩展性的决定性因素。尽管如此,将连接扩展到数百万个连接是一项挑战。可以使用集群代理节点来实现如此高水平的连接,以使用负载平衡器将负载分配到更多单独的服务器上。
很明显,代理在发布/订阅过程中起着关键作用。但是代理如何过滤所有消息,以便每个订阅者只接收感兴趣的消息?如您所见,代理有几个过滤选项:
选项 1:基于主题的过滤
此过滤基于作为每条消息一部分的主题或主题。接收客户端向代理订阅感兴趣的主题。从那时起,代理确保接收客户端获取发布到订阅主题的所有消息。通常,主题是具有层次结构的字符串,允许基于有限数量的表达式进行过滤。
选项 2:基于内容的过滤
在基于内容的过滤中,代理根据特定的内容过滤语言过滤消息。接收客户端订阅过滤他们感兴趣的消息的查询。这种方法的一个显着缺点是必须事先知道消息的内容,并且不能加密或轻易更改。
选项 3:基于类型的过滤
当使用面向对象的语言时,基于消息(事件)的类型/类别进行过滤是一种常见的做法。例如,订阅者可以收听所有类型为 Exception 或任何子类型的消息。
当然,发布/订阅并不是每个用例的答案。在使用此模型之前,您需要考虑一些事项。发布者和订阅者的解耦是发布/订阅中的关键,它本身就带来了一些挑战。例如,您需要事先了解已发布数据的结构。对于基于主题的过滤,发布者和订阅者都需要知道要使用哪些主题。要记住的另一件事是消息传递。发布者不能假设有人正在监听发送的消息。在某些情况下,可能没有订阅者读取特定消息。
既然我们已经大致探讨了发布/订阅模型,让我们专门关注MQTT。根据您想要实现的目标,MQTT 体现了我们提到的 pub/sub 的所有方面:
MQTT 在空间上解耦了发布者和订阅者。要发布或接收消息,发布者和订阅者只需要知道代理的主机名/IP 和端口即可。
MQTT 按时间解耦。尽管大多数 MQTT 用例以近乎实时的方式传递消息,但如果需要,代理可以为不在线的客户端存储消息。(存储消息必须满足两个条件:客户端已连接到持久会话并订阅了服务质量(QoS)大于 0 的主题)。
MQTT 异步工作。因为大多数客户端库都是异步工作的,并且基于回调或类似模型,所以在等待消息或发布消息时不会阻塞任务。在某些用例中,同步是可取的并且是可能的。为了等待某个消息,一些库具有同步 API。但是流程通常是异步的。
在大多数 pub/sub 系统中,数据的逻辑处理都是在代理端设置和进行,MQTT 在客户端时确实是实现了 pub/sub 的本质,这使其成为小型和受限设备的轻量级协议,而且在客户端特别容易使用
MQTT 使用基于主题的消息过滤。每条消息都包含一个主题,代理可以使用该主题来确定订阅客户端是否需求该消息。
为了应对发布/订阅系统的挑战,MQTT 具有三个服务质量 (QoS) 级别。您可以轻松地指定消息成功地从客户端传递到代理或从代理传递到客户端。但是可能出现无人订阅的主题,管理者必须知道并处理这种情况。HiveMQ MQTT 代理具有可以解决此类情况的插件系统。您可以让代理采取行动,或者简单地将每条消息记录到数据库中以进行历史分析。
关于 MQTT 的名称以及该协议是否作为消息队列实现存在很多混淆。我们将尝试阐明该主题并解释其中的差异。在上一篇文章中,我们提到 MQTT 指的是 IBM 的 MQseries 产品,与“消息队列”无关。无论名称来自何处,了解 MQTT 与传统消息队列之间的区别都很有用:
消息队列存储消息直到它们被消费 当您使用消息队列时,每条传入的消息都存储在队列中,直到被客户端(通常称为消费者)拾取。如果没有客户端接收到该消息,则该消息将停留在队列中并等待被消费。在消息队列中,消息不可能不被任何客户端处理,就像在没有人订阅主题的情况下在 MQTT 中一样。
一条消息只能由一个客户端消费 另一个很大的区别是,在传统的消息队列中,一条消息只能由一个消费者处理。负载分布在队列的所有消费者之间。在 MQTT 中,行为完全相反:订阅主题的每个订阅者都会收到消息。
队列被命名并且必须显式创建 队列比主题更严格。在可以使用队列之前,必须使用单独的命令显式创建队列。只有在队列被命名和创建后,才能发布或消费消息。相比之下,MQTT 主题非常灵活,可以动态创建。