当前位置: 首页 > news >正文

MQTT 服务质量 (QoS) 深度解析

什么是 QoS?为什么它很重要?

想象一下寄信,你有几种选择:

  1. 平信 (Postcard): 丢进邮筒就不管了,可能寄到,也可能寄丢。这是最快、最便宜的方式。

  2. 挂号信 (Registered Mail): 需要收件方签收,邮局保证至少会送到对方手上,如果没收到签收回执,会重新投递。对方可能会因为某些原因(比如回执丢失)收到两次。

  3. 机密文件专送 (Secure Courier): 一个非常严谨的过程。快递员送出文件,对方签收表示“已收到但未开启”,快递员回报总部。总部再授权快递员让对方“开启文件”,对方开启后再次签字确认“已开启并处理”,快递员再次回报总部。整个过程确保文件只被对方精确地接收和处理一次。

MQTT 的 QoS (Quality of Service) 就扮演着类似的角色。它不是指网络速度有多快,而是指 消息投递的可靠性承诺。根据你的业务需求,你可以在“快但不一定可靠”和“绝对可靠但慢一点”之间做出选择。

QoS 0: 最多一次 (At most once) - “发射后不管”

这是最基础的等级,就像寄平信。

核心机制
  • 发布者 (Publisher): 发送消息 (PUBLISH)。

  • 代理 (Broker): 接收消息并尝试发送给订阅者。

  • 订阅者 (Subscriber): 接收消息。

整个过程没有任何确认环节。发布者把消息发出去后,就认为任务完成了,不会关心对方是否收到,也不会重发。

场景与数据讲解
  • 场景: 智能家居中的一个温度传感器,每 5 秒向家庭控制中心报告一次客厅温度。

  • 设备:

    • 发布者: 客厅温度传感器 (sensor-livingroom)

    • 订阅者: 家庭控制 App (app-control-center)

    • 主题: home/livingroom/temperature

  • 数据: {"temperature": 22.5, "timestamp": 1677610000}

情况一:网络正常

  1. sensor-livingroom 发送 PUBLISH 消息,内容为 {"temperature": 22.5, ...}

  2. Broker 收到消息。

  3. Broker 将消息转发给 app-control-center

  4. App 成功收到温度数据,更新界面显示。

[传感器] --- PUBLISH ---> [Broker] --- PUBLISH ---> [App]
(发送后即忘)

情况二:网络瞬间中断

  1. sensor-livingroom 发送 PUBLISH 消息。

  2. 在消息到达 Broker 之前,网络出现波动,数据包丢失。

  3. sensor-livingroom 不知道消息丢失了,它不会做任何事。

  4. Broker 从未收到该消息,因此 app-control-center 也永远不会收到 22.5 这条温度读数。

  5. 5 秒后,传感器发送下一条数据,例如 {"temperature": 22.6, ...},一切照常。

  • 优点: 速度极快,网络和设备开销最小。因为它只发送一次数据包。

  • 缺点: 消息可能会丢失

  • 适用场景:

    • 可以容忍数据丢失的非关键业务。

    • 数据更新频率非常高,丢失一两条无伤大雅(如上面的温度传感器)。

    • 网络环境非常稳定可靠。

QoS 1: 至少一次 (At least once) - “必须收到回执”

这个等级保证消息至少会送达一次,就像寄挂号信。

核心机制

为了确保送达,QoS 1 引入了一个确认机制:PUBACK (Publish Acknowledgment)。

  • 发布者 (Publisher): 发送 PUBLISH 消息,并在本地保存该消息的副本,直到收到确认为止。

  • 接收方 (Broker/Subscriber): 收到消息后,必须回复一个 PUBACK 确认包。

  • 重发机制: 如果发布者在一定时间内没有收到 PUBACK,它会认为消息丢失了,于是会重新发送该消息的副本(此时消息头中的 DUP 标志位会被设为 1,表示这是一个重复的消息)。

场景与数据讲解
  • 场景: 你通过手机 App 给家里的智能电灯发送一个“开灯”指令。

  • 设备:

    • 发布者: 你的手机 App (app-remote-control)

    • 订阅者: 客厅的智能电灯 (smart-light-01)

    • 主题: home/livingroom/light/command

  • 数据: {"action": "ON"}

情况一:网络正常

  1. App 发送 PUBLISH 指令 {"action": "ON"},并启动一个计时器等待确认。

  2. Broker 收到消息,并立即回复一个 PUBACK 给 App。

  3. App 收到 PUBACK,知道 Broker 已成功接收,于是删除本地保存的指令副本。

  4. 同时,Broker 将指令转发给智能电灯(这个过程同样遵循 QoS 1)。

  5. 电灯收到指令,执行开灯动作,并回复一个 PUBACK 给 Broker。

  6. Broker 收到电灯的 PUBACK,整个流程完成。

[App] --- PUBLISH (保存副本) ---> [Broker]
[App] <--- PUBACK --- [Broker] (收到确认, 删除副本)[Broker] --- PUBLISH (保存副本) ---> [电灯]
[Broker] <--- PUBACK --- [电灯] (收到确认, 删除副本)

情况二:确认包 (PUBACK) 丢失

这是 QoS 1 可能产生重复消息的关键点。

  1. App 发送 PUBLISH 指令 {"action": "ON"}

  2. Broker 成功收到,并将指令转发给电灯。

  3. 电灯收到指令,成功开灯

  4. 电灯向 Broker 回复 PUBACK

  5. 不幸的是,这个 PUBACK 在传输过程中丢失了。

  6. Broker 没有收到电灯的确认,它的计时器超时了。

  7. Broker 重新发送 PUBLISH 指令 {"action": "ON"}(DUP 标志位为 1)。

  8. 电灯再次收到“开灯”指令。对于一个简单的电灯,它可能会再次执行“开灯”动作(虽然灯已经是亮的),或者忽略这个重复指令。

  9. 这次电灯回复的 PUBACK 成功到达 Broker,流程结束。

  • 优点: 保证消息不会丢失,可靠性高。

  • 缺点: 可能会收到重复的消息。订阅方需要有能力处理重复数据(例如,通过消息 ID 来判断是否已处理过)。

  • 适用场景:

    • 绝大多数要求可靠传输的场景。

    • 远程控制指令,如开关、调节设备状态。重复执行一次通常没有严重副作用。

    • 重要的状态通知,如“门已打开”、“设备离线”。

QoS 2: 正好一次 (Exactly once) - “严谨的四步握手”

这是最可靠但也最慢的等级,通过一个精密的四步握手协议来确保消息不重不丢,就像护送机密文件。

核心机制

QoS 2 不仅要确认对方“收到”,还要确认对方“处理”了这个消息。

  1. PUBLISH: 发布者发送消息,并在本地保存

  2. PUBREC (Publish Received): 接收方收到后,回复 PUBREC。这相当于说:“我收到你的消息了,我把它存起来了,请放心。现在请告诉我是否可以发布/处理它。”

  3. PUBREL (Publish Release): 发布者收到 PUBREC 后,就可以丢弃本地的消息副本了,然后回复一个 PUBREL。这相当于说:“好的,既然你收到了,现在请正式发布/处理它吧。”

  4. PUBCOMP (Publish Complete): 接收方收到 PUBREL 后,才将消息传递给订阅者,并回复 PUBCOMP。这相当于说:“好的,我已经处理完毕。” 发布者收到 PUBCOMP 后,整个流程才算真正结束。

场景与数据讲解
  • 场景: 一个在线支付系统通过 MQTT 发送一条支付指令,要求从账户 A 扣款 100 元。这条指令绝对不能丢失,也绝对不能被执行两次。

  • 设备:

    • 发布者: 支付网关 (payment-gateway)

    • 订阅者: 银行核心系统 (bank-core-system)

    • 主题: billing/transaction

  • 数据: {"from_account": "A", "to_account": "B", "amount": 100}

流程演示(网络一切正常)

  1. PUBLISH: payment-gateway 发送扣款指令,并在数据库中将该条指令标记为“处理中”。

  2. PUBREC: Broker 收到后,将消息持久化存储,并回复 PUBREC

  3. PUBREL: payment-gateway 收到 PUBREC 后,更新指令状态为“已确认送达”,并发送 PUBREL

  4. PUBCOMP: Broker 收到 PUBREL 后,才将消息推送给 bank-core-system,并回复 PUBCOMPbank-core-system 执行扣款,确保只执行一次。

--- 第1,2步:确认送达 ---
[支付网关] --- PUBLISH (保存消息) ---> [Broker]
[支付网关] <--- PUBREC (收到, Broker存储消息) --- [Broker]--- 第3,4步:确认处理 ---
[支付网关] --- PUBREL (删除本地消息) ---> [Broker]
[支付网关] <--- PUBCOMP (Broker处理完成) --- [Broker]

为什么不会重复?

关键在于 PUBRECPUBREL 这两步。Broker 只有在收到 PUBREL 之后,才会真正将消息投递给订阅者。即使 PUBREL 丢失了,发布者会重发 PUBREL,Broker 收到重复的 PUBREL 也知道这是同一个流程,不会导致消息被投递两次。这个双重确认机制保证了消息的唯一性。

  • 优点: 精确一次,既不丢失也不重复,可靠性最高。

  • 缺点: 流程最复杂,通信往返次数最多(四次),因此性能开销最大,速度最慢

  • 适用场景:

    • 绝对不允许数据重复或丢失的场景。

    • 计费、支付系统。

    • 关键的、不可逆的业务指令。

总结对比

特性

QoS 0 (最多一次)

QoS 1 (至少一次)

QoS 2 (正好一次)

可靠性

最低

中等

最高

消息传递保证

不保证

保证至少一次

保证正好一次

消息丢失可能

消息重复可能

网络开销

最小 (1次传输)

中等 (2次传输)

最大 (4次传输)

性能/速度

最快

较快

最慢

核心机制

发射后不管

PUBACK 确认

PUBREC/PUBREL/PUBCOMP 四步握手

适用场景

传感器数据、可丢弃的日志

远程控制、重要状态通知

计费、交易、关键业务指令

希望这份结合了具体场景和数据流的详细讲解,能帮助你彻底理解 MQTT QoS 的工作原理和选择依据。

http://www.dtcms.com/a/395422.html

相关文章:

  • MySQL EXPLAIN 中的七种 type 类型详解
  • NestJS认识
  • 6.MySQL索引的数据结构【面试题】
  • 【vLLM 最新版v0.10.2】docker运行openai服务与GGUF量化使用方式
  • 鸿蒙开发入门:ArkTS基础与实战
  • #C语言——刷题攻略:牛客编程入门训练(十三):一维数组(二),轻松拿捏!
  • 2.16Vue全家桶-Vuex状态管理
  • 【SSR】SSR 性能问题
  • 《UE教程》第二章第四回——父类蓝图
  • GORM库用法查漏补缺
  • C++11 移动语义与右值
  • FPGA学习笔记——图像处理之对比度调节(直方图均衡化)
  • 如何进行人脸识别
  • 计算机视觉笔试选择题:题组1
  • 第八篇:常量表达式:从const到constexpr的革命
  • RV1126 NO.30:RV1126多线程获取音频AI的PCM数据
  • 基于蚁群算法解决车辆路径问题(VRP)的MATLAB实现
  • C语言自学--C语⾔内存函数
  • 磁带记录仪:从磁带到数字的数据存储之旅
  • 【运维】Ubuntu上WebDAV挂载与自动同步完整指南
  • Ubuntu之旅-04 Docker
  • python(73) 引用.dll文件并调用函数
  • Chrome 学习小记5——demo:(动态壁纸基础)
  • 手写 Android Dex VMP 壳:自定义虚拟机 + 指令解释执行全流程
  • 【Netty】创建一个 SSL 处理器,实现客户端与服务器之间的安全通信
  • 13 Python数据结构与算法
  • 爱:宇宙的心跳
  • Python字节数据写入文本文件完全指南:从基础到高级实战
  • 零基础Windows10安装LLVM
  • selenium三种等待方式详解