we’ll explore the basic concepts (publish/subscribe, client/broker
) and basic functionality (Connect, Publish, Subscribe
) of MQTT. Then, we’ll look at the features: Quality of Service, Retained Messages, Persistent Session, Last Will and Testament, Keep Alive and more
.
“MQTT 是客户端服务器发布/订阅消息传输协议。它重量轻、开放、简单,并且设计得易于实施。这些特性使其非常适合在许多情况下使用,包括受限环境,例如在机器对机器 (M2M) 和物联网 (IoT) 环境中需要少量代码占用空间和/或网络带宽非常宝贵的环境中的通信。 “
引用自官方MQTT 3.1.1 规范
值得注意的是,许多来源错误地将 MQTT 标记为消息队列协议。MQTT 不是传统的消息排队解决方案(尽管在某些情况下可以对消息进行排队,我们将在即将发布的帖子中详细讨论这一事实)
The publish/subscribe pattern 提供了传统客户端-服务器架构的替代方案。在客户端-服务器模型中,客户端直接与端点通信。发布/订阅模型decouples publisher from subscribers(完成发布者和订阅者的解耦)
– 它们之间的连接由第三个组件处理( broker)
pub/sub 最重要的方面是消息的发布者与接收者(订阅者)的解耦。这种解耦有几个维度:
很明显,broker 在 pub/sub 过程中起着举足轻重的作用。但是代理如何过滤所有消息,以便每个订阅者只接收感兴趣的消息?正如您将看到的,经纪人有几个过滤选项:
选项 1:基于主题的过滤
此过滤基于属于每条消息的主题topic。接收客户端向代理订阅感兴趣的主题。从那时起,代理确保接收客户端获取所有发布到订阅主题的消息。通常,主题是具有分层结构的字符串,允许基于有限数量的表达式进行过滤。
选项 2:基于内容的过滤
在基于内容的过滤中,代理根据特定的内容过滤语言过滤消息。接收客户端订阅过滤他们感兴趣的消息查询。这种方法的一个显着缺点是必须事先知道消息的内容,不能加密或轻易更改。
选项 3:基于类型的过滤
当使用面向对象的语言时,基于消息(事件)的类型/类进行过滤是一种常见做法。例如,订阅者可以收听所有类型为 Exception 或任何子类型的消息。
当然,发布/订阅并不是每个用例的答案。在使用此模型之前,您需要考虑一些事项。发布者和订阅者的解耦是 pub/sub 的关键,它本身也存在一些挑战。例如,您需要事先了解已发布数据的结构
。对于基于主题的过滤,发布者和订阅者都需要知道要使用哪些主题。要记住的另一件事是消息传递。发布者不能假设有人正在收听发送的消息。在某些情况下,可能没有订阅者读取特定消息。
既然我们已经大致探讨了发布/订阅模型,让我们专门关注MQTT。根据您想要实现的目标,MQTT 体现了我们提到的 pub/sub 的所有方面
:
MQTT 在空间上分离了发布者和订阅者。要发布或接收消息,发布者和订阅者只需要知道代理的主机名/IP 和端口。
MQTT 按时间解耦。尽管大多数 MQTT 用例都近乎实时地传递消息,但如果需要,代理可以为不在线的客户端存储消息。(必须满足两个条件才能存储消息:客户端已连接到持久会话并订阅了 Quality of Service
大于 0 的主题)
MQTT 异步工作。由于大多数客户端库异步工作并且基于回调或类似模型,因此在等待消息或发布消息时不会阻塞任务。在某些用例中,同步是可取的,也是可能的。为了等待某个消息,一些库具有同步 API。但流程通常是异步的。
另一件应该提到的事情是MQTT在客户端特别容易使用。大多数发布/订阅系统都有代理端的逻辑,但在使用客户端库时,MQTT 确实是发布/订阅的本质,这使其成为适用于小型和受限设备的轻量级协议。
MQTT 使用subject-based filtering of messages。每条消息都包含一个topic(subject),代理可以使用它来确定订阅客户端是否收到消息。
为了应对发布/订阅系统的挑战,MQTT 具有三个服务质量 (QoS) 级别。您可以轻松指定消息从客户端成功传送到代理或从代理成功传送到客户端。但是,有可能没有人订阅特定主题。如果这是一个问题,the broker必须知道如何处理这种情况
消息队列存储消息直到消息被消费
,每条传入消息都存储在队列中,直到被客户端(通常称为消费者)接收。如果没有客户端接收到消息,消息将保持在队列中并等待被消费。在消息队列中,消息不可能不被任何客户端处理
一条消息只被一个客户端消费
,在传统的消息队列中,一条消息只能被一个消费者处理。负载分布在队列的所有消费者之间。在 MQTT 中,行为完全相反:订阅主题的每个订阅者都会收到消息。
队列是命名的,必须显式创建 。队列比主题严格得多。在使用队列之前,必须使用单独的命令显式创建队列。只有在队列命名和创建之后,才可以发布或消费消息。相比之下,MQTT 主题非常灵活,可以即时创建。