MQTT基础——Part 2. 发布/订阅模式

本节主要讲述发布/订阅模式。首先关注发布/订阅的基本特征,然后再聚焦到MQTT本身,着重讲述MQTT协议与传统消息队列协议的不同。

一、发布/订阅模式

发布/订阅模式即Pub/Sub,是传统客户端/服务器模型(客户端直接连接服务器)的替代。传统的客户端/服务器模型是客户端直接连接到服务端(Endpoint),而发布/订阅模式实现了客户端的解耦。客户端(Publisher,消息发布者)发送特定的消息到另一个客户端(Subscriber,消息接收者)。这意味着发布者和订阅者都无需关心对方的存在与否。它们之间还有第三个组件,即消息经纪人(Broker),所有的发布者和接收者都要连接到消息经纪人,消息经纪人会过滤所有到来的消息,并根据需要分发这些消息。

发布/订阅模式实现了发布者和订阅者之间的解耦,可以从多维度进行区分:
1)空间解耦:发布者和订阅者均无需知道彼此的存在(比如对方的IP地址和端口)
2)时间解耦:发布者和订阅者无需同时运行
3)同步解耦:在发布或接收期间,双方组件的操作不会暂停
总的来说,发布/订阅模式从消息上对发布者和订阅者进行了解耦,并且通过对消息的过滤实现了只有某些客户端才能受到相应的消息。解耦包含了三个维度:空间、时间、同步。

可扩展性

发布/订阅模式还提供了比传统的客户端-服务器模式更好的可扩展性。这是因为,在经纪人Broker上的操作,是可以高度并行和基于事件驱动进行处理的。而且还可以对消息进行缓存和对消息进行智能路由,以进一步改善可扩展性。但是,要把发布/订阅模式扩展到支持数百万的连接,在技术上仍然是一个挑战。这一点可以通过对经纪人Broker节点进行集群来实现,以便通过负载平衡器把负载分配到更多的服务器节点来实现。

消息过滤

经纪人Broker怎样过滤所有的消息,以及怎样让每个订阅者只获得他感兴趣的消息。

选项1:基于主题的过滤

过滤是根据标题(Subject)或主题(Topic)的,而标题或主题是每一个消息的组成部分之一。正在接收的客户端订阅了它感兴趣的主题Topic,就可以从经纪人Broker那里得到它基于订阅主题Topic的所有消息。主题Topic通常是一个具有分层结构的字符串,可以基于有限数量的表达式进行过滤。

选项2:基于内容的过滤

基于内容的过滤正如名字的暗示,是指经纪人Broker过滤基于特定内容、特定过滤器语言的消息。因此客户端订阅他们感兴趣的消息的过滤查询。这种过滤方法有一个较大的缺点,即消息的内容必须事先已知,并且消息不容易进行加密或修改。

选项3:基于类型的过滤

在使用面向对象的编程语言时,有一种常见的做法,基于消息或事件的类型/类别进行过滤。在这种情况下,订阅者可以监听所有的消息,并从类型异常或子类型中获取消息。
当然,发布/订阅模式并不能解决一切,在使用此模式之前需要考虑一些事情。发布者和订阅者之间的解耦是发布/订阅模式的关键,这给使用它带来了一些挑战。你必须事先知道发布的数据的结构。在基于主题的过滤的场景,无论是发布者还是订阅者,都需要理解怎样正确的使用主题。另一方面是消息的交付,发布者不能想当然地认为有人会监听他发送的消息。因为在一些情况下,发布的消息可能没有任何一位订阅者。

MQTT

现在我们对发布/订阅模式已经有了一定的了解,但对于MQTT还不够。MQTT体现了前面所有提及的方面,这取决于你想达到什么目标。MQTT从空间上对发布者和订阅者进行了解耦。因此,客户端只需知道Broker的主机名(或IP)和端口,以实现发布/订阅消息。MQTT还从时间上对发布者和订阅者进行了解耦,但这往往是倒退的行为,因为大多数使用场景都是以近实时的方式传递消息。当然,Broker还要能够存储信息,方便那些不在线的客户端(订阅者)。(这需要满足两个条件:客户端已连接一次,它的会话是持久的;并且客户端订阅了服务质量QoS大于0的主题,关于QoS在后续会讲到)。
MQTT还能够解耦同步,因为大多数客户端库是异步工作的,是基于回调或类似模型的实现。所以客户端在等待消息或发布消息时不会阻止其它任务的执行。但也有一些场景需要使用同步,而且这也是可以实现的。因此有些客户端库提供了同步API,以等待某种消息。但通常的流程都是异步的。还应该特别强调一点,MQTT在客户端特别容易使用。大多数的发布/订阅系统的实现逻辑通常都在Broker端,而且MQTT协议仅仅使用了发布/订阅模式的基础内容,这使得MQTT确实是轻量级协议,确实是为小型和受限的设备而设计的协议。

MQTT使用了基于主题的过滤方式。因此,每一个消息都包含了一个主题Topic,要判断订阅了主题的客户端是否能够接收消息,Broker会使用主题Topic进行查找。后续会详细讨论主题Topic,以及接收消息的各种可能性。也会介绍MQTT Broker的实现之一——HiveMQ的基于内容的过滤以及它自定义的插件系统。
通常为了解决发布/订阅系统的挑战,MQTT有服务质量QoS分级。QoS能够很容易确定某一个消息是否成功从客户端交付给Broker或者从Broker交付给客户端。这里也存在一些偶然性的情况,有些特定主题没有订阅者。如果这是一个问题,它取决于Broker如何处理这样的情况。例如,HiveMQ的Broker有一个插件系统,它能够识别这种情况,并采取行动或仅仅是输出相应的日志到数据库供后续分析。为了减轻Topic主题的不灵活性,认真设计主题树(Topic Tree)并留有扩展的余地,在未来是很重要的。如果你遵循这些策略,那么MQTT会越用越好。

MQTT与消息队列的区别

对于MQTT,存在一些混淆的地方,它的名字使得它容易被当作消息队列来实现。这里要再次强调,MQTT不是消息队列,在IBM的产品中,提供的消息队列被称为MQserie。那么,MQTT与传统的消息队列到底有什么区别?

1)消息队列会一直保存消息,直到消息被消费(Consume)

在使用消息队列时,每一条即将到来的消息都会被存放到消息队列中,直到消息被任何客户端取走(通常称为消费。否则,消息会一直存放在队列中,等待被消费。如果某条消息一直不被任何客户端所消费,这是不可能的,就好像MQTT的主题Topic没有任何客户端订阅的情况。

2)一条消息只能由一个客户端消费

这是一个很大的区别,在传统的消息队列中,一条消息仅能由一个消费者进行处理。因此,所有消费者之间的负载可以被分发到某个特定的队列上。而在MQTT上则完全相反,对于每一个订阅者,只要他们订阅了某个主题,那么他们都可以得到该主题的消息。

3)队列被命名,且必须以明确的方式创建

队列远远不及主题那么灵活。在使用队列之前,必须先使用一个单独的命令明确地创建队列。只有在队列创建之后,才可以在该队列上发布消息或消费消息。而MQTT的主题Topic是非常灵活的,可以动态地创建主题。

猜你喜欢

转载自www.linuxidc.com/Linux/2016-10/136404.htm