MQTT
简介
MQTT 可以简单看做一个网络协议,用于机器对机器的通信(与客户端到服务器的传输有点区别)。智能传感器、可穿戴设备和其他物联网(IoT)设备通常必须通过带宽有限的资源受限网络传输和接收数据。这些物联网设备使用 MQTT 进行数据传输,因为它易于实施,并且可以有效地传输物联网数据。MQTT 支持设备到云端和云端到设备之间的消息传递。
特点
- 轻量级:MQTT协议是一种轻量级的协议,它的开销非常小,可以在低带宽、不稳定的网络环境下运行。
- 灵活性:双向通信,MQTT协议支持多种消息传输模式,包括点对点、发布/订阅和请求/响应等模式,可以根据不同的应用场景选择合适的模式。
- 可靠性:MQTT协议支持QoS(服务质量)等级,可以确保消息的可靠传输,同时还支持消息持久化和重传机制,保证消息不会丢失。
- 易于集成:MQTT协议可以与各种不同的平台和设备集成,包括传感器、嵌入式设备、移动设备和云平台等,可以实现跨平台、跨设备的通信。
- 安全性:MQTT协议支持TLS/SSL加密和认证机制,可以保证消息的安全传输和身份验证,防止数据泄露和攻击。
MQTT为什么轻量
从连接角度上看
HTTP:假设有一个传感器设备需要定期向云平台发送数据,数据量很小,只有几个字节,但是设备的网络带宽很低,且网络连接不稳定。如果使用传统的HTTP协议来传输数据,由于HTTP协议的开销较大,每次传输都需要建立TCP连接、发送HTTP头部等操作,会导致网络带宽的浪费和连接的不稳定性。
MQTT:而如果使用MQTT协议来传输数据,由于MQTT协议是一种轻量级的协议,它的开销非常小。1.只需要建立一次TCP连接,然后就可以通过MQTT协议来传输数据,不需要每次都重新建立连接和发送头部信息,可以大大减少网络带宽的浪费和连接的不稳定性。2.同时,MQTT协议还支持QoS等级,可以确保数据的可靠传输,即使网络连接不稳定,也可以保证数据不会丢失。
MQTT的长连接机制和HTTP的Keep-Alive机制有所不同,HTTP中客户端和服务器之间的数据传输是同步的。而MQTT客户端和服务器之间的数据传输是异步的,客户端可以发送多个消息,服务器可以异步地接收和处理这些消息。需要注意的是,MQTT协议的长连接机制需要客户端和服务器之间的协作,客户端需要定期发送心跳包来保持连接,服务器需要及时响应心跳包,以保证连接的稳定性。同时,长时间的连接也会占用服务器的资源,因此需要合理设置连接的超时时间和心跳包的发送间隔。
从报文角度上看
- MQTT协议的头部信息
MQTT协议的头部信息非常简单,只需要几个字节就可以表示。MQTT协议的头部信息包括以下几个字段:
- 固定头部:包含消息类型、QoS等级、保留标志等信息,共1个字节。
- 可变头部:包含消息标识符、主题名等信息,长度不固定。
- 消息体:包含消息的实际内容,长度不固定。
例如:假设有一个MQTT客户端需要向服务器发送一条消息,消息内容为"Hello, MQTT!“,主题名为"test”,QoS等级为1。
固定头部:0x32(消息类型为Publish,QoS等级为1,保留标志为0)
可变头部:0x00 0x04(主题名长度为4)
主题名:0x74 0x65 0x73 0x74(主题名为"test")
消息标识符:0x00 0x01(消息标识符为1)
消息体:0x48 0x65 0x6C 0x6C 0x6F 2C 0x20 0x4D 0x51 0x54 0x54 0x21(消息内容为"Hello, MQTT!")
- HTTP协议的头部信息
HTTP协议的头部信息相对复杂,包含了很多字段,主要用于描述请求或响应的相关信息。HTTP协议的头部信息包括以下几个部分:
- 请求行:包含请求方法、请求URL和HTTP协议版本等信息。
- 请求头部:包含请求的附加信息,如请求头部字段、Cookie等。
- 空行:用于分隔请求头部和请求正文。
- 响应行:包含HTTP协议版本、状态码和状态描述等信息。
- 响应头部:包含响应的附加信息,如响应头部字段、Cookie等。
- 空行:用于分隔响应头部和响应正文。
- 响应正文:包含响应的实际内容,长度不固定。
例如:
假设有一个HTTP客户端需要向服务器发送一个GET请求,请求URL为"http://www.example.com/index.html",请求头部包含"User-Agent"和"Accept-Language"字段。那么该请求的HTTP协议头部信息如下:
请求行:GET /index.html HTTP/1.1
请求头部: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Accept-Language: en-US,en;q=0.8
空行:
响应行:HTTP/1.1 200 OK
响应头部: Content-Type: text/html; charset=UTF-8 Content-Length: 1234
空行:
响应正文:(省略)
可以对比看出MQTT协议的报文信息都是用十六进制表示,各个请求属性也是这种用二进制的位图去区分,相对HTTP报文都是字符串来说更加的轻量,节省传输资源。
从数据类型上看
HTTP协议的数据格式是文本格式,即使用ASCII码表示的文本字符串,而MQTT协议的数据格式是二进制格式,即使用字节流表示的二进制数据。
使用二进制表示的话,每个字节可以表示多个0或1,而文本字符串使用字符编码方式表示,每个字符通常需要使用多个字节来表示,例如UTF-8编码中的中文字符需要使用3个字节来表示,这就导致了文本字符串的数据大小通常比二进制数据大。
同时二进制数据通常可以使用压缩算法进行压缩,压缩后的数据大小更小,传输效率更高。而文本字符串的压缩效果通常不如二进制数据,因为文本字符串中的字符通常是不规则的,难以进行有效的压缩。
总的来说,使用二进制数据传输可以减少数据的大小,提高传输效率,因为二进制数据使用二进制编码方式表示,可以更紧凑地表示数据,同时可以使用更高效的压缩算法进行压缩,而且二进制数据通常使用二进制传输方式,传输效率更高。与之相比,文本字符串的数据大小通常较大,压缩效果较差,传输效率较低。
三种服务质量
MQTT的三种服务质量及实现原理:
https://www.emqx.com/zh/blog/introduction-to-mqtt-qos
QoS 0:最多一次传递
在QoS 0级别下,发布者只需将消息发送给MQTT代理,代理会将消息发送给订阅者,但不会保证消息是否到达订阅者。这种级别的实现非常简单,因为不需要进行确认或重传,所以消息传递的延迟和带宽占用都很低。
通常选择使用 QoS 0 传输一些高频且不那么重要的数据,比如传感器数据,周期性更新,即使遗漏几个周期的数据也可以接受。
QoS 1:最少一次传递
发送方发送报文后,需要收到一个ACK报文,才会停止重复发送,否则到了超时时间会重传报文。这里其实是涉及到两次网络传输(一次发送报文消息、一次接收方发送ACK报文信息)
在QoS 1级别下,发布者将消息发送给MQTT代理,代理会将消息发送给订阅者,并等待订阅者的确认。如果代理没有收到确认,它会重新发送消息,直到收到确认为止。这种级别的实现需要进行确认和重传,所以消息传递的延迟和带宽占用都比QoS 0高。
QoS1在协议层面上无法避免消息重复的情况,因此在我们的报文内容里面通常加上报文的唯一id,在应用层通过id去对消息去重是最常规的处理方法。
QoS 2:恰好一次传递
前提:MQTT识别报文是通过一个Packet ID来识别是不是重复信息。Qos1收到两条相同Packet ID报文的消息是没办法知道这是重复的信息,还是两条不同的消息只是用的同一个ID的情况,因此Qos1是存在重复消息的问题。QoS2会用更加复杂的交互逻辑来确保消息不会重复。
发送方发送完报文,需要等待收到接收方发送PUBREC报文(在这期间还可以超时重发这个报文),等到收到PUBREC报文后,发送方会继续发送PUBREL (Publish Release)报文,此时不能再重发报文了,并且Packet ID也不能使用。等待收到对端回复的 PUBCOMP 报文后,才表示此时Packet ID被释放完成,可以继续使用Packet ID去发送消息。
因此对于接收方以 PUBREL 报文为界限,凡是在 PUBREL 报文之前到达的 PUBLISH 报文,有相同的Packet ID都必然是重复的消息;而凡是在 PUBREL 报文之后到达的 PUBLISH 报文,有相同的Packet ID都必然是全新的消息。
在QoS 2级别下,发布者将消息发送给MQTT代理,代理会将消息发送给订阅者,并等待订阅者的确认。如果代理没有收到确认,它会重新发送消息,直到收到确认为止。订阅者收到消息后,会发送确认给代理,代理再将确认发送给发布者。如果代理没有收到确认,它会重新发送消息,直到收到确认为止。这种级别的实现需要进行确认、重传和去重,所以消息传递的延迟和带宽占用都比QoS 1更高。
总的来说,QoS级别越高,消息传递的可靠性越高,但延迟和带宽占用也越高。在选择QoS级别时,需要根据应用场景的需求来进行权衡。
在MQTT中,QoS级别是在发布消息时指定的。接收者也可以指定所需的QoS级别。如果发布者和接收者的QoS级别不同,MQTT会自动升级到更高的级别。例如,如果发布者使用QoS 0,而接收者使用QoS 2,MQTT会将消息升级到QoS 2级别。
消息可靠
MQTT还有一个持久会话的功能,对应的是Clean Session(清理会话)选项。当Clean Session = 0,以及QoS为1或着2的时候,加入接收端离线了也会在下次在线时收到断连期间的消息。此时的消息会暂存在我们的MQTT服务器端。
MQTT Broker
MQTT Broker 也称为 MQTT 消息服务器,它可以是运行了 MQTT 消息服务器软件的一台服务器或一个服务器集群。
MQTT Broker 负责接收来自客户端的网络连接,并处理客户端的订阅/取消订阅、消息发布(Publish)请求,同时也会将客户端发布的消息转发给其他订阅者。
MQTT负载均衡
MQTT通过共享订阅的方式来做订阅端的负载均衡:
MQTT使用共享订阅来实现负载均衡的方法如下:
-
创建一个共享订阅,多个客户端可以订阅同一个主题。
-
在共享订阅中,MQTT服务器将消息平均分配给所有订阅者,以实现负载均衡。
-
当一个客户端订阅了共享订阅中的主题时,它将收到一部分消息。
-
如果有多个客户端订阅了共享订阅中的主题,它们将共享消息的负载。
-
如果一个客户端断开连接,MQTT服务器将重新分配该客户端的负载给其他订阅者。
-
使用共享订阅可以提高MQTT服务器的性能和可伸缩性,同时确保消息的可靠传递。
例如实时数据处理:当需要处理大量实时数据时,使用共享订阅可以将数据平均分配给多个订阅者,从而实现负载均衡。
MQTT broker也可以通过集群实现服务器端的负载均衡
多服务器节点集群,且支持节点的自动发现。相对于单服务器,集群能通过多台服务器之间的协作带来以下优势:
- 高可用性。单台或少量的服务器故障并不会导致整个消息服务中断,其余的正常工作的节点可以继续提供服务;
- 负载均衡。通过负载均衡机制,集群可以把负载平均的分布在各个节点;
- 更高的整体性能。相比单机部署,多节点的集群能够成倍的提升整个系统的连接和消息处理能力;
- 可扩展性。可以通过在集群中添加新节点的方式来完成扩容而无需停机。
MQTT做请求应答模式
MQTT本身是一种发布/订阅模式的通信协议,不直接支持请求/响应模式。但是,可以通过以下方法实现请求/响应模式:
-
使用MQTT的QoS(服务质量)机制。将QoS设置为1或2,可以确保消息的可靠传输和确认。在请求消息中设置一个唯一的标识符,然后在响应消息中包含相同的标识符,以便请求方可以识别响应。 2. 使用专门的主题来处理请求和响应。例如,可以使用“request”主题来发送请求消息,然后使用“response”主题来发送响应消息。请求方可以订阅响应主题,以便在收到响应时进行处理。
-
使用MQTT的遗嘱消息机制。请求方可以在发送请求消息时设置遗嘱消息,以便在请求方离线或者异常时,服务器可以代替请求方给所有订阅相关主题的客户端发送一个响应消息作为遗嘱消息。文章来源:https://www.uudwc.com/A/5ZdR1/
需要注意的是,使用MQTT实现请求/响应模式可能会增加通信的延迟和复杂性。因此,应该根据具体的应用场景和需求来选择合适的通信模式。文章来源地址https://www.uudwc.com/A/5ZdR1/