3. 遗嘱消息与保留消息

遗嘱消息与保留消息 学习卡片

任务目标

在MQTT中,保留消息(Retained Message)让新订阅的客户端能立即获取主题上的最新状态;遗嘱消息(Last Will and Testament, LWT)则在客户端异常断开时由Broker代为发布,通知其他客户端。本案例使用EMQX Broker和Eclipse Paho Python客户端,模拟一个设备异常掉线,验证其他客户端能收到遗嘱通知,并能通过保留消息获取设备的最新在线状态。

环境准备

  • 安装EMQX(本机默认端口1883),启动服务
  • 安装Paho MQTT客户端:pip install paho-mqtt

完整代码

import paho.mqtt.client as mqtt
import time
import threading

BROKER = "127.0.0.1"
PORT = 1883
STATUS_TOPIC = "device/status"
LWT_TOPIC = "device/lwt"

# ---------- 订阅端(观察者) ----------
def on_message(client, userdata, msg):
    print(f"[订阅端] 收到主题 '{msg.topic}' 的消息: {msg.payload.decode()}")

sub_client = mqtt.Client("subscriber")
sub_client.on_message = on_message
sub_client.connect(BROKER, PORT)
sub_client.subscribe([(STATUS_TOPIC, 0), (LWT_TOPIC, 0)])
sub_client.loop_start()

# ---------- 设备端(模拟异常断开) ----------
def on_connect(client, userdata, flags, rc):
    print("[设备端] 连接成功,发布保留消息并设置遗嘱")
    # 发布保留消息:设备在线状态
    client.publish(STATUS_TOPIC, "online", qos=1, retain=True)

device_client = mqtt.Client("device_001")
device_client.will_set(LWT_TOPIC, "device_001 offline", qos=1, retain=False)
device_client.on_connect = on_connect
device_client.connect(BROKER, PORT)
device_client.loop_start()

# 等待设备完成连接和保留消息发布
time.sleep(1)

# 模拟异常断开:直接停止loop并关闭socket,不发送DISCONNECT包
print("[设备端] 模拟异常断电,强制断开连接...")
device_client.loop_stop()
device_client.socket().close()

# 等待Broker检测到连接断开并发布遗嘱
time.sleep(3)

# 验证:新客户端订阅,应能立即收到保留消息
print("\n[验证] 新客户端订阅状态主题,观察是否收到保留消息...")
verify_client = mqtt.Client("verify_subscriber")
verify_client.on_message = on_message
verify_client.connect(BROKER, PORT)
verify_client.subscribe(STATUS_TOPIC, 0)
verify_client.loop_start()
time.sleep(1)

# 清理
verify_client.loop_stop()
sub_client.loop_stop()
print("\n[完成] 测试结束")

运行与验证

  1. 确保EMQX Broker已启动:emqx start(或使用Docker:docker run -d --name emqx -p 1883:1883 emqx/emqx:latest)
  2. 运行上述Python脚本:python mqtt_lwt_retain.py
  3. 观察控制台输出,预期结果:
[设备端] 连接成功,发布保留消息并设置遗嘱
[订阅端] 收到主题 'device/status' 的消息: online
[设备端] 模拟异常断电,强制断开连接...
[订阅端] 收到主题 'device/lwt' 的消息: device_001 offline
[验证] 新客户端订阅状态主题,观察是否收到保留消息...
[订阅端] 收到主题 'device/status' 的消息: online

通过条件:

  • 订阅端在设备断开后收到device/lwt上的遗嘱消息,内容为device_001 offline
  • 新订阅的验证客户端在订阅device/status后立即收到保留消息online,无需等待设备重新发布
  • 若设备正常断开(调用disconnect()),Broker不会发布遗嘱——可自行对比测试
关键说明:遗嘱消息仅在客户端异常断开(如网络中断、断电、崩溃)时由Broker发布;正常发送DISCONNECT包则不会触发。保留消息存储在Broker端,每个主题仅保留最新一条,新订阅者立即可获取。
MQTT遗嘱消息与保留消息时序图 设备端 EMQX Broker 订阅端 CONNECT (遗嘱: device/lwt) PUBLISH device/status "online" (retain=1) 存储保留消息 转发: device/status "online" 异常断开 (无DISCONNECT) PUBLISH device/lwt "device_001 offline" 新客户端订阅 SUBSCRIBE device/status 立即推送保留消息 "online" 设备操作 Broker转发 遗嘱发布 保留消息推送