QoS 2(恰好一次)
QoS 2(恰好一次)
Section titled “QoS 2(恰好一次)”本节介绍 MQTT QoS 2(恰好一次送达)的完整握手协议、应用场景和性能特性。学习完成后,您将能够:
- 理解 QoS 2 的四段握手流程
- 掌握 PUBREC/PUBREL/PUBCOMP 的交互过程
- 识别需要 QoS 2 的关键业务场景
- 评估 QoS 2 对系统性能的影响
在开始本节之前,请确保:
- 理解 QoS 0 和 QoS 1 的概念
- 理解 QoS 级别选择对整个系统的影响
- Mosquitto Broker 已运行
QoS 2 Overview
Section titled “QoS 2 Overview”What is QoS 2?
Section titled “What is QoS 2?”QoS 2(Exactly once,恰好一次)是 MQTT 中最高的可靠性级别,确保消息不会丢失也不会重复。
核心特点:
- 使用四段握手协议
- 消息不会被重复送达
- 最高可靠性保证
- 最大网络开销和处理延迟
Message Flow
Section titled “Message Flow”Publisher Broker Subscriber │ │ │ │─── PUBLISH ──────────→│ │ │ (QoS=2, PacketID=1) │ │ │ │ │ │←─── PUBREC ───────────│ │ │ (PacketID=1) │ │ │ 收到记录确认 │ │ │ │ │ │─── PUBREL ───────────→│ │ │ (PacketID=1) │ │ │ 确认释放 │ │ │ │─── PUBLISH ──────────→│ │ │ (QoS=2, PacketID=1)│ │ │ │ │←─── PUBCOMP ──────────│←─── PUBREC ──────────│ │ (PacketID=1) │ (PacketID=1) │ │ 完成确认 │ │ │ │─── PUBREL ──────────→│ │ │ (PacketID=1) │ │ │ │ │ │←─── PUBCOMP ────────│ │ │ (PacketID=1) │ │ │ │ │ 双方完成确认 │ 双方完成确认 │Protocol Packet Descriptions
Section titled “Protocol Packet Descriptions”| 报文 | 方向 | 含义 |
|---|---|---|
| PUBLISH | P→B | 发布消息(QoS=2, PacketID) |
| PUBREC | B→P | 收到发布(Publish Received),确认收到 |
| PUBREL | P→B | 释放发布(Publish Release),确认已处理 |
| PUBCOMP | B→P | 完成发布(Publish Complete),握手完成 |
Retry Scenarios
Section titled “Retry Scenarios”场景 1:PUBREC 丢失
Publisher Broker │ │ │─── PUBLISH ──────────→│ │ │ │←─── [PUBREC] ──✗─── │ PUBREC 丢失 │ │ │─── PUBLISH (重试) ───→│ 超时重发 PUBLISH │ │ │←─── PUBREC ───────────│ Broker 识别重复,重发 PUBREC │ │ │─── PUBREL ───────────→│ 正常继续 │ │ │←─── PUBCOMP ──────────│场景 2:PUBREL 丢失
Publisher Broker │ │ │ ←第一阶段完成→ │ │ │ │─── [PUBREL] ──✗────→│ PUBREL 丢失 │ │ │←─── PUBREC ───────────│ Broker 未收到 PUBREL,重发 PUBREC │ │ │─── PUBREL (重试) ────→│ 重发 PUBREL │ │ │←─── PUBCOMP ──────────│ 完成State Management
Section titled “State Management”Publisher State
Section titled “Publisher State”发布者在 QoS 2 状态下需要维护的状态:
初始状态: 未发送
发送 PUBLISH: 等待 PUBREC ↓收到 PUBREC: 等待 PUBREL 确认 ↓发送 PUBREL: 等待 PUBCOMP ↓收到 PUBCOMP: 完成(丢弃 PacketID)Broker State
Section titled “Broker State”Broker 需要在内存中维护所有进行中的 QoS 2 事务:
// Broker 内部 QoS 2 状态管理(概念)class QoS2Transaction { constructor(clientId, packetId, packet) { this.clientId = clientId; this.packetId = packetId; this.packet = packet; this.state = 'WAITING_PUBREL'; // 收到 PUBREC 后的状态 this.created = Date.now(); }}
// 存储所有活跃的 QoS 2 事务const activeTransactions = new Map();
function handlePubrec(clientId, packetId) { // Broker 收到 PUBREL 前需保持状态 activeTransactions.set( `${clientId}:${packetId}`, new QoS2Transaction(clientId, packetId, packet) );}When to Use QoS 2
Section titled “When to Use QoS 2”Recommended Scenarios
Section titled “Recommended Scenarios”| 场景 | 推荐 QoS | 理由 |
|---|---|---|
| 支付交易 | QoS 2 | 不可重复也不可丢失 |
| 库存计数更新 | QoS 2 | 重复计数会导致库存错误 |
| 计费数据 | QoS 2 | 必须精确记录每次使用 |
| 关键告警(火警/安防) | QoS 2 | 必须到达且只能一次 |
| 设备激活/注册 | QoS 2 | 重复注册会导致系统错误 |
Not Recommended Scenarios
Section titled “Not Recommended Scenarios”| 场景 | 推荐 QoS | 理由 |
|---|---|---|
| 几乎所有通用场景 | QoS 0/1 | QoS 2 开销过大 |
| 高频传感器数据 | QoS 0 | 不需要精确一次 |
| 设备状态更新 | QoS 1 | 重复状态可接受 |
| 调试日志 | QoS 0 | 丢失无关紧要 |
Implementation
Section titled “Implementation”Node-RED Configuration
Section titled “Node-RED Configuration”MQTT Out 节点配置:├── Server: [MQTT Broker]├── Topic: payment/transaction├── QoS: 2 (Exactly once) ← 选择├── Retain: false└── Name: Payment Transaction
注意:Node-RED 默认的 MQTT 节点使用 QoS 2仅在必要时修改为更低级别Mosquitto Command Line
Section titled “Mosquitto Command Line”# 发布 QoS 2 消息mosquitto_pub -h localhost -t "critical/alarm" \ -q 2 -m '{"alarm": "fire", "zone": "A-12"}'
# 订阅 QoS 2mosquitto_sub -h localhost -t "critical/alarm" \ -q 2 -v
# 验证四段握手(查看详细日志)mosquitto_sub -h localhost -t "critical/alarm" \ -q 2 -v -d # -d 开启调试输出Performance Impact
Section titled “Performance Impact”QoS Level Comparison Summary
Section titled “QoS Level Comparison Summary”| 指标 | QoS 0 | QoS 1 | QoS 2 |
|---|---|---|---|
| 消息交换次数 | 1(单向) | 2(发+确认) | 4(两轮双向) |
| 最小延迟 | < 1ms | 1-2ms | 3-5ms |
| 吞吐量 | 100%(基准) | ~40% | ~15% |
| 带宽开销 | 最低 | 中 | 最高 |
| 发布者存储 | 无 | 未确认消息 | 完整握手状态 |
| Broker 存储 | 无 | 未确认消息 | 完整握手状态 |
| 去重保证 | 不适用 | 否 | 是 |
| 适用网络 | 所有 | 可靠网络 | 极不可靠网络 |
When Overhead Matters
Section titled “When Overhead Matters”QoS 2 的实际影响:
100 个设备,每秒发布 1 条消息(1KB payload): QoS 0: ~100 KB/s 入站流量 QoS 1: ~150 KB/s (+100 PUBACK) QoS 2: ~400 KB/s (+300 控制报文)
CPU 使用率(单核): QoS 0: 5% QoS 1: 15% QoS 2: 40%验证 QoS 2 四段握手
Section titled “验证 QoS 2 四段握手”# 使用调试模式查看完整的 MQTT 协议交互mosquitto_pub -h localhost -t "qos2/test" -q 2 -m "test" -d
# 预期输出:# Client ... sending CONNECT# Client ... received CONNACK# Client ... sending PUBLISH (d0, q2, r0, m1, 'qos2/test', ... (4 bytes))# Client ... received PUBREC (m1, rc=0)# Client ... sending PUBREL (m1)# Client ... received PUBCOMP (m1)# 确认: 看到 PUBREC → PUBREL → PUBCOMP 的完整流程- ✅ 推荐: 只在必须保证不重不漏的关键场景使用 QoS 2
- ✅ 推荐: 对 QoS 2 消息设置合理的超时和重试策略
- ✅ 推荐: 监控 QoS 2 消息的完成率(过高未完成表示系统问题)
- ❌ 避免: 在高吞吐场景中使用 QoS 2(降低系统性能)
- ❌ 避免: 对传感器数据使用 QoS 2(QoS 0 已足够)
- ❌ 避免: 在同一 Topic 中混用不同 QoS 级别
Pre-sales Communication
Section titled “Pre-sales Communication”解释 QoS 选择
Section titled “解释 QoS 选择”Q: 为什么不是所有消息都用最高可靠性?
A: 可以这样理解:QoS 级别就像快递服务的选择。
- QoS 0:平邮——便宜、快速、但可能丢失
- QoS 1:挂号信——有签收确认、保证送到、可能多跑一趟
- QoS 2:专车配送——全程跟踪、精确送达一次、最贵最慢
对于温度数据这种每 3 秒更新一次的信息,用平邮(QoS 0)就足够了。只有控制设备或处理支付的指令才需要专车配送(QoS 2)。
Summary
Section titled “Summary”本节要点总结:
- QoS 2 机制:四段握手(PUBLISH → PUBREC → PUBREL → PUBCOMP)
- 可靠性:最高级别,消息不会丢失也不会重复
- 性能开销:吞吐量约为 QoS 0 的 15%,延迟增加 3-5ms
- 适用场景:支付交易、库存计数、计费数据、关键告警
- 选择建议:仅在业务逻辑要求”不能多也不能少”时使用